1 /* Copyright (C) 1991, 1995 to 1999 Aladdin Enterprises. 2001 ArtifexSoftware Inc. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /*$Id: gdevdevn.c,v 1.28 2005/10/01 04:40:18 dan Exp $ */
18 /* Example DeviceN process color model devices. */
19
20 #include "math_.h"
21 #include "string_.h"
22 #include "gdevprn.h"
23 #include "gsparam.h"
24 #include "gscrd.h"
25 #include "gscrdp.h"
26 #include "gxlum.h"
27 #include "gdevdcrd.h"
28 #include "gstypes.h"
29 #include "gxdcconv.h"
30 #include "gdevdevn.h"
31 #include "gsequivc.h"
32
33 /*
34 * Utility routines for common DeviceN related parameters:
35 * SeparationColorNames, SeparationOrder, and MaxSeparations
36 */
37
38 /* Convert a gray color space to DeviceN colorants. */
39 void
gray_cs_to_devn_cm(gx_device * dev,int * map,frac gray,frac out[])40 gray_cs_to_devn_cm(gx_device * dev, int * map, frac gray, frac out[])
41 {
42 int i = dev->color_info.num_components - 1;
43
44 for(; i >= 0; i--) /* Clear colors */
45 out[i] = frac_0;
46 if ((i = map[3]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
47 out[i] = frac_1 - gray;
48 }
49
50 /* Convert an RGB color space to DeviceN colorants. */
51 void
rgb_cs_to_devn_cm(gx_device * dev,int * map,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])52 rgb_cs_to_devn_cm(gx_device * dev, int * map,
53 const gs_imager_state *pis, frac r, frac g, frac b, frac out[])
54 {
55 int i = dev->color_info.num_components - 1;
56 frac cmyk[4];
57
58 for(; i >= 0; i--) /* Clear colors */
59 out[i] = frac_0;
60 color_rgb_to_cmyk(r, g, b, pis, cmyk);
61 if ((i = map[0]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
62 out[i] = cmyk[0];
63 if ((i = map[1]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
64 out[i] = cmyk[1];
65 if ((i = map[2]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
66 out[i] = cmyk[2];
67 if ((i = map[3]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
68 out[i] = cmyk[3];
69 }
70
71 /* Convert a CMYK color space to DeviceN colorants. */
72 void
cmyk_cs_to_devn_cm(gx_device * dev,int * map,frac c,frac m,frac y,frac k,frac out[])73 cmyk_cs_to_devn_cm(gx_device * dev, int * map,
74 frac c, frac m, frac y, frac k, frac out[])
75 {
76 int i = dev->color_info.num_components - 1;
77
78 for(; i >= 0; i--) /* Clear colors */
79 out[i] = frac_0;
80 if ((i = map[0]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
81 out[i] = c;
82 if ((i = map[1]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
83 out[i] = m;
84 if ((i = map[2]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
85 out[i] = y;
86 if ((i = map[3]) != GX_DEVICE_COLOR_MAX_COMPONENTS)
87 out[i] = k;
88 }
89
90 /*
91 * This utility routine calculates the number of bits required to store
92 * color information. In general the values are rounded up to an even
93 * byte boundary except those cases in which mulitple pixels can evenly
94 * into a single byte.
95 *
96 * The parameter are:
97 * ncomp - The number of components (colorants) for the device. Valid
98 * values are 1 to GX_DEVICE_COLOR_MAX_COMPONENTS
99 * bpc - The number of bits per component. Valid values are 1, 2, 4, 5,
100 * and 8.
101 * Input values are not tested for validity.
102 */
103 int
bpc_to_depth(int ncomp,int bpc)104 bpc_to_depth(int ncomp, int bpc)
105 {
106 static const byte depths[4][8] = {
107 {1, 2, 0, 4, 8, 0, 0, 8},
108 {2, 4, 0, 8, 16, 0, 0, 16},
109 {4, 8, 0, 16, 16, 0, 0, 24},
110 {4, 8, 0, 16, 32, 0, 0, 32}
111 };
112
113 if (ncomp <=4 && bpc <= 8)
114 return depths[ncomp -1][bpc-1];
115 else
116 return (ncomp * bpc + 7) & 0xf8;
117 }
118
119 #define compare_color_names(name, name_size, str, str_size) \
120 (name_size == str_size && \
121 (strncmp((const char *)name, (const char *)str, name_size) == 0))
122
123 /*
124 * This routine will check if a name matches any item in a list of process
125 * color model colorant names.
126 */
127 private bool
check_process_color_names(fixed_colorant_names_list plist,const gs_param_string * pstring)128 check_process_color_names(fixed_colorant_names_list plist,
129 const gs_param_string * pstring)
130 {
131 if (plist) {
132 uint size = pstring->size;
133
134 while( *plist) {
135 if (compare_color_names(*plist, strlen(*plist), pstring->data, size)) {
136 return true;
137 }
138 plist++;
139 }
140 }
141 return false;
142 }
143
144 /*
145 * This routine will check to see if the color component name match those
146 * of either the process color model colorants or the names on the
147 * SeparationColorNames list.
148 *
149 * Parameters:
150 * dev - pointer to device data structure.
151 * pname - pointer to name (zero termination not required)
152 * nlength - length of the name
153 *
154 * This routine returns a positive value (0 to n) which is the device colorant
155 * number if the name is found. It returns a negative value if not found.
156 */
157 int
check_pcm_and_separation_names(const gx_device * dev,const gs_devn_params * pparams,const char * pname,int name_size,int component_type)158 check_pcm_and_separation_names(const gx_device * dev,
159 const gs_devn_params * pparams, const char * pname,
160 int name_size, int component_type)
161 {
162 fixed_colorant_name * pcolor = pparams->std_colorant_names;
163 int color_component_number = 0;
164 int i;
165
166 /* Check if the component is in the process color model list. */
167 if (pcolor) {
168 while( *pcolor) {
169 if (compare_color_names(pname, name_size, *pcolor, strlen(*pcolor)))
170 return color_component_number;
171 pcolor++;
172 color_component_number++;
173 }
174 }
175
176 /* Check if the component is in the separation names list. */
177 {
178 const gs_separations * separations = &pparams->separations;
179 int num_spot = separations->num_separations;
180
181 for (i=0; i<num_spot; i++) {
182 if (compare_color_names((const char *)separations->names[i].data,
183 separations->names[i].size, pname, name_size)) {
184 return color_component_number;
185 }
186 color_component_number++;
187 }
188 }
189
190 return -1;
191 }
192
193 /*
194 * This routine will check to see if the color component name match those
195 * that are available amoung the current device's color components.
196 *
197 * Parameters:
198 * dev - pointer to device data structure.
199 * pname - pointer to name (zero termination not required)
200 * nlength - length of the name
201 * component_type - separation name or not
202 * pdevn_params - pointer to device's DeviceN paramters
203 * pequiv_colors - pointer to equivalent color structure (may be NULL)
204 *
205 * This routine returns a positive value (0 to n) which is the device colorant
206 * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if
207 * the color component is found but is not being used due to the
208 * SeparationOrder device parameter. It returns a negative value if not found.
209 *
210 * This routine will also add separations to the device if space is
211 * available.
212 */
213 int
devn_get_color_comp_index(const gx_device * dev,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pequiv_colors,const char * pname,int name_size,int component_type,int auto_spot_colors)214 devn_get_color_comp_index(const gx_device * dev, gs_devn_params * pdevn_params,
215 equivalent_cmyk_color_params * pequiv_colors,
216 const char * pname, int name_size, int component_type,
217 int auto_spot_colors)
218 {
219 int num_order = pdevn_params->num_separation_order_names;
220 int color_component_number = 0;
221 int max_spot_colors = GX_DEVICE_MAX_SEPARATIONS;
222
223 /*
224 * Check if the component is in either the process color model list
225 * or in the SeparationNames list.
226 */
227 color_component_number = check_pcm_and_separation_names(dev, pdevn_params,
228 pname, name_size, component_type);
229
230 /* If we have a valid component */
231 if (color_component_number >= 0) {
232 /* Check if the component is in the separation order map. */
233 if (num_order)
234 color_component_number =
235 pdevn_params->separation_order_map[color_component_number];
236 else
237 /*
238 * We can have more spot colors than we can image. We simply
239 * ignore the component (i.e. treat it the same as we would
240 * treat a component that is not in the separation order map).
241 * Note: Most device do not allow more spot colors than we can
242 * image. (See the options for auto_spot_color in gdevdevn.h.)
243 */
244 if (color_component_number >= dev->color_info.num_components)
245 color_component_number = GX_DEVICE_COLOR_MAX_COMPONENTS;
246
247 return color_component_number;
248 }
249 /*
250 * The given name does not match any of our current components or
251 * separations. Check if we should add the spot color to our list.
252 * If the SeparationOrder parameter has been specified then we should
253 * already have our complete list of desired spot colorants.
254 */
255 if (component_type != SEPARATION_NAME ||
256 auto_spot_colors == NO_AUTO_SPOT_COLORS ||
257 pdevn_params->num_separation_order_names != 0)
258 return -1; /* Do not add --> indicate colorant unknown. */
259 /*
260 * Check if we have room for another spot colorant.
261 */
262 if (auto_spot_colors == ENABLE_AUTO_SPOT_COLORS)
263 max_spot_colors = dev->color_info.num_components -
264 pdevn_params->num_std_colorant_names;
265 if (pdevn_params->separations.num_separations < max_spot_colors) {
266 byte * sep_name;
267 gs_separations * separations = &pdevn_params->separations;
268 int sep_num = separations->num_separations++;
269
270 /* We have a new spot colorant */
271 sep_name = gs_alloc_bytes(dev->memory,
272 name_size, "devn_get_color_comp_index");
273 memcpy(sep_name, pname, name_size);
274 separations->names[sep_num].size = name_size;
275 separations->names[sep_num].data = sep_name;
276 color_component_number = sep_num + pdevn_params->num_std_colorant_names;
277 pdevn_params->separation_order_map[color_component_number] =
278 color_component_number;
279
280 if (pequiv_colors != NULL) {
281 /* Indicate that we need to find equivalent CMYK color. */
282 pequiv_colors->color[sep_num].color_info_valid = false;
283 pequiv_colors->all_color_info_valid = false;
284 }
285 }
286
287 return color_component_number;
288 }
289
290 #define set_param_array(a, d, s)\
291 (a.data = d, a.size = s, a.persistent = false);
292
293 /* Get parameters. We provide a default CRD. */
294 int
devn_get_params(gx_device * pdev,gs_param_list * plist,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pequiv_colors)295 devn_get_params(gx_device * pdev, gs_param_list * plist,
296 gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pequiv_colors)
297 {
298 int code;
299 bool seprs = false;
300 gs_param_string_array scna;
301 gs_param_string_array sona;
302
303 set_param_array(scna, NULL, 0);
304 set_param_array(sona, NULL, 0);
305
306 if ( (code = sample_device_crd_get_params(pdev, plist, "CRDDefault")) < 0 ||
307 (code =
308 param_write_name_array(plist, "SeparationColorNames", &scna)) < 0 ||
309 (code = param_write_name_array(plist, "SeparationOrder", &sona)) < 0 ||
310 (code = param_write_bool(plist, "Separations", &seprs)) < 0)
311 return code;
312
313 return 0;
314 }
315 #undef set_param_array
316
317 #define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
318 BEGIN\
319 switch (code = pread(plist, (param_name = pname), &(pa))) {\
320 case 0:\
321 if ((pa).size != psize) {\
322 ecode = gs_note_error(gs_error_rangecheck);\
323 (pa).data = 0; /* mark as not filled */\
324 } else
325 #define END_ARRAY_PARAM(pa, e)\
326 goto e;\
327 default:\
328 ecode = code;\
329 e: param_signal_error(plist, param_name, ecode);\
330 case 1:\
331 (pa).data = 0; /* mark as not filled */\
332 }\
333 END
334
335 /*
336 * Utility routine for handling DeviceN related parameters. This routine
337 * may modify the color_info, devn_params, and the equiv_cmyk_colors fields.
338 *
339 * Note: This routine does not restore values in case of a problem. This
340 * is left to the caller.
341 */
342 int
devn_put_params(gx_device * pdev,gs_param_list * plist,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pequiv_colors)343 devn_put_params(gx_device * pdev, gs_param_list * plist,
344 gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pequiv_colors)
345 {
346 int code = 0, ecode;
347 gs_param_name param_name;
348 int npcmcolors = pdevn_params->num_std_colorant_names;
349 int num_spot = pdevn_params->separations.num_separations;
350 bool num_spot_changed = false;
351 int num_order = pdevn_params->num_separation_order_names;
352 int max_sep = pdevn_params->max_separations;
353 gs_param_string_array scna; /* SeparationColorNames array */
354 gs_param_string_array sona; /* SeparationOrder names array */
355
356 /* Get the SeparationOrder names */
357 BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationOrder",
358 sona, sona.size, sone)
359 {
360 break;
361 } END_ARRAY_PARAM(sona, sone);
362 if (sona.data != 0 && sona.size > GX_DEVICE_COLOR_MAX_COMPONENTS)
363 return_error(gs_error_rangecheck);
364
365 /* Get the SeparationColorNames */
366 BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationColorNames",
367 scna, scna.size, scne)
368 {
369 break;
370 } END_ARRAY_PARAM(scna, scne);
371 if (scna.data != 0 && scna.size > GX_DEVICE_MAX_SEPARATIONS)
372 return_error(gs_error_rangecheck);
373
374 /* Separations are only valid with a subrtractive color model */
375 if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
376 /*
377 * Process the SeparationColorNames. Remove any names that already
378 * match the process color model colorant names for the device.
379 */
380 if (scna.data != 0) {
381 int i;
382 int num_names = scna.size;
383 fixed_colorant_names_list pcomp_names =
384 pdevn_params->std_colorant_names;
385
386 for (i = num_spot = 0; i < num_names; i++) {
387 /* Verify that the name is not one of our process colorants */
388 if (!check_process_color_names(pcomp_names, &scna.data[i])) {
389 byte * sep_name;
390 int name_size = scna.data[i].size;
391
392 /* We have a new separation */
393 sep_name = (byte *)gs_alloc_bytes(pdev->memory,
394 name_size, "devicen_put_params_no_sep_order");
395 memcpy(sep_name, scna.data[i].data, name_size);
396 pdevn_params->separations.names[num_spot].size = name_size;
397 pdevn_params->separations.names[num_spot].data = sep_name;
398 if (pequiv_colors != NULL) {
399 /* Indicate that we need to find equivalent CMYK color. */
400 pequiv_colors->color[num_spot].color_info_valid = false;
401 pequiv_colors->all_color_info_valid = false;
402 }
403 num_spot++;
404 }
405 }
406 pdevn_params->separations.num_separations = num_spot;
407 num_spot_changed = true;
408 for (i = 0; i < num_spot + npcmcolors; i++)
409 pdevn_params->separation_order_map[i] = i;
410 }
411 /*
412 * Process the SeparationOrder names.
413 */
414 if (sona.data != 0) {
415 int i, comp_num;
416
417 num_order = sona.size;
418 for (i = 0; i < num_spot + npcmcolors; i++)
419 pdevn_params->separation_order_map[i] = GX_DEVICE_COLOR_MAX_COMPONENTS;
420 for (i = 0; i < num_order; i++) {
421 /*
422 * Check if names match either the process color model or
423 * SeparationColorNames. If not then error.
424 */
425 if ((comp_num = check_pcm_and_separation_names(pdev, pdevn_params,
426 (const char *)sona.data[i].data, sona.data[i].size, 0)) < 0) {
427 return_error(gs_error_rangecheck);
428 }
429 pdevn_params->separation_order_map[comp_num] = i;
430 }
431 }
432 /*
433 * Adobe says that MaxSeparations is supposed to be 'read only'
434 * however we use this to allow the specification of the maximum
435 * number of separations. Memory is allocated for the specified
436 * number of separations. This allows us to then accept separation
437 * colors in color spaces even if they we not specified at the start
438 * of the image file.
439 */
440 code = param_read_int(plist, param_name = "MaxSeparations", &max_sep);
441 switch (code) {
442 default:
443 param_signal_error(plist, param_name, code);
444 case 1:
445 break;
446 case 0:
447 if (max_sep < 1 || max_sep > GX_DEVICE_COLOR_MAX_COMPONENTS)
448 return_error(gs_error_rangecheck);
449 {
450 int depth =
451 bpc_to_depth(max_sep, pdevn_params->bitspercomponent);
452
453 if (depth > 8 * size_of(gx_color_index))
454 return_error(gs_error_rangecheck);
455 pdevn_params->max_separations =
456 pdev->color_info.max_components =
457 pdev->color_info.num_components = max_sep;
458 pdev->color_info.depth = depth;
459 }
460 }
461 /*
462 * The DeviceN device can have zero components if nothing has been
463 * specified. This causes some problems so force at least one
464 * component until something is specified.
465 */
466 if (!pdev->color_info.num_components)
467 pdev->color_info.num_components = 1;
468 /*
469 * Update the number of device components if we have changes in
470 * SeparationColorNames, SeparationOrder, or MaxSeparations.
471 */
472 if (num_spot_changed || pdevn_params->max_separations != max_sep ||
473 pdevn_params->num_separation_order_names != num_order) {
474 pdevn_params->separations.num_separations = num_spot;
475 pdevn_params->num_separation_order_names = num_order;
476 pdevn_params->max_separations = max_sep;
477 /*
478 * If we have SeparationOrder specified then the number of
479 * components is given by the number of names in the list.
480 * Otherwise check if the MaxSeparations parameter has specified
481 * a value. If so then use that value, otherwise use the number
482 * of ProcessColorModel components plus the number of
483 * SeparationColorNames is used.
484 */
485 pdev->color_info.num_components = (num_order) ? num_order
486 : (pdevn_params->max_separations)
487 ? pdevn_params->max_separations
488 : npcmcolors + num_spot;
489 pdev->color_info.depth = bpc_to_depth(pdev->color_info.num_components,
490 pdevn_params->bitspercomponent);
491 }
492 }
493 return code;
494 }
495
496 /*
497 * Utility routine for handling DeviceN related parameters in a
498 * standard raster printer type device.
499 */
500 int
devn_printer_put_params(gx_device * pdev,gs_param_list * plist,gs_devn_params * pdevn_params,equivalent_cmyk_color_params * pequiv_colors)501 devn_printer_put_params(gx_device * pdev, gs_param_list * plist,
502 gs_devn_params * pdevn_params, equivalent_cmyk_color_params * pequiv_colors)
503 {
504 int code;
505 /* Save current data in case we have a problem */
506 gx_device_color_info save_info = pdev->color_info;
507 gs_devn_params saved_devn_params = *pdevn_params;
508 equivalent_cmyk_color_params saved_equiv_colors;
509
510 if (pequiv_colors != NULL)
511 saved_equiv_colors = *pequiv_colors;
512
513 /* Use utility routine to handle parameters */
514 code = devn_put_params(pdev, plist, pdevn_params, pequiv_colors);
515
516 /* Check for default printer parameters */
517 if (code >= 0)
518 code = gdev_prn_put_params(pdev, plist);
519
520 /* If we have an error then restore original data. */
521 if (code < 0) {
522 pdev->color_info = save_info;
523 *pdevn_params = saved_devn_params;
524 if (pequiv_colors != NULL)
525 *pequiv_colors = saved_equiv_colors;
526 return code;
527 }
528
529 /* If anything changed, then close the device, etc. */
530 if (memcmp(&pdev->color_info, &save_info, sizeof(gx_device_color_info)) ||
531 memcmp(pdevn_params, &saved_devn_params,
532 sizeof(gs_devn_params)) ||
533 (pequiv_colors != NULL &&
534 memcmp(pequiv_colors, &saved_equiv_colors,
535 sizeof(equivalent_cmyk_color_params)))) {
536 gs_closedevice(pdev);
537 /* Reset the sparable and linear shift, masks, bits. */
538 set_linear_color_bits_mask_shift(pdev);
539 pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
540 }
541
542 return code;
543 }
544
545 /* ***************** The spotcmyk and devicen devices ***************** */
546
547 /* Define the device parameters. */
548 #ifndef X_DPI
549 # define X_DPI 72
550 #endif
551 #ifndef Y_DPI
552 # define Y_DPI 72
553 #endif
554
555 /* The device descriptor */
556 private dev_proc_open_device(spotcmyk_prn_open);
557 private dev_proc_get_params(spotcmyk_get_params);
558 private dev_proc_put_params(spotcmyk_put_params);
559 private dev_proc_print_page(spotcmyk_print_page);
560 private dev_proc_get_color_mapping_procs(get_spotcmyk_color_mapping_procs);
561 private dev_proc_get_color_mapping_procs(get_devicen_color_mapping_procs);
562 private dev_proc_get_color_comp_index(spotcmyk_get_color_comp_index);
563 private dev_proc_encode_color(spotcmyk_encode_color);
564 private dev_proc_decode_color(spotcmyk_decode_color);
565
566 /*
567 * A structure definition for a DeviceN type device
568 */
569 typedef struct spotcmyk_device_s {
570 gx_device_common;
571 gx_prn_device_common;
572 gs_devn_params devn_params;
573 } spotcmyk_device;
574
575 /* GC procedures */
576
577 private
ENUM_PTRS_WITH(spotcmyk_device_enum_ptrs,spotcmyk_device * pdev)578 ENUM_PTRS_WITH(spotcmyk_device_enum_ptrs, spotcmyk_device *pdev)
579 {
580 if (index < pdev->devn_params.separations.num_separations)
581 ENUM_RETURN(pdev->devn_params.separations.names[index].data);
582 ENUM_PREFIX(st_device_printer,
583 pdev->devn_params.separations.num_separations);
584 }
585
586 ENUM_PTRS_END
RELOC_PTRS_WITH(spotcmyk_device_reloc_ptrs,spotcmyk_device * pdev)587 private RELOC_PTRS_WITH(spotcmyk_device_reloc_ptrs, spotcmyk_device *pdev)
588 {
589 RELOC_PREFIX(st_device_printer);
590 {
591 int i;
592
593 for (i = 0; i < pdev->devn_params.separations.num_separations; ++i) {
594 RELOC_PTR(spotcmyk_device, devn_params.separations.names[i].data);
595 }
596 }
597 }
598 RELOC_PTRS_END
599
600 /* Even though spotcmyk_device_finalize is the same as gx_device_finalize, */
601 /* we need to implement it separately because st_composite_final */
602 /* declares all 3 procedures as private. */
603 private void
spotcmyk_device_finalize(void * vpdev)604 spotcmyk_device_finalize(void *vpdev)
605 {
606 gx_device_finalize(vpdev);
607 }
608
609 gs_private_st_composite_final(st_spotcmyk_device, spotcmyk_device,
610 "spotcmyk_device", spotcmyk_device_enum_ptrs, spotcmyk_device_reloc_ptrs,
611 spotcmyk_device_finalize);
612
613 /*
614 * Macro definition for DeviceN procedures
615 */
616 #define device_procs(get_color_mapping_procs)\
617 { spotcmyk_prn_open,\
618 gx_default_get_initial_matrix,\
619 NULL, /* sync_output */\
620 gdev_prn_output_page, /* output_page */\
621 gdev_prn_close, /* close */\
622 NULL, /* map_rgb_color - not used */\
623 NULL, /* map_color_rgb - not used */\
624 NULL, /* fill_rectangle */\
625 NULL, /* tile_rectangle */\
626 NULL, /* copy_mono */\
627 NULL, /* copy_color */\
628 NULL, /* draw_line */\
629 NULL, /* get_bits */\
630 spotcmyk_get_params, /* get_params */\
631 spotcmyk_put_params, /* put_params */\
632 NULL, /* map_cmyk_color - not used */\
633 NULL, /* get_xfont_procs */\
634 NULL, /* get_xfont_device */\
635 NULL, /* map_rgb_alpha_color */\
636 gx_page_device_get_page_device, /* get_page_device */\
637 NULL, /* get_alpha_bits */\
638 NULL, /* copy_alpha */\
639 NULL, /* get_band */\
640 NULL, /* copy_rop */\
641 NULL, /* fill_path */\
642 NULL, /* stroke_path */\
643 NULL, /* fill_mask */\
644 NULL, /* fill_trapezoid */\
645 NULL, /* fill_parallelogram */\
646 NULL, /* fill_triangle */\
647 NULL, /* draw_thin_line */\
648 NULL, /* begin_image */\
649 NULL, /* image_data */\
650 NULL, /* end_image */\
651 NULL, /* strip_tile_rectangle */\
652 NULL, /* strip_copy_rop */\
653 NULL, /* get_clipping_box */\
654 NULL, /* begin_typed_image */\
655 NULL, /* get_bits_rectangle */\
656 NULL, /* map_color_rgb_alpha */\
657 NULL, /* create_compositor */\
658 NULL, /* get_hardware_params */\
659 NULL, /* text_begin */\
660 NULL, /* finish_copydevice */\
661 NULL, /* begin_transparency_group */\
662 NULL, /* end_transparency_group */\
663 NULL, /* begin_transparency_mask */\
664 NULL, /* end_transparency_mask */\
665 NULL, /* discard_transparency_layer */\
666 get_color_mapping_procs, /* get_color_mapping_procs */\
667 spotcmyk_get_color_comp_index, /* get_color_comp_index */\
668 spotcmyk_encode_color, /* encode_color */\
669 spotcmyk_decode_color, /* decode_color */\
670 NULL, /* pattern_manage */\
671 NULL /* fill_rectangle_hl_color */\
672 }
673
674 fixed_colorant_name DeviceCMYKComponents[] = {
675 "Cyan",
676 "Magenta",
677 "Yellow",
678 "Black",
679 0 /* List terminator */
680 };
681
682
683 #define spotcmyk_device_body(procs, dname, ncomp, pol, depth, mg, mc, cn)\
684 std_device_full_body_type_extended(spotcmyk_device, &procs, dname,\
685 &st_spotcmyk_device,\
686 (int)((long)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10),\
687 (int)((long)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10),\
688 X_DPI, Y_DPI,\
689 GX_DEVICE_COLOR_MAX_COMPONENTS, /* MaxComponents */\
690 ncomp, /* NumComp */\
691 pol, /* Polarity */\
692 depth, 0, /* Depth, GrayIndex */\
693 mg, mc, /* MaxGray, MaxColor */\
694 mg + 1, mc + 1, /* DitherGray, DitherColor */\
695 GX_CINFO_SEP_LIN, /* Linear & Separable */\
696 cn, /* Process color model name */\
697 0, 0, /* offsets */\
698 0, 0, 0, 0 /* margins */\
699 ),\
700 prn_device_body_rest_(spotcmyk_print_page)
701
702 /*
703 * Example device with CMYK and spot color support
704 */
705 private const gx_device_procs spot_cmyk_procs = device_procs(get_spotcmyk_color_mapping_procs);
706
707 const spotcmyk_device gs_spotcmyk_device =
708 {
709 spotcmyk_device_body(spot_cmyk_procs, "spotcmyk", 4, GX_CINFO_POLARITY_SUBTRACTIVE, 4, 1, 1, "DeviceCMYK"),
710 /* DeviceN device specific parameters */
711 { 1, /* Bits per color - must match ncomp, depth, etc. above */
712 DeviceCMYKComponents, /* Names of color model colorants */
713 4, /* Number colorants for CMYK */
714 0, /* MaxSeparations has not been specified */
715 {0}, /* SeparationNames */
716 0, /* SeparationOrder names */
717 {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
718 }
719 };
720
721 /*
722 * Example DeviceN color device
723 */
724 private const gx_device_procs devicen_procs = device_procs(get_devicen_color_mapping_procs);
725
726 const spotcmyk_device gs_devicen_device =
727 {
728 spotcmyk_device_body(devicen_procs, "devicen", 4, GX_CINFO_POLARITY_SUBTRACTIVE, 32, 255, 255, "DeviceCMYK"),
729 /* DeviceN device specific parameters */
730 { 8, /* Bits per color - must match ncomp, depth, etc. above */
731 NULL, /* No names for standard DeviceN color model */
732 0, /* No standard colorants for DeviceN */
733 0, /* MaxSeparations has not been specified */
734 {0}, /* SeparationNames */
735 0, /* SeparationOrder names */
736 {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
737 }
738 };
739
740 /* Open the psd devices */
741 int
spotcmyk_prn_open(gx_device * pdev)742 spotcmyk_prn_open(gx_device * pdev)
743 {
744 int code = gdev_prn_open(pdev);
745
746 set_linear_color_bits_mask_shift(pdev);
747 pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
748 return code;
749 }
750
751 /* Color mapping routines for the spotcmyk device */
752
753 private void
gray_cs_to_spotcmyk_cm(gx_device * dev,frac gray,frac out[])754 gray_cs_to_spotcmyk_cm(gx_device * dev, frac gray, frac out[])
755 {
756 int * map = ((spotcmyk_device *) dev)->devn_params.separation_order_map;
757
758 gray_cs_to_devn_cm(dev, map, gray, out);
759 }
760
761 private void
rgb_cs_to_spotcmyk_cm(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])762 rgb_cs_to_spotcmyk_cm(gx_device * dev, const gs_imager_state *pis,
763 frac r, frac g, frac b, frac out[])
764 {
765 int * map = ((spotcmyk_device *) dev)->devn_params.separation_order_map;
766
767 rgb_cs_to_devn_cm(dev, map, pis, r, g, b, out);
768 }
769
770 private void
cmyk_cs_to_spotcmyk_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])771 cmyk_cs_to_spotcmyk_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
772 {
773 int * map = ((spotcmyk_device *) dev)->devn_params.separation_order_map;
774
775 cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out);
776 }
777
778 private const gx_cm_color_map_procs spotCMYK_procs = {
779 gray_cs_to_spotcmyk_cm, rgb_cs_to_spotcmyk_cm, cmyk_cs_to_spotcmyk_cm
780 };
781
782 private const gx_cm_color_map_procs *
get_spotcmyk_color_mapping_procs(const gx_device * dev)783 get_spotcmyk_color_mapping_procs(const gx_device * dev)
784 {
785 return &spotCMYK_procs;
786 }
787
788 /* Also use the spotcmyk procs for the devicen device. */
789
790 private const gx_cm_color_map_procs *
get_devicen_color_mapping_procs(const gx_device * dev)791 get_devicen_color_mapping_procs(const gx_device * dev)
792 {
793 return &spotCMYK_procs;
794 }
795
796
797 /*
798 * Encode a list of colorant values into a gx_color_index_value.
799 */
800 private gx_color_index
spotcmyk_encode_color(gx_device * dev,const gx_color_value colors[])801 spotcmyk_encode_color(gx_device *dev, const gx_color_value colors[])
802 {
803 int bpc = ((spotcmyk_device *)dev)->devn_params.bitspercomponent;
804 int drop = sizeof(gx_color_value) * 8 - bpc;
805 gx_color_index color = 0;
806 int i = 0;
807 int ncomp = dev->color_info.num_components;
808
809 for (; i<ncomp; i++) {
810 color <<= bpc;
811 color |= (colors[i] >> drop);
812 }
813 return (color == gx_no_color_index ? color ^ 1 : color);
814 }
815
816 /*
817 * Decode a gx_color_index value back to a list of colorant values.
818 */
819 private int
spotcmyk_decode_color(gx_device * dev,gx_color_index color,gx_color_value * out)820 spotcmyk_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
821 {
822 int bpc = ((spotcmyk_device *)dev)->devn_params.bitspercomponent;
823 int drop = sizeof(gx_color_value) * 8 - bpc;
824 int mask = (1 << bpc) - 1;
825 int i = 0;
826 int ncomp = dev->color_info.num_components;
827
828 for (; i<ncomp; i++) {
829 out[ncomp - i - 1] = (gx_color_value)((color & mask) << drop);
830 color >>= bpc;
831 }
832 return 0;
833 }
834
835 /* Get parameters. */
836 private int
spotcmyk_get_params(gx_device * pdev,gs_param_list * plist)837 spotcmyk_get_params(gx_device * pdev, gs_param_list * plist)
838 {
839 int code = gdev_prn_get_params(pdev, plist);
840
841 if (code < 0)
842 return code;
843 return devn_get_params(pdev, plist,
844 &(((spotcmyk_device *)pdev)->devn_params), NULL);
845 }
846
847 /* Set parameters. */
848 private int
spotcmyk_put_params(gx_device * pdev,gs_param_list * plist)849 spotcmyk_put_params(gx_device * pdev, gs_param_list * plist)
850 {
851 return devn_printer_put_params(pdev, plist,
852 &(((spotcmyk_device *)pdev)->devn_params), NULL);
853 }
854
855 /*
856 * This routine will check to see if the color component name match those
857 * that are available amoung the current device's color components.
858 *
859 * Parameters:
860 * dev - pointer to device data structure.
861 * pname - pointer to name (zero termination not required)
862 * nlength - length of the name
863 *
864 * This routine returns a positive value (0 to n) which is the device colorant
865 * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if
866 * the colorant is not being used due to a SeparationOrder device parameter.
867 * It returns a negative value if not found.
868 */
869 private int
spotcmyk_get_color_comp_index(gx_device * dev,const char * pname,int name_size,int component_type)870 spotcmyk_get_color_comp_index(gx_device * dev, const char * pname,
871 int name_size, int component_type)
872 {
873 return devn_get_color_comp_index(dev,
874 &(((spotcmyk_device *)dev)->devn_params), NULL,
875 pname, name_size, component_type, ENABLE_AUTO_SPOT_COLORS);
876 }
877
878
879 /*
880 * This routine will extract a specified set of bits from a buffer and pack
881 * them into a given buffer.
882 *
883 * Parameters:
884 * source - The source of the data
885 * dest - The destination for the data
886 * depth - The size of the bits per pixel - must be a multiple of 8
887 * first_bit - The location of the first data bit (LSB).
888 * bit_width - The number of bits to be extracted.
889 * npixel - The number of pixels.
890 *
891 * Returns:
892 * Length of the output line (in bytes)
893 * Data in dest.
894 */
895 int
repack_data(byte * source,byte * dest,int depth,int first_bit,int bit_width,int npixel)896 repack_data(byte * source, byte * dest, int depth, int first_bit,
897 int bit_width, int npixel)
898 {
899 int in_nbyte = depth >> 3; /* Number of bytes per input pixel */
900 int out_nbyte = bit_width >> 3; /* Number of bytes per output pixel */
901 gx_color_index mask = 1;
902 gx_color_index data;
903 int i, j, length = 0;
904 byte temp;
905 byte * out = dest;
906 int in_bit_start = 8 - depth;
907 int out_bit_start = 8 - bit_width;
908 int in_byte_loc = in_bit_start, out_byte_loc = out_bit_start;
909
910 mask = (mask << bit_width) - 1;
911 for (i=0; i<npixel; i++) {
912 /* Get the pixel data */
913 if (!in_nbyte) { /* Multiple pixels per byte */
914 data = *source;
915 data >>= in_byte_loc;
916 in_byte_loc -= depth;
917 if (in_byte_loc < 0) { /* If finished with byte */
918 in_byte_loc = in_bit_start;
919 source++;
920 }
921 }
922 else { /* One or more bytes per pixel */
923 data = *source++;
924 for (j=1; j<in_nbyte; j++)
925 data = (data << 8) + *source++;
926 }
927 data >>= first_bit;
928 data &= mask;
929
930 /* Put the output data */
931 if (!out_nbyte) { /* Multiple pixels per byte */
932 temp = (byte)(*out & ~(mask << out_byte_loc));
933 *out = (byte)(temp | (data << out_byte_loc));
934 out_byte_loc -= bit_width;
935 if (out_byte_loc < 0) { /* If finished with byte */
936 out_byte_loc = out_bit_start;
937 out++;
938 }
939 }
940 else { /* One or more bytes per pixel */
941 *out++ = (byte)(data >> ((out_nbyte - 1) * 8));
942 for (j=1; j<out_nbyte; j++) {
943 *out++ = (byte)(data >> ((out_nbyte - 1 - j) * 8));
944 }
945 }
946 }
947 /* Return the number of bytes in the destination buffer. */
948 if (out_byte_loc != out_bit_start) { /* If partially filled last byte */
949 *out = *out & ((~0) << out_byte_loc); /* Mask unused part of last byte */
950 out++;
951 }
952 length = out - dest;
953 return length;
954 }
955
956 private int write_pcx_file(gx_device_printer * pdev, char * filename, int ncomp,
957 int bpc, int pcmlinelength);
958 /*
959 * This is an example print page routine for a DeviceN device. This routine
960 * will handle a DeviceN, a CMYK with spot colors, or an RGB process color model.
961 *
962 * This routine creates several output files. If the process color model is
963 * RGB or CMYK then a bit image file is created which contains the data for the
964 * process color model data. This data is put into the given file stream.
965 * I.e. into the output file specified by the user. This file is not created
966 * for the DeviceN process color model. A separate bit image file is created
967 * is created for the data for each of the given spot colors. The names for
968 * these files are created by taking the given output file name and appending
969 * "sn" (where n is the spot color number 0 to ...) to the output file name.
970 * The results are unknown if the output file is stdout etc.
971 *
972 * After the bit image files are created, then a set of PCX format files are
973 * created from the bit image files. This files have a ".pcx" appended to the
974 * end of the files. Thus a CMYK process color model with two spot colors
975 * would end up with a total of six files being created. (xxx, xxxs0, xxxs1,
976 * xxx.pcx, xxxs0.pcx, and xxxs1.pcx).
977 *
978 * I do not assume that any users will actually want to create all of these
979 * different files. However I wanted to show an example of how each of the
980 * spot * colorants could be unpacked from the process color model colorants.
981 * The bit images files are an easy way to show this without the complication
982 * of trying to put the data into a specific format. However I do not have a
983 * tool which will display the bit image data directly so I needed to convert
984 * it to a form which I can view. Thus the PCX format files are being created.
985 * Note: The PCX implementation is not complete. There are many (most)
986 * combinations of bits per pixel and number of colorants that are not supported.
987 */
988 private int
spotcmyk_print_page(gx_device_printer * pdev,FILE * prn_stream)989 spotcmyk_print_page(gx_device_printer * pdev, FILE * prn_stream)
990 {
991 int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
992 byte *in = gs_alloc_bytes(pdev->memory, line_size, "spotcmyk_print_page(in)");
993 byte *buf = gs_alloc_bytes(pdev->memory, line_size + 3, "spotcmyk_print_page(buf)");
994 const spotcmyk_device * pdevn = (spotcmyk_device *) pdev;
995 int npcmcolors = pdevn->devn_params.num_std_colorant_names;
996 int ncomp = pdevn->color_info.num_components;
997 int depth = pdevn->color_info.depth;
998 int nspot = pdevn->devn_params.separations.num_separations;
999 int bpc = pdevn->devn_params.bitspercomponent;
1000 int lnum = 0, bottom = pdev->height;
1001 int width = pdev->width;
1002 FILE * spot_file[GX_DEVICE_COLOR_MAX_COMPONENTS] = {0};
1003 int i, code = 0;
1004 int first_bit;
1005 int pcmlinelength = 0; /* Initialize against indeterminizm in case of pdev->height == 0. */
1006 int linelength[GX_DEVICE_COLOR_MAX_COMPONENTS];
1007 byte *data;
1008 char spotname[gp_file_name_sizeof];
1009
1010 if (in == NULL || buf == NULL) {
1011 code = gs_error_VMerror;
1012 goto prn_done;
1013 }
1014 /*
1015 * Check if the SeparationOrder list has changed the order of the process
1016 * color model colorants. If so then we will treat all colorants as if they
1017 * are spot colors.
1018 */
1019 for (i = 0; i < npcmcolors; i++)
1020 if (pdevn->devn_params.separation_order_map[i] != i)
1021 break;
1022 if (i < npcmcolors || ncomp < npcmcolors) {
1023 nspot = ncomp;
1024 npcmcolors = 0;
1025 }
1026
1027 /* Open the output files for the spot colors */
1028 for(i = 0; i < nspot; i++) {
1029 sprintf(spotname, "%ss%d", pdevn->fname, i);
1030 spot_file[i] = fopen(spotname, "wb");
1031 if (spot_file[i] == NULL) {
1032 code = gs_error_VMerror;
1033 goto prn_done;
1034 }
1035 }
1036
1037
1038 /* Now create the output bit image files */
1039 for (; lnum < bottom; ++lnum) {
1040 gdev_prn_get_bits(pdev, lnum, in, &data);
1041 /* Now put the pcm data into the output file */
1042 if (npcmcolors) {
1043 first_bit = bpc * (ncomp - npcmcolors);
1044 pcmlinelength = repack_data(data, buf, depth, first_bit, bpc * npcmcolors, width);
1045 fwrite(buf, 1, pcmlinelength, prn_stream);
1046 }
1047 /* Put spot color data into the output files */
1048 for (i = 0; i < nspot; i++) {
1049 first_bit = bpc * (nspot - 1 - i);
1050 linelength[i] = repack_data(data, buf, depth, first_bit, bpc, width);
1051 fwrite(buf, 1, linelength[i], spot_file[i]);
1052 }
1053 }
1054
1055 /* Close the bit image files */
1056 for(i = 0; i < nspot; i++) {
1057 fclose(spot_file[i]);
1058 spot_file[i] = NULL;
1059 }
1060
1061 /* Now convert the bit image files into PCX files */
1062 if (npcmcolors) {
1063 code = write_pcx_file(pdev, (char *) &pdevn->fname,
1064 npcmcolors, bpc, pcmlinelength);
1065 if (code < 0)
1066 return code;
1067 }
1068 for(i = 0; i < nspot; i++) {
1069 sprintf(spotname, "%ss%d", pdevn->fname, i);
1070 code = write_pcx_file(pdev, spotname, 1, bpc, linelength[i]);
1071 if (code < 0)
1072 return code;
1073 }
1074
1075
1076 /* Clean up and exit */
1077 prn_done:
1078 for(i = 0; i < nspot; i++) {
1079 if (spot_file[i] != NULL)
1080 fclose(spot_file[i]);
1081 }
1082 if (in != NULL)
1083 gs_free_object(pdev->memory, in, "spotcmyk_print_page(in)");
1084 if (buf != NULL)
1085 gs_free_object(pdev->memory, buf, "spotcmyk_print_page(buf)");
1086 return code;
1087 }
1088
1089 /*
1090 * We are using the PCX output format. This is done for simplicity.
1091 * Much of the following code was copied from gdevpcx.c.
1092 */
1093
1094 /* ------ Private definitions ------ */
1095
1096 /* All two-byte quantities are stored LSB-first! */
1097 #if arch_is_big_endian
1098 # define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
1099 #else
1100 # define assign_ushort(a,v) a = (v)
1101 #endif
1102
1103 typedef struct pcx_header_s {
1104 byte manuf; /* always 0x0a */
1105 byte version;
1106 #define version_2_5 0
1107 #define version_2_8_with_palette 2
1108 #define version_2_8_without_palette 3
1109 #define version_3_0 /* with palette */ 5
1110 byte encoding; /* 1=RLE */
1111 byte bpp; /* bits per pixel per plane */
1112 ushort x1; /* X of upper left corner */
1113 ushort y1; /* Y of upper left corner */
1114 ushort x2; /* x1 + width - 1 */
1115 ushort y2; /* y1 + height - 1 */
1116 ushort hres; /* horz. resolution (dots per inch) */
1117 ushort vres; /* vert. resolution (dots per inch) */
1118 byte palette[16 * 3]; /* color palette */
1119 byte reserved;
1120 byte nplanes; /* number of color planes */
1121 ushort bpl; /* number of bytes per line (uncompressed) */
1122 ushort palinfo;
1123 #define palinfo_color 1
1124 #define palinfo_gray 2
1125 byte xtra[58]; /* fill out header to 128 bytes */
1126 } pcx_header;
1127
1128 /* Define the prototype header. */
1129 private const pcx_header pcx_header_prototype =
1130 {
1131 10, /* manuf */
1132 0, /* version (variable) */
1133 1, /* encoding */
1134 0, /* bpp (variable) */
1135 00, 00, /* x1, y1 */
1136 00, 00, /* x2, y2 (variable) */
1137 00, 00, /* hres, vres (variable) */
1138 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* palette (variable) */
1139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
1142 0, /* reserved */
1143 0, /* nplanes (variable) */
1144 00, /* bpl (variable) */
1145 00, /* palinfo (variable) */
1146 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* xtra */
1147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
1150 };
1151
1152
1153 /* Forward declarations */
1154 private void pcx_write_rle(const byte *, const byte *, int, FILE *);
1155 private int pcx_write_page(gx_device_printer * pdev, FILE * infile,
1156 int linesize, FILE * outfile, pcx_header * phdr, bool planar, int depth);
1157
1158 static const byte pcx_cmyk_palette[16 * 3] =
1159 {
1160 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00,
1161 0xff, 0x00, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x00,
1162 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x00,
1163 0x00, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x1f, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f,
1164 };
1165
1166 static const byte pcx_ega_palette[16 * 3] =
1167 {
1168 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa,
1169 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa,
1170 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff,
1171 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff
1172 };
1173
1174
1175 /*
1176 * This routine will set up the revision and palatte for the output
1177 * file.
1178 *
1179 * Please note that this routine does not currently handle all possible
1180 * combinations of bits and planes.
1181 *
1182 * Input parameters:
1183 * pdev - Pointer to device data structure
1184 * file - output file
1185 * header - The header structure to hold the data.
1186 * bits_per_plane - The number of bits per plane.
1187 * num_planes - The number of planes.
1188 */
1189 private bool
setup_pcx_header(gx_device_printer * pdev,pcx_header * phdr,int num_planes,int bits_per_plane)1190 setup_pcx_header(gx_device_printer * pdev, pcx_header * phdr, int num_planes, int bits_per_plane)
1191 {
1192 bool planar = true; /* Invalid cases could cause an indeterminizm. */
1193
1194 *phdr = pcx_header_prototype;
1195 phdr->bpp = bits_per_plane;
1196 phdr->nplanes = num_planes;
1197
1198 switch (num_planes) {
1199 case 1:
1200 switch (bits_per_plane) {
1201 case 1:
1202 phdr->version = version_2_8_with_palette;
1203 assign_ushort(phdr->palinfo, palinfo_gray);
1204 memcpy((byte *) phdr->palette, "\000\000\000\377\377\377", 6);
1205 planar = false;
1206 break;
1207 case 2: /* Not defined */
1208 break;
1209 case 4:
1210 phdr->version = version_2_8_with_palette;
1211 memcpy((byte *) phdr->palette, pcx_ega_palette, sizeof(pcx_ega_palette));
1212 planar = true;
1213 break;
1214 case 5: /* Not defined */
1215 break;
1216 case 8:
1217 phdr->version = version_3_0;
1218 assign_ushort(phdr->palinfo, palinfo_gray);
1219 planar = false;
1220 break;
1221 case 16: /* Not defined */
1222 break;
1223 }
1224 break;
1225 case 2:
1226 switch (bits_per_plane) {
1227 case 1: /* Not defined */
1228 break;
1229 case 2: /* Not defined */
1230 break;
1231 case 4: /* Not defined */
1232 break;
1233 case 5: /* Not defined */
1234 break;
1235 case 8: /* Not defined */
1236 break;
1237 case 16: /* Not defined */
1238 break;
1239 }
1240 break;
1241 case 3:
1242 switch (bits_per_plane) {
1243 case 1: /* Not defined */
1244 break;
1245 case 2: /* Not defined */
1246 break;
1247 case 4: /* Not defined */
1248 break;
1249 case 5: /* Not defined */
1250 break;
1251 case 8:
1252 phdr->version = version_3_0;
1253 assign_ushort(phdr->palinfo, palinfo_color);
1254 planar = true;
1255 break;
1256 case 16: /* Not defined */
1257 break;
1258 }
1259 break;
1260 case 4:
1261 switch (bits_per_plane) {
1262 case 1:
1263 phdr->version = 2;
1264 memcpy((byte *) phdr->palette, pcx_cmyk_palette,
1265 sizeof(pcx_cmyk_palette));
1266 planar = false;
1267 phdr->bpp = 4;
1268 phdr->nplanes = 1;
1269 break;
1270 case 2: /* Not defined */
1271 break;
1272 case 4: /* Not defined */
1273 break;
1274 case 5: /* Not defined */
1275 break;
1276 case 8: /* Not defined */
1277 break;
1278 case 16: /* Not defined */
1279 break;
1280 }
1281 break;
1282 }
1283 return planar;
1284 }
1285
1286 /* Write a palette on a file. */
1287 private int
pc_write_mono_palette(gx_device * dev,uint max_index,FILE * file)1288 pc_write_mono_palette(gx_device * dev, uint max_index, FILE * file)
1289 {
1290 uint i, c;
1291 gx_color_value rgb[3];
1292
1293 for (i = 0; i < max_index; i++) {
1294 rgb[0] = rgb[1] = rgb[2] = i << 8;
1295 for (c = 0; c < 3; c++) {
1296 byte b = gx_color_value_to_byte(rgb[c]);
1297
1298 fputc(b, file);
1299 }
1300 }
1301 return 0;
1302 }
1303 /*
1304 * This routine will send any output data required at the end of a file
1305 * for a particular combination of planes and bits per plane.
1306 *
1307 * Please note that most combinations do not require anything at the end
1308 * of a data file.
1309 *
1310 * Input parameters:
1311 * pdev - Pointer to device data structure
1312 * file - output file
1313 * header - The header structure to hold the data.
1314 * bits_per_plane - The number of bits per plane.
1315 * num_planes - The number of planes.
1316 */
1317 private int
finish_pcx_file(gx_device_printer * pdev,FILE * file,pcx_header * header,int num_planes,int bits_per_plane)1318 finish_pcx_file(gx_device_printer * pdev, FILE * file, pcx_header * header, int num_planes, int bits_per_plane)
1319 {
1320 switch (num_planes) {
1321 case 1:
1322 switch (bits_per_plane) {
1323 case 1: /* Do nothing */
1324 break;
1325 case 2: /* Not defined */
1326 break;
1327 case 4: /* Do nothing */
1328 break;
1329 case 5: /* Not defined */
1330 break;
1331 case 8:
1332 fputc(0x0c, file);
1333 return pc_write_mono_palette((gx_device *) pdev, 256, file);
1334 case 16: /* Not defined */
1335 break;
1336 }
1337 break;
1338 case 2:
1339 switch (bits_per_plane) {
1340 case 1: /* Not defined */
1341 break;
1342 case 2: /* Not defined */
1343 break;
1344 case 4: /* Not defined */
1345 break;
1346 case 5: /* Not defined */
1347 break;
1348 case 8: /* Not defined */
1349 break;
1350 case 16: /* Not defined */
1351 break;
1352 }
1353 break;
1354 case 3:
1355 switch (bits_per_plane) {
1356 case 1: /* Not defined */
1357 break;
1358 case 2: /* Not defined */
1359 break;
1360 case 4: /* Not defined */
1361 break;
1362 case 5: /* Not defined */
1363 break;
1364 case 8: /* Do nothing */
1365 break;
1366 case 16: /* Not defined */
1367 break;
1368 }
1369 break;
1370 case 4:
1371 switch (bits_per_plane) {
1372 case 1: /* Do nothing */
1373 break;
1374 case 2: /* Not defined */
1375 break;
1376 case 4: /* Not defined */
1377 break;
1378 case 5: /* Not defined */
1379 break;
1380 case 8: /* Not defined */
1381 break;
1382 case 16: /* Not defined */
1383 break;
1384 }
1385 break;
1386 }
1387 return 0;
1388 }
1389
1390 /* Send the page to the printer. */
1391 private int
write_pcx_file(gx_device_printer * pdev,char * filename,int ncomp,int bpc,int linesize)1392 write_pcx_file(gx_device_printer * pdev, char * filename, int ncomp,
1393 int bpc, int linesize)
1394 {
1395 pcx_header header;
1396 int code;
1397 bool planar;
1398 char outname[gp_file_name_sizeof];
1399 FILE * in;
1400 FILE * out;
1401 int depth = bpc_to_depth(ncomp, bpc);
1402
1403 in = fopen(filename, "rb");
1404 if (!in)
1405 return_error(gs_error_invalidfileaccess);
1406 sprintf(outname, "%s.pcx", filename);
1407 out = fopen(outname, "wb");
1408 if (!out) {
1409 fclose(in);
1410 return_error(gs_error_invalidfileaccess);
1411 }
1412
1413 planar = setup_pcx_header(pdev, &header, ncomp, bpc);
1414 code = pcx_write_page(pdev, in, linesize, out, &header, planar, depth);
1415 if (code >= 0)
1416 code = finish_pcx_file(pdev, out, &header, ncomp, bpc);
1417
1418 fclose(in);
1419 fclose(out);
1420 return code;
1421 }
1422
1423 /* Write out a page in PCX format. */
1424 /* This routine is used for all formats. */
1425 /* The caller has set header->bpp, nplanes, and palette. */
1426 private int
pcx_write_page(gx_device_printer * pdev,FILE * infile,int linesize,FILE * outfile,pcx_header * phdr,bool planar,int depth)1427 pcx_write_page(gx_device_printer * pdev, FILE * infile, int linesize, FILE * outfile,
1428 pcx_header * phdr, bool planar, int depth)
1429 {
1430 int raster = linesize;
1431 uint rsize = ROUND_UP((pdev->width * phdr->bpp + 7) >> 3, 2); /* PCX format requires even */
1432 int height = pdev->height;
1433 uint lsize = raster + rsize;
1434 byte *line = gs_alloc_bytes(pdev->memory, lsize, "pcx file buffer");
1435 byte *plane = line + raster;
1436 int y;
1437 int code = 0; /* return code */
1438
1439 if (line == 0) /* can't allocate line buffer */
1440 return_error(gs_error_VMerror);
1441
1442 /* Fill in the other variable entries in the header struct. */
1443
1444 assign_ushort(phdr->x2, pdev->width - 1);
1445 assign_ushort(phdr->y2, height - 1);
1446 assign_ushort(phdr->hres, (int)pdev->x_pixels_per_inch);
1447 assign_ushort(phdr->vres, (int)pdev->y_pixels_per_inch);
1448 assign_ushort(phdr->bpl, (planar || depth == 1 ? rsize :
1449 raster + (raster & 1)));
1450
1451 /* Write the header. */
1452
1453 if (fwrite((const char *)phdr, 1, 128, outfile) < 128) {
1454 code = gs_error_ioerror;
1455 goto pcx_done;
1456 }
1457 /* Write the contents of the image. */
1458 for (y = 0; y < height; y++) {
1459 byte *row = line;
1460 byte *end;
1461
1462 code = fread(line, sizeof(byte), linesize, infile);
1463 if (code < 0)
1464 break;
1465 end = row + raster;
1466 if (!planar) { /* Just write the bits. */
1467 if (raster & 1) { /* Round to even, with predictable padding. */
1468 *end = end[-1];
1469 ++end;
1470 }
1471 pcx_write_rle(row, end, 1, outfile);
1472 } else
1473 switch (depth) {
1474
1475 case 4:
1476 {
1477 byte *pend = plane + rsize;
1478 int shift;
1479
1480 for (shift = 0; shift < 4; shift++) {
1481 register byte *from, *to;
1482 register int bright = 1 << shift;
1483 register int bleft = bright << 4;
1484
1485 for (from = row, to = plane;
1486 from < end; from += 4
1487 ) {
1488 *to++ =
1489 (from[0] & bleft ? 0x80 : 0) |
1490 (from[0] & bright ? 0x40 : 0) |
1491 (from[1] & bleft ? 0x20 : 0) |
1492 (from[1] & bright ? 0x10 : 0) |
1493 (from[2] & bleft ? 0x08 : 0) |
1494 (from[2] & bright ? 0x04 : 0) |
1495 (from[3] & bleft ? 0x02 : 0) |
1496 (from[3] & bright ? 0x01 : 0);
1497 }
1498 /* We might be one byte short of rsize. */
1499 if (to < pend)
1500 *to = to[-1];
1501 pcx_write_rle(plane, pend, 1, outfile);
1502 }
1503 }
1504 break;
1505
1506 case 24:
1507 {
1508 int pnum;
1509
1510 for (pnum = 0; pnum < 3; ++pnum) {
1511 pcx_write_rle(row + pnum, row + raster, 3, outfile);
1512 if (pdev->width & 1)
1513 fputc(0, outfile); /* pad to even */
1514 }
1515 }
1516 break;
1517
1518 default:
1519 code = gs_note_error(gs_error_rangecheck);
1520 goto pcx_done;
1521
1522 }
1523 code = 0;
1524 }
1525
1526 pcx_done:
1527 gs_free_object(pdev->memory, line, "pcx file buffer");
1528
1529 return code;
1530 }
1531
1532 /* ------ Internal routines ------ */
1533
1534 /* Write one line in PCX run-length-encoded format. */
1535 private void
pcx_write_rle(const byte * from,const byte * end,int step,FILE * file)1536 pcx_write_rle(const byte * from, const byte * end, int step, FILE * file)
1537 { /*
1538 * The PCX format theoretically allows encoding runs of 63
1539 * identical bytes, but some readers can't handle repetition
1540 * counts greater than 15.
1541 */
1542 #define MAX_RUN_COUNT 15
1543 int max_run = step * MAX_RUN_COUNT;
1544
1545 while (from < end) {
1546 byte data = *from;
1547
1548 from += step;
1549 if (data != *from || from == end) {
1550 if (data >= 0xc0)
1551 putc(0xc1, file);
1552 } else {
1553 const byte *start = from;
1554
1555 while ((from < end) && (*from == data))
1556 from += step;
1557 /* Now (from - start) / step + 1 is the run length. */
1558 while (from - start >= max_run) {
1559 putc(0xc0 + MAX_RUN_COUNT, file);
1560 putc(data, file);
1561 start += max_run;
1562 }
1563 if (from > start || data >= 0xc0)
1564 putc((from - start) / step + 0xc1, file);
1565 }
1566 putc(data, file);
1567 }
1568 #undef MAX_RUN_COUNT
1569 }
1570