xref: /plan9/sys/src/cmd/gs/src/gdevperm.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2002 artofcode LLC.  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: gdevperm.c,v 1.4 2004/06/18 07:00:47 dan Exp $ */
18 /* Device which permutes color components, for testing DeviceN. */
19 #include "gdevprn.h"
20 #include "gxdcconv.h"
21 
22 /**
23  * With no additional parameters, the device named "permute" looks to
24  * Ghostscript like a standard CMYK contone device, and outputs a PPM
25  * file, using a simple CMYK->RGB transform. This should be the
26  * baseline for regression testing.
27  *
28  * With the addition of -dPermute=1, the internal behavior changes
29  * somewhat, but in most cases the resulting rendered file should be
30  * the same. In this mode, the color model becomes "DeviceN" rather
31  * than "DeviceCMYK", the number of components goes to six, and the
32  * color model is considered to be the (yellow, cyan, cyan, magenta,
33  * 0, black) tuple. This is what's rendered into the memory
34  * buffer. Finally, on conversion to RGB for output, the colors are
35  * permuted back.
36  *
37  * As such, this code should check that all imaging code paths are
38  * 64-bit clean. Additionally, it should find incorrect code that
39  * assumes that the color model is one of DeviceGray, DeviceRGB, or
40  * DeviceCMYK.
41  **/
42 
43 private dev_proc_print_page(perm_print_page);
44 private dev_proc_get_params(perm_get_params);
45 private dev_proc_put_params(perm_put_params);
46 private dev_proc_get_color_mapping_procs(perm_get_color_mapping_procs);
47 private dev_proc_get_color_comp_index(perm_get_color_comp_index);
48 private dev_proc_encode_color(perm_encode_color);
49 private dev_proc_decode_color(perm_decode_color);
50 
51 struct gx_device_perm_s {
52     gx_device_common;
53     gx_prn_device_common;
54     const char **std_colorant_names;
55     int num_std_colorant_names;	/* Number of names in list */
56     int mode;
57     int permute;
58 };
59 typedef struct gx_device_perm_s gx_device_perm_t;
60 
61 private const gx_device_procs perm_procs = {
62     gdev_prn_open,
63     NULL,
64     NULL,
65     gdev_prn_output_page,
66     gdev_prn_close,
67     NULL,
68     NULL,
69     NULL,				/* fill_rectangle */
70     NULL,				/* tile_rectangle */
71     NULL,				/* copy_mono */
72     NULL,				/* copy_color */
73     NULL,				/* draw_line */
74     NULL,				/* get_bits */
75     perm_get_params,		        /* get_params */
76     perm_put_params,		        /* put_params */
77     NULL,				/* map_cmyk_color - not used */
78     NULL,				/* get_xfont_procs */
79     NULL,				/* get_xfont_device */
80     NULL,				/* map_rgb_alpha_color */
81     gx_page_device_get_page_device,	/* get_page_device */
82     NULL,				/* get_alpha_bits */
83     NULL,				/* copy_alpha */
84     NULL,				/* get_band */
85     NULL,				/* copy_rop */
86     NULL,				/* fill_path */
87     NULL,				/* stroke_path */
88     NULL,				/* fill_mask */
89     NULL,				/* fill_trapezoid */
90     NULL,				/* fill_parallelogram */
91     NULL,				/* fill_triangle */
92     NULL,				/* draw_thin_line */
93     NULL,				/* begin_image */
94     NULL,				/* image_data */
95     NULL,				/* end_image */
96     NULL,				/* strip_tile_rectangle */
97     NULL,				/* strip_copy_rop */
98     NULL,				/* get_clipping_box */
99     NULL,				/* begin_typed_image */
100     NULL,				/* get_bits_rectangle */
101     NULL,				/* map_color_rgb_alpha */
102     NULL,				/* create_compositor */
103     NULL,				/* get_hardware_params */
104     NULL,				/* text_begin */
105     NULL,				/* finish_copydevice */
106     NULL,				/* begin_transparency_group */
107     NULL,				/* end_transparency_group */
108     NULL,				/* begin_transparency_mask */
109     NULL,				/* end_transparency_mask */
110     NULL,				/* discard_transparency_layer */
111     perm_get_color_mapping_procs,	/* get_color_mapping_procs */
112     perm_get_color_comp_index,
113     perm_encode_color,		/* encode_color */
114     perm_decode_color		/* decode_color */
115 
116 };
117 
118 const gx_device_perm_t gs_perm_device = {
119     prn_device_body_extended(gx_device_perm_t, perm_procs, "permute",
120 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, 72, 72,
121 	0, 0, 0, 0,
122 	GX_DEVICE_COLOR_MAX_COMPONENTS, 4,
123 	GX_CINFO_POLARITY_SUBTRACTIVE,
124 	32, 0, 255, 255, 256, 256,
125 	GX_CINFO_SEP_LIN,
126 	"DeviceN",
127 	perm_print_page),
128     NULL, 0, 0, 0
129 };
130 
131 
132 private int
perm_print_page(gx_device_printer * pdev,FILE * pstream)133 perm_print_page(gx_device_printer *pdev, FILE *pstream)
134 {
135     int y;
136     gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
137     int ncomp = dev->num_std_colorant_names;
138     int raw_raster = pdev->width * ncomp;
139     byte *raw_line;
140     byte *cooked_line;
141     byte *row;
142     int code = 0;
143     int mode = dev->mode;
144     int permute = dev->permute;
145 
146     fprintf(pstream, "P6\n%d %d\n255\n", dev->width, dev->height);
147     raw_line = gs_alloc_bytes(pdev->memory, raw_raster, "perm_print_page");
148     cooked_line = gs_alloc_bytes(pdev->memory, dev->width * 3, "perm_print_page");
149     for (y = 0; y < dev->height; y++) {
150 	int x;
151 	code = gdev_prn_get_bits(pdev, y, raw_line, &row);
152 	for (x = 0; x < dev->width; x++) {
153 	    int c, m, y, k;
154 	    int r, g, b;
155 
156 	    if (mode == 0) {
157 		if (permute) {
158 		    c = row[x * ncomp + 1];
159 		    m = row[x * ncomp + 3];
160 		    y = row[x * ncomp + 0];
161 		    k = row[x * ncomp + 5];
162 		} else {
163 		    c = row[x * ncomp];
164 		    m = row[x * ncomp + 1];
165 		    y = row[x * ncomp + 2];
166 		    k = row[x * ncomp + 3];
167 		}
168 	    } else /* if (mode == 1) */ {
169 		if (permute) {
170 		    c = row[x * ncomp + 1];
171 		    m = row[x * ncomp + 3];
172 		    y = row[x * ncomp + 0];
173 		    k = 0;
174 		} else {
175 		    c = row[x * ncomp];
176 		    m = row[x * ncomp + 1];
177 		    y = row[x * ncomp + 2];
178 		    k = 0;
179 		}
180 	    }
181 	    r = (255 - c) * (255 - k) / 255;
182 	    g = (255 - m) * (255 - k) / 255;
183 	    b = (255 - y) * (255 - k) / 255;
184 	    cooked_line[x * 3] = r;
185 	    cooked_line[x * 3 + 1] = g;
186 	    cooked_line[x * 3 + 2] = b;
187 	}
188 	fwrite(cooked_line, 1, dev->width * 3, pstream);
189     }
190     gs_free_object(pdev->memory, cooked_line, "perm_print_page");
191     gs_free_object(pdev->memory, raw_line, "perm_print_page");
192     return code;
193 }
194 
195 private void
perm_permute_cm(gx_device * pdev,frac out[])196 perm_permute_cm(gx_device *pdev, frac out[])
197 {
198     gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
199     if (dev->permute) {
200 	frac y;
201 	out[5] = dev->mode == 0 ? out[3] : 0;
202 	out[4] = frac_0;
203 	y = out[2];
204 	out[3] = out[1];
205 	out[2] = out[0];
206 	out[1] = out[0];
207 	out[0] = y;
208     }
209 }
210 
211 private void
gray_cs_to_perm_cm_0(gx_device * dev,frac gray,frac out[])212 gray_cs_to_perm_cm_0(gx_device *dev, frac gray, frac out[])
213 {
214     out[0] = out[1] = out[2] = frac_0;
215     out[3] = frac_1 - gray;
216     perm_permute_cm(dev, out);
217 }
218 
219 private void
rgb_cs_to_perm_cm_0(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])220 rgb_cs_to_perm_cm_0(gx_device *dev, const gs_imager_state *pis,
221 				  frac r, frac g, frac b, frac out[])
222 {
223     color_rgb_to_cmyk(r, g, b, pis, out);
224     perm_permute_cm(dev, out);
225 }
226 
227 private void
cmyk_cs_to_perm_cm_0(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])228 cmyk_cs_to_perm_cm_0(gx_device *dev, frac c, frac m, frac y, frac k, frac out[])
229 {
230     out[0] = c;
231     out[1] = m;
232     out[2] = y;
233     out[3] = k;
234     perm_permute_cm(dev, out);
235 };
236 
237 private void
gray_cs_to_perm_cm_1(gx_device * dev,frac gray,frac out[])238 gray_cs_to_perm_cm_1(gx_device *dev, frac gray, frac out[])
239 {
240     out[0] = out[1] = out[2] = frac_1 - gray;
241     perm_permute_cm(dev, out);
242 }
243 
244 private void
rgb_cs_to_perm_cm_1(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])245 rgb_cs_to_perm_cm_1(gx_device *dev, const gs_imager_state *pis,
246 				  frac r, frac g, frac b, frac out[])
247 {
248     out[0] = frac_1 - r;
249     out[1] = frac_1 - g;
250     out[2] = frac_1 - b;
251     perm_permute_cm(dev, out);
252 }
253 
254 private void
cmyk_cs_to_perm_cm_1(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])255 cmyk_cs_to_perm_cm_1(gx_device *dev, frac c, frac m, frac y, frac k, frac out[])
256 {
257     color_cmyk_to_rgb(c, m, y, k, NULL, out);
258     out[0] = frac_1 - out[0];
259     out[1] = frac_1 - out[1];
260     out[2] = frac_1 - out[2];
261     perm_permute_cm(dev, out);
262 };
263 
264 private const gx_cm_color_map_procs perm_cmapping_procs_0 = {
265     gray_cs_to_perm_cm_0, rgb_cs_to_perm_cm_0, cmyk_cs_to_perm_cm_0
266 };
267 
268 private const gx_cm_color_map_procs perm_cmapping_procs_1 = {
269     gray_cs_to_perm_cm_1, rgb_cs_to_perm_cm_1, cmyk_cs_to_perm_cm_1
270 };
271 
272 private const gx_cm_color_map_procs *perm_cmapping_procs[] = {
273     &perm_cmapping_procs_0,
274     &perm_cmapping_procs_1
275 };
276 
277 private const gx_cm_color_map_procs *
perm_get_color_mapping_procs(const gx_device * dev)278 perm_get_color_mapping_procs(const gx_device *dev)
279 {
280     const gx_device_perm_t * const pdev = (const gx_device_perm_t *)dev;
281 
282     if (pdev->mode < 0 || pdev->mode >= sizeof(perm_cmapping_procs) / sizeof(perm_cmapping_procs[0]))
283 	return NULL;
284     return perm_cmapping_procs[pdev->mode];
285 }
286 
287 #define compare_color_names(name, name_size, str, str_size) \
288     (name_size == str_size && \
289 	(strncmp((const char *)name, (const char *)str, name_size) == 0))
290 
291 private int
perm_get_color_comp_index(const gx_device * pdev,const char * pname,int name_size,int component_type)292 perm_get_color_comp_index(const gx_device *pdev, const char *pname,
293 					int name_size, int component_type)
294 {
295     const gx_device_perm_t * const dev = (const gx_device_perm_t *)pdev;
296     int n_separation_names = dev->num_std_colorant_names;
297     int i;
298 
299     for (i = 0; i < n_separation_names; i++) {
300 	const char *sep_name = dev->std_colorant_names[i];
301 	if (compare_color_names(pname, name_size, sep_name, strlen(sep_name)))
302 	    return i;
303     }
304     return -1;
305 }
306 
307 /* Note: the encode and decode procs are entirely standard. The
308    permutation is all done in the color space to color model mapping.
309    In fact, we could probably just use the default here.
310 */
311 
312 /*
313  * Encode a list of colorant values into a gx_color_index_value.
314  */
315 private gx_color_index
perm_encode_color(gx_device * dev,const gx_color_value colors[])316 perm_encode_color(gx_device *dev, const gx_color_value colors[])
317 {
318     int bpc = 8;
319     int drop = sizeof(gx_color_value) * 8 - bpc;
320     gx_color_index color = 0;
321     int i = 0;
322     int ncomp = dev->color_info.num_components;
323 
324     for (; i<ncomp; i++) {
325 	color <<= bpc;
326         color |= (colors[i] >> drop);
327     }
328     return (color == gx_no_color_index ? color ^ 1 : color);
329 }
330 
331 /*
332  * Decode a gx_color_index value back to a list of colorant values.
333  */
334 private int
perm_decode_color(gx_device * dev,gx_color_index color,gx_color_value * out)335 perm_decode_color(gx_device *dev, gx_color_index color, gx_color_value *out)
336 {
337     int bpc = 8;
338     int drop = sizeof(gx_color_value) * 8 - bpc;
339     int mask = (1 << bpc) - 1;
340     int i = 0;
341     int ncomp = dev->color_info.num_components;
342 
343     for (; i<ncomp; i++) {
344         out[ncomp - i - 1] = (gx_color_value)((color & mask) << drop);
345 	color >>= bpc;
346     }
347     return 0;
348 }
349 
350 #define set_param_array(a, d, s)\
351   (a.data = d, a.size = s, a.persistent = false);
352 
353 private int
perm_get_params(gx_device * pdev,gs_param_list * plist)354 perm_get_params(gx_device *pdev, gs_param_list *plist)
355 {
356     gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
357     int code;
358 
359     code = param_write_int(plist, "Permute", &dev->permute);
360     if (code >= 0)
361 	code = param_write_int(plist, "Mode", &dev->mode);
362     /*
363      * We need to specify the SeparationColorNames if we are permuting the colors.
364      */
365     if (code >= 0 && dev->permute == 1) {
366 	int i;
367 	/* Temp variables.  The data is copied into the plist below. */
368         gs_param_string_array scna;
369         gs_param_string scn[6];
370 
371         set_param_array(scna, scn, dev->num_std_colorant_names);
372 	/* Place colorant names into string array elements */
373 	for (i = 0; i < dev->num_std_colorant_names; i++)
374 	    param_string_from_string(scn[i], dev->std_colorant_names[i]);
375 	/*
376 	 * Place the name array in the plist.  This includes allocating
377 	 * memory for the name array element and the actual string array.
378 	 */
379 	code = param_write_name_array(plist, "SeparationColorNames", &scna);
380     }
381     if (code >= 0)
382 	code = gdev_prn_get_params(pdev, plist);
383     return code;
384 }
385 
386 #undef set_param_array
387 
388 private const char * DeviceCMYKComponents[] = {
389 	"Cyan",
390 	"Magenta",
391 	"Yellow",
392 	"Black",
393 	0		/* List terminator */
394 };
395 
396 private const char * DeviceCMYComponents[] = {
397 	"Cyan",
398 	"Magenta",
399 	"Yellow",
400 	0		/* List terminator */
401 };
402 
403 private const char * DeviceNComponents[] = {
404 	"Yellow",
405 	"Cyan",
406 	"Cyan2",
407 	"Magenta",
408 	"Zero",
409 	"Black",
410 	0		/* List terminator */
411 };
412 
413 private int
perm_set_color_model(gx_device_perm_t * dev,int mode,int permute)414 perm_set_color_model(gx_device_perm_t *dev, int mode, int permute)
415 {
416     dev->mode = mode;
417     dev->permute = permute;
418     if (mode == 0 && permute == 0) {
419 	dev->std_colorant_names = DeviceCMYKComponents;
420 	dev->num_std_colorant_names = 4;
421 	dev->color_info.cm_name = "DeviceCMYK";
422 	dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
423     } else if (mode == 0 && permute == 1) {
424 	dev->std_colorant_names = DeviceNComponents;
425 	dev->num_std_colorant_names = 6;
426 	dev->color_info.cm_name = "DeviceN";
427 	dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
428     } else if (mode == 1 && permute == 0) {
429 	dev->std_colorant_names = DeviceCMYComponents;
430 	dev->num_std_colorant_names = 3;
431 	dev->color_info.cm_name = "DeviceCMY";
432 	dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
433     } else if (mode == 1 && permute == 1) {
434 	dev->std_colorant_names = DeviceNComponents;
435 	dev->num_std_colorant_names = 6;
436 	dev->color_info.cm_name = "DeviceN";
437 	dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
438     } else {
439 	return -1;
440     }
441     dev->color_info.num_components = dev->num_std_colorant_names;
442     dev->color_info.depth = 8 * dev->num_std_colorant_names;
443 
444     return 0;
445 }
446 
447 private int
perm_put_params(gx_device * pdev,gs_param_list * plist)448 perm_put_params(gx_device *pdev, gs_param_list *plist)
449 {
450     gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
451     gx_device_color_info save_info;
452     int code;
453     int new_permute = dev->permute;
454     int new_mode = dev->mode;
455 
456     code = param_read_int(plist, "Permute", &new_permute);
457     if (code < 0)
458 	return code;
459     code = param_read_int(plist, "Mode", &new_mode);
460     if (code < 0)
461 	return code;
462     if (new_mode < 0 || new_mode >= sizeof(perm_cmapping_procs) / sizeof(perm_cmapping_procs[0])) {
463 	dlprintf("rangecheck!\n");
464 	return_error(gs_error_rangecheck);
465     }
466     dev->permute = new_permute;
467     dev->mode = new_mode;
468     save_info = pdev->color_info;
469     code = perm_set_color_model(dev, dev->mode, dev->permute);
470     if (code >= 0)
471 	code = gdev_prn_put_params(pdev, plist);
472     if (code < 0)
473 	pdev->color_info = save_info;
474     return code;
475 }
476 
477