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