xref: /plan9/sys/src/cmd/gs/src/gdevdevn.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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