1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2 All Rights Reserved.
3
4 This software is provided AS-IS with no warranty, either express or
5 implied.
6
7 This software is distributed under license and may not be copied,
8 modified or distributed except as expressly authorized under the terms
9 of the license contained in the file LICENSE in this distribution.
10
11 Refer to licensing information at http://www.artifex.com or contact
12 Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
13 CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15
16 /* Portable Bit/Gray/PixMap drivers */
17 #include "gdevprn.h"
18 #include "gscdefs.h"
19 #include "gscspace.h" /* For pnm_begin_typed_image(..) */
20 #include "gxgetbit.h"
21 #include "gxlum.h"
22 #include "gxiparam.h" /* For pnm_begin_typed_image(..) */
23 #include "gdevmpla.h"
24 #include "gdevplnx.h"
25 #include "gdevppla.h"
26
27 /*
28 * Thanks are due to Jos Vos (jos@bull.nl) for an earlier P*M driver,
29 * on which this one is based; to Nigel Roles (ngr@cotswold.demon.co.uk),
30 * for the plan9bm changes; and to Leon Bottou (leonb@research.att.com)
31 * for the color detection code in pnm_begin_typed_image.
32 */
33
34 /*
35 * There are 8 (families of) drivers here, plus one less related one:
36 * pbm[raw] - outputs PBM (black and white).
37 * pgm[raw] - outputs PGM (gray-scale).
38 * pgnm[raw] - outputs PBM if the page contains only black and white,
39 * otherwise PGM.
40 * ppm[raw] - outputs PPM (RGB).
41 * pnm[raw] - outputs PBM if the page contains only black and white,
42 * otherwise PGM if the page contains only gray shades,
43 * otherwise PPM.
44 * If GrayDetection is true, then the pageneutral color is used to decide
45 between PGM and PPM.
46 * pkm[raw] - computes internally in CMYK, outputs PPM (RGB).
47 * pksm[raw] - computes internally in CMYK, outputs 4 PBM pages.
48 * pamcmyk4 - outputs CMYK as PAM 1-bit per color
49 * pamcmyk32 - outputs CMYK as PAM 8-bits per color
50 * pnmcmyk - With GrayDetection true, outputs either the 8-bit K plane as PGM or
51 * 32-bit CMYK (pam 8-bit per component) depending on pageneutralcolor.
52 * pam - previous name for the pamcmyk32 device retained for backwards compatibility
53 * plan9bm - outputs Plan 9 bitmap format.
54 */
55
56 /*
57 * The code here is designed to work with variable depths for PGM and PPM.
58 * The code will work with any of the values in brackets, but the
59 * Ghostscript imager requires that depth be a power of 2 or be 24,
60 * so the actual allowed values are more limited.
61 * pgm, pgnm: 1, 2, 4, 8, 16. [1-16]
62 * pgmraw, pgnmraw: 1, 2, 4, 8. [1-8]
63 * ppm, pnm: 4(3x1), 8(3x2), 16(3x5), 24(3x8), 32(3x10). [3-32]
64 * ppmraw, pnmraw: 4(3x1), 8(3x2), 16(3x5), 24(3x8). [3-24]
65 * pkm, pkmraw: 4(4x1), 8(4x2), 16(4x4), 32(4x8). [4-32]
66 * pksm, pksmraw: ibid.
67 * pam: 32 (CMYK), 4 (CMYK)
68 */
69
70 /* Structure for P*M devices, which extend the generic printer device. */
71
72 #define MAX_COMMENT 70 /* max user-supplied comment */
73 struct gx_device_pbm_s {
74 gx_device_common;
75 gx_prn_device_common;
76 /* Additional state for P*M devices */
77 char magic; /* n for "Pn" */
78 char comment[MAX_COMMENT + 1]; /* comment for head of file */
79 byte is_raw; /* 1 if raw format, 0 if plain */
80 byte optimize; /* 1 if optimization OK, 0 if not */
81 byte uses_color; /* 0 if image is black and white, */
82 /* 1 if gray (PGM or PPM only), */
83 /* 2 or 3 if colored (PPM only) */
84 bool UsePlanarBuffer; /* 0 if chunky buffer, 1 if planar */
85 dev_proc_copy_alpha((*save_copy_alpha));
86 dev_proc_begin_typed_image((*save_begin_typed_image));
87 };
88 typedef struct gx_device_pbm_s gx_device_pbm;
89
90 /* ------ The device descriptors ------ */
91
92 /*
93 * Default X and Y resolution.
94 */
95 #define X_DPI 72
96 #define Y_DPI 72
97
98 /* Macro for generating P*M device descriptors. */
99 #define pbm_prn_device(procs, dev_name, magic, is_raw, num_comp, depth, max_gray, max_rgb, optimize, x_dpi, y_dpi, print_page)\
100 { prn_device_body(gx_device_pbm, procs, dev_name,\
101 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi,\
102 0, 0, 0, 0,\
103 num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
104 print_page),\
105 magic,\
106 { 0 },\
107 is_raw,\
108 optimize,\
109 0, 0, 0\
110 }
111
112 /* For all but PBM, we need our own color mapping and alpha procedures. */
113 static dev_proc_map_rgb_color(pgm_map_rgb_color);
114 static dev_proc_map_rgb_color(ppm_map_rgb_color);
115 static dev_proc_map_color_rgb(pgm_map_color_rgb);
116 static dev_proc_map_color_rgb(ppm_map_color_rgb);
117 static dev_proc_map_cmyk_color(pkm_map_cmyk_color);
118 static dev_proc_map_color_rgb(pkm_map_color_rgb);
119 static dev_proc_get_params(ppm_get_params);
120 static dev_proc_put_params(ppm_put_params);
121 static dev_proc_copy_alpha(pnm_copy_alpha);
122 static dev_proc_begin_typed_image(pnm_begin_typed_image);
123
124 /* We need to initialize uses_color when opening the device, */
125 /* and after each showpage. */
126 static dev_proc_open_device(ppm_open);
127 static dev_proc_open_device(pnmcmyk_open);
128 static dev_proc_output_page(ppm_output_page);
129
130 /* And of course we need our own print-page routines. */
131 static dev_proc_print_page(pbm_print_page);
132 static dev_proc_print_page(pgm_print_page);
133 static dev_proc_print_page(ppm_print_page);
134 static dev_proc_print_page(pkm_print_page);
135 static dev_proc_print_page(psm_print_page);
136 static dev_proc_print_page(psm_print_page);
137 static dev_proc_print_page(pam_print_page);
138 static dev_proc_print_page(pam4_print_page);
139 static dev_proc_print_page(pnmcmyk_print_page);
140
141 /* The device procedures */
142
143 /* See gdevprn.h for the template for the following. */
144 #define pgpm_procs(p_open, p_get_params, p_map_rgb_color, p_map_color_rgb, p_map_cmyk_color) {\
145 p_open, NULL, NULL, ppm_output_page, gdev_prn_close,\
146 p_map_rgb_color, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
147 p_get_params, ppm_put_params,\
148 p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device\
149 }
150
151 static const gx_device_procs pbm_procs =
152 pgpm_procs(gdev_prn_open, gdev_prn_get_params,
153 gdev_prn_map_rgb_color, gdev_prn_map_color_rgb, NULL);
154 static const gx_device_procs pgm_procs =
155 pgpm_procs(ppm_open, gdev_prn_get_params,
156 pgm_map_rgb_color, pgm_map_color_rgb, NULL);
157 static const gx_device_procs ppm_procs =
158 pgpm_procs(ppm_open, ppm_get_params,
159 gx_default_rgb_map_rgb_color, ppm_map_color_rgb, NULL);
160 static const gx_device_procs pnm_procs =
161 pgpm_procs(ppm_open, ppm_get_params,
162 ppm_map_rgb_color, ppm_map_color_rgb, NULL);
163 static const gx_device_procs pkm_procs =
164 pgpm_procs(ppm_open, ppm_get_params,
165 NULL, cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color);
166 static const gx_device_procs pam_procs =
167 pgpm_procs(ppm_open, ppm_get_params,
168 NULL, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color);
169 static const gx_device_procs pnmcmyk_procs =
170 pgpm_procs(pnmcmyk_open, ppm_get_params,
171 NULL, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color);
172
173 /* The device descriptors themselves */
174 const gx_device_pbm gs_pbm_device =
175 pbm_prn_device(pbm_procs, "pbm", '1', 0, 1, 1, 1, 0, 0,
176 X_DPI, Y_DPI, pbm_print_page);
177 const gx_device_pbm gs_pbmraw_device =
178 pbm_prn_device(pbm_procs, "pbmraw", '4', 1, 1, 1, 1, 1, 0,
179 X_DPI, Y_DPI, pbm_print_page);
180 const gx_device_pbm gs_pgm_device =
181 pbm_prn_device(pgm_procs, "pgm", '2', 0, 1, 8, 255, 0, 0,
182 X_DPI, Y_DPI, pgm_print_page);
183 const gx_device_pbm gs_pgmraw_device =
184 pbm_prn_device(pgm_procs, "pgmraw", '5', 1, 1, 8, 255, 0, 0,
185 X_DPI, Y_DPI, pgm_print_page);
186 const gx_device_pbm gs_pgnm_device =
187 pbm_prn_device(pgm_procs, "pgnm", '2', 0, 1, 8, 255, 0, 1,
188 X_DPI, Y_DPI, pgm_print_page);
189 const gx_device_pbm gs_pgnmraw_device =
190 pbm_prn_device(pgm_procs, "pgnmraw", '5', 1, 1, 8, 255, 0, 1,
191 X_DPI, Y_DPI, pgm_print_page);
192 const gx_device_pbm gs_ppm_device =
193 pbm_prn_device(ppm_procs, "ppm", '3', 0, 3, 24, 255, 255, 0,
194 X_DPI, Y_DPI, ppm_print_page);
195 const gx_device_pbm gs_ppmraw_device =
196 pbm_prn_device(ppm_procs, "ppmraw", '6', 1, 3, 24, 255, 255, 0,
197 X_DPI, Y_DPI, ppm_print_page);
198 const gx_device_pbm gs_pnm_device =
199 pbm_prn_device(pnm_procs, "pnm", '3', 0, 3, 24, 255, 255, 1,
200 X_DPI, Y_DPI, ppm_print_page);
201 const gx_device_pbm gs_pnmraw_device =
202 pbm_prn_device(pnm_procs, "pnmraw", '6', 1, 3, 24, 255, 255, 1,
203 X_DPI, Y_DPI, ppm_print_page);
204 const gx_device_pbm gs_pkm_device =
205 pbm_prn_device(pkm_procs, "pkm", '3', 0, 4, 4, 1, 1, 0,
206 X_DPI, Y_DPI, pkm_print_page);
207 const gx_device_pbm gs_pkmraw_device =
208 pbm_prn_device(pkm_procs, "pkmraw", '6', 1, 4, 4, 1, 1, 0,
209 X_DPI, Y_DPI, pkm_print_page);
210 const gx_device_pbm gs_pksm_device =
211 pbm_prn_device(pkm_procs, "pksm", '1', 0, 4, 4, 1, 1, 0,
212 X_DPI, Y_DPI, psm_print_page);
213 const gx_device_pbm gs_pksmraw_device =
214 pbm_prn_device(pkm_procs, "pksmraw", '4', 1, 4, 4, 1, 1, 0,
215 X_DPI, Y_DPI, psm_print_page);
216 const gx_device_pbm gs_pamcmyk32_device =
217 pbm_prn_device(pam_procs, "pamcmyk32", '7', 1, 4, 32, 255, 255, 0,
218 X_DPI, Y_DPI, pam_print_page);
219 const gx_device_pbm gs_pnmcmyk_device =
220 pbm_prn_device(pnmcmyk_procs, "pnmcmyk", '7', 1, 4, 32, 255, 255, 0, /* optimize false since this relies on GrayDetection */
221 X_DPI, Y_DPI, pnmcmyk_print_page); /* May output PGM, magic = 5 */
222 const gx_device_pbm gs_pamcmyk4_device =
223 pbm_prn_device(pam_procs, "pamcmyk4", '7', 1, 4, 4, 1, 1, 0,
224 X_DPI, Y_DPI, pam4_print_page);
225 /* Also keep the old device name so anyone using it won't be surprised */
226 const gx_device_pbm gs_pam_device =
227 pbm_prn_device(pam_procs, "pam", '7', 1, 4, 32, 255, 255, 0,
228 X_DPI, Y_DPI, pam_print_page);
229
230 /* Plan 9 bitmaps default to 100 dpi. */
231 const gx_device_pbm gs_plan9bm_device =
232 pbm_prn_device(pbm_procs, "plan9bm", '9', 1, 1, 1, 1, 1, 1,
233 100, 100, pbm_print_page);
234
235 /* ------ Initialization ------ */
236
237 /* Set the copy_alpha and color mapping procedures if necessary. */
238 static void
ppm_set_dev_procs(gx_device * pdev)239 ppm_set_dev_procs(gx_device * pdev)
240 {
241 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
242
243 if (dev_proc(pdev, copy_alpha) != pnm_copy_alpha) {
244 bdev->save_copy_alpha = dev_proc(pdev, copy_alpha);
245 if (pdev->color_info.depth > 4)
246 set_dev_proc(pdev, copy_alpha, pnm_copy_alpha);
247 }
248 if (dev_proc(pdev, begin_typed_image) != pnm_begin_typed_image) {
249 bdev->save_begin_typed_image = dev_proc(pdev, begin_typed_image);
250 set_dev_proc(pdev, begin_typed_image, pnm_begin_typed_image);
251 }
252 if (bdev->color_info.num_components == 4) {
253 if (bdev->color_info.depth == 4) {
254 set_dev_proc(pdev, map_color_rgb, cmyk_1bit_map_color_rgb);
255 set_dev_proc(pdev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
256 } else if (bdev->magic == 7) {
257 set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb);
258 set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
259 } else {
260 set_dev_proc(pdev, map_color_rgb, pkm_map_color_rgb);
261 set_dev_proc(pdev, map_cmyk_color, pkm_map_cmyk_color);
262 }
263 }
264 }
265
266 /*
267 * Define a special open procedure that changes create_buf_device to use
268 * a planar device.
269 */
270
271 static int
ppm_open(gx_device * pdev)272 ppm_open(gx_device * pdev)
273 {
274 gx_device_pbm * bdev = (gx_device_pbm *)pdev;
275 int code;
276
277 #ifdef TEST_PAD_AND_ALIGN
278 pdev->pad = 5;
279 pdev->log2_align_mod = 6;
280 #endif
281
282 code = gdev_prn_open_planar(pdev, bdev->UsePlanarBuffer);
283 while (pdev->child)
284 pdev = pdev->child;
285
286 bdev = (gx_device_pbm *)pdev;;
287
288 if (code < 0)
289 return code;
290 pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
291 set_linear_color_bits_mask_shift(pdev);
292 bdev->uses_color = 0;
293 ppm_set_dev_procs(pdev);
294 return code;
295 }
296
297 /*
298 * For pnmcmyk, we set GrayDection true by default. This may be overridden by
299 * the default_put_params, but that's OK.
300 */
301 static int
pnmcmyk_open(gx_device * pdev)302 pnmcmyk_open(gx_device *pdev)
303 {
304 pdev->icc_struct->graydetection = true;
305 pdev->icc_struct->pageneutralcolor = true; /* enable detection */
306
307 return ppm_open(pdev);
308 }
309
310 /* Print a page, and reset uses_color if this is a showpage. */
311 static int
ppm_output_page(gx_device * pdev,int num_copies,int flush)312 ppm_output_page(gx_device * pdev, int num_copies, int flush)
313 {
314 /* Safe to start the page in the background */
315 int code = gdev_prn_bg_output_page(pdev, num_copies, flush);
316 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
317
318 if (code < 0)
319 return code;
320 if (flush)
321 bdev->uses_color = 0;
322 return code;
323 }
324
325 /* ------ Color mapping routines ------ */
326
327 /* Map an RGB color to a PGM gray value. */
328 /* Keep track of whether the image is black-and-white or gray. */
329 static gx_color_index
pgm_map_rgb_color(gx_device * pdev,const gx_color_value cv[])330 pgm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
331 { /* We round the value rather than truncating it. */
332 gx_color_value gray;
333 /* TO_DO_DEVICEN - Kludge to emulate pre DeviceN math errors */
334 #if 1
335 gx_color_value r, g, b;
336
337 r = cv[0]; g = cv[0]; b = cv[0];
338 gray = ((r * (ulong) lum_red_weight) +
339 (g * (ulong) lum_green_weight) +
340 (b * (ulong) lum_blue_weight) +
341 (lum_all_weights / 2)) / lum_all_weights
342 * pdev->color_info.max_gray / gx_max_color_value;
343 #else /* Should be ... */
344 gray = cv[0] * pdev->color_info.max_gray / gx_max_color_value;
345 #endif
346
347 if (!(gray == 0 || gray == pdev->color_info.max_gray)) {
348 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
349
350 bdev->uses_color = 1;
351 }
352 return gray;
353 }
354
355 /* Map a PGM gray value back to an RGB color. */
356 static int
pgm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])357 pgm_map_color_rgb(gx_device * dev, gx_color_index color,
358 gx_color_value prgb[3])
359 {
360 gx_color_value gray =
361 color * gx_max_color_value / dev->color_info.max_gray;
362
363 prgb[0] = gray;
364 prgb[1] = gray;
365 prgb[2] = gray;
366 return 0;
367 }
368
369 /*
370 * Pre gs8.00 version of RGB mapping for 24-bit true (RGB) color devices
371 * It is kept here for backwards comparibility since the gs8.00 version
372 * has changed in functionality. The new one requires that the device be
373 * 'separable'. This routine is logically separable but does not require
374 * the various color_info fields associated with separability (comp_shift,
375 * comp_bits, and comp_mask) be setup.
376 */
377
378 static gx_color_index
gx_old_default_rgb_map_rgb_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b)379 gx_old_default_rgb_map_rgb_color(gx_device * dev,
380 gx_color_value r, gx_color_value g, gx_color_value b)
381 {
382 if (dev->color_info.depth == 24)
383 return gx_color_value_to_byte(b) +
384 ((uint) gx_color_value_to_byte(g) << 8) +
385 ((ulong) gx_color_value_to_byte(r) << 16);
386 else {
387 int bpc = dev->color_info.depth / 3;
388 int drop = sizeof(gx_color_value) * 8 - bpc;
389
390 return (((((gx_color_index)(r >> drop)) << bpc) + (g >> drop)) << bpc) + (b >> drop);
391 }
392 }
393
394 /* Map an RGB color to a PPM color tuple. */
395 /* Keep track of whether the image is black-and-white, gray, or colored. */
396 static gx_color_index
ppm_map_rgb_color(gx_device * pdev,const gx_color_value cv[])397 ppm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
398 {
399 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
400 gx_color_index color =
401 gx_old_default_rgb_map_rgb_color(pdev, cv[0], cv[1], cv[2]);
402 uint bpc = pdev->color_info.depth / 3;
403 gx_color_index mask =
404 ((gx_color_index)1 << (pdev->color_info.depth - bpc)) - 1;
405 if (!(((color >> bpc) ^ color) & mask)) { /* gray shade */
406 if (color != 0 && (~color & mask))
407 bdev->uses_color |= 1;
408 } else /* color */
409 bdev->uses_color = 2;
410 return color;
411 }
412
413 /* Map a PPM color tuple back to an RGB color. */
414 static int
ppm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])415 ppm_map_color_rgb(gx_device * dev, gx_color_index color,
416 gx_color_value prgb[3])
417 {
418 uint bitspercolor = dev->color_info.depth / 3;
419 uint colormask = (1 << bitspercolor) - 1;
420 uint max_rgb = dev->color_info.max_color;
421
422 prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
423 (ulong) gx_max_color_value / max_rgb;
424 prgb[1] = ((color >> bitspercolor) & colormask) *
425 (ulong) gx_max_color_value / max_rgb;
426 prgb[2] = (color & colormask) *
427 (ulong) gx_max_color_value / max_rgb;
428 return 0;
429 }
430
431 /* Map a CMYK color to a pixel value. */
432 static gx_color_index
pkm_map_cmyk_color(gx_device * pdev,const gx_color_value cv[])433 pkm_map_cmyk_color(gx_device * pdev, const gx_color_value cv[])
434 {
435 uint bpc = pdev->color_info.depth >> 2;
436 uint max_value = pdev->color_info.max_color;
437 uint cc = cv[0] * max_value / gx_max_color_value;
438 uint mc = cv[1] * max_value / gx_max_color_value;
439 uint yc = cv[2] * max_value / gx_max_color_value;
440 uint kc = cv[3] * max_value / gx_max_color_value;
441 gx_color_index color =
442 ((((((gx_color_index)cc << bpc) + mc) << bpc) + yc) << bpc) + kc;
443
444 return (color == gx_no_color_index ? color ^ 1 : color);
445 }
446
447 /* Map a CMYK pixel value to RGB. */
448 static int
pkm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])449 pkm_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3])
450 {
451 int bpc = dev->color_info.depth >> 2;
452 gx_color_index cshift = color;
453 uint mask = (1 << bpc) - 1;
454 uint k = cshift & mask;
455 uint y = (cshift >>= bpc) & mask;
456 uint m = (cshift >>= bpc) & mask;
457 uint c = cshift >> bpc;
458 uint max_value = dev->color_info.max_color;
459 uint not_k = max_value - k;
460
461 #define CVALUE(c)\
462 ((gx_color_value)((ulong)(c) * gx_max_color_value / max_value))
463 /* We use our improved conversion rule.... */
464 rgb[0] = CVALUE((max_value - c) * not_k / max_value);
465 rgb[1] = CVALUE((max_value - m) * not_k / max_value);
466 rgb[2] = CVALUE((max_value - y) * not_k / max_value);
467 #undef CVALUE
468 return 0;
469 }
470
471 /* Augment get/put_params to add UsePlanarBuffer */
472
473 static int
ppm_get_params(gx_device * pdev,gs_param_list * plist)474 ppm_get_params(gx_device * pdev, gs_param_list * plist)
475 {
476 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
477 int code;
478
479 code = gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
480 if (code < 0) return code;
481 code = param_write_null(plist, "OutputIntent");
482 return code;
483 }
484
485 static int
ppm_put_params(gx_device * pdev,gs_param_list * plist)486 ppm_put_params(gx_device * pdev, gs_param_list * plist)
487 {
488 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
489 gx_device_color_info save_info;
490 int ncomps = pdev->color_info.num_components;
491 int bpc = pdev->color_info.depth / ncomps;
492 int ecode = 0;
493 int code;
494 long v;
495 gs_param_string_array intent;
496 const char *vname;
497
498 if ((code = param_read_string_array(plist, "OutputIntent", &intent)) == 0) {
499 /* This device does not use the OutputIntent parameter.
500 We include this code just as a sample how to handle it.
501 The PDF interpreter extracts OutputIntent from a PDF file and sends it to here,
502 if a device includes it in the .getdeviceparams response.
503 This device does include due to ppm_get_params implementation.
504 ppm_put_params must handle it (and ingore it) against 'rangecheck'.
505 */
506 static const bool debug_print_OutputIntent = false;
507
508 if (debug_print_OutputIntent) {
509 int i, j;
510
511 dmlprintf1(pdev->memory, "%d strings:\n", intent.size);
512 for (i = 0; i < intent.size; i++) {
513 const gs_param_string *s = &intent.data[i];
514 dmlprintf2(pdev->memory, " %d: size %d:", i, s->size);
515 if (i < 4) {
516 for (j = 0; j < s->size; j++)
517 dmlprintf1(pdev->memory, "%c", s->data[j]);
518 } else {
519 for (j = 0; j < s->size; j++)
520 dmlprintf1(pdev->memory, " %02x", s->data[j]);
521 }
522 dmlprintf(pdev->memory, "\n");
523 }
524 }
525 }
526 save_info = pdev->color_info;
527 if ((code = param_read_long(plist, (vname = "GrayValues"), &v)) != 1 ||
528 (code = param_read_long(plist, (vname = "RedValues"), &v)) != 1 ||
529 (code = param_read_long(plist, (vname = "GreenValues"), &v)) != 1 ||
530 (code = param_read_long(plist, (vname = "BlueValues"), &v)) != 1
531 ) {
532 if (code < 0)
533 ecode = code;
534 else if (v < 2 || v > (bdev->is_raw || ncomps > 1 ? 256 : 65536L))
535 param_signal_error(plist, vname,
536 ecode = gs_error_rangecheck);
537 else if (v == 2)
538 bpc = 1;
539 else if (v <= 4)
540 bpc = 2;
541 else if (v <= 16)
542 bpc = 4;
543 else if (v <= 32 && ncomps == 3)
544 bpc = 5;
545 else if (v <= 256)
546 bpc = 8;
547 else
548 bpc = 16;
549 if (ecode >= 0) {
550 static const byte depths[4][16] =
551 {
552 {1, 2, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 16},
553 {0},
554 {4, 8, 0, 16, 16, 0, 0, 24},
555 {4, 8, 0, 16, 0, 0, 0, 32},
556 };
557
558 pdev->color_info.depth = depths[ncomps - 1][bpc - 1];
559 pdev->color_info.max_gray = pdev->color_info.max_color =
560 (pdev->color_info.dither_grays =
561 pdev->color_info.dither_colors = (int)v) - 1;
562 }
563 }
564 if ((code = ecode) < 0 ||
565 (code = gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer)) < 0
566 )
567 pdev->color_info = save_info;
568 ppm_set_dev_procs(pdev);
569 return code;
570 }
571
572 /* Copy an alpha map, noting whether we may generate some non-black/white */
573 /* colors through blending. */
574 static int
pnm_copy_alpha(gx_device * pdev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index color,int depth)575 pnm_copy_alpha(gx_device * pdev, const byte * data, int data_x,
576 int raster, gx_bitmap_id id, int x, int y, int width, int height,
577 gx_color_index color, int depth)
578 {
579 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
580
581 if (pdev->color_info.depth < 24 ||
582 (color >> 8) == (color & 0xffff)
583 )
584 bdev->uses_color |= 1;
585 else
586 bdev->uses_color |= 2;
587 return (*bdev->save_copy_alpha) (pdev, data, data_x, raster, id,
588 x, y, width, height, color, depth);
589 }
590
591 /* Begin processing an image, noting whether we may generate some */
592 /* non-black/white colors in the process. */
593 static int
pnm_begin_typed_image(gx_device * dev,const gs_gstate * pgs,const gs_matrix * pmat,const gs_image_common_t * pim,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gx_image_enum_common_t ** pinfo)594 pnm_begin_typed_image(gx_device *dev,
595 const gs_gstate *pgs, const gs_matrix *pmat,
596 const gs_image_common_t *pim, const gs_int_rect *prect,
597 const gx_drawing_color *pdcolor,
598 const gx_clip_path *pcpath,
599 gs_memory_t *memory, gx_image_enum_common_t **pinfo)
600 {
601 gx_device_pbm * const bdev = (gx_device_pbm *)dev;
602 bool has_gray_icc;
603
604 /* Conservatively guesses whether this operation causes color usage
605 that might not be otherwise captured by ppm_map_color_rgb. */
606 if (pim && pim->type) {
607 switch (pim->type->index) {
608 case 1: case 3: case 4: {
609 /* Use colorspace to handle image types 1,3,4 */
610 const gs_pixel_image_t *pim1 = (const gs_pixel_image_t *)pim;
611
612 if (pim1->ColorSpace) {
613 has_gray_icc = false;
614 if (pim1->ColorSpace->cmm_icc_profile_data) {
615 if (pim1->ColorSpace->cmm_icc_profile_data->num_comps == 1) {
616 has_gray_icc = true;
617 }
618 }
619 if (gs_color_space_get_index(pim1->ColorSpace) ==
620 gs_color_space_index_DeviceGray || has_gray_icc) {
621 if (pim1->BitsPerComponent > 1)
622 bdev->uses_color |= 1;
623 } else
624 bdev->uses_color = 2;
625 }
626 break;
627 }
628 default:
629 /* Conservatively handles other image types */
630 bdev->uses_color = 2;
631 }
632 }
633 /* Forward to saved routine */
634 return (*bdev->save_begin_typed_image)(dev, pgs, pmat, pim, prect,
635 pdcolor, pcpath, memory, pinfo);
636 }
637
638 /* ------ Internal routines ------ */
639
640 /* NOP row processing function used when no output */
nop_row_proc(gx_device_printer * pdev,byte * data,int len,gp_file * f)641 static int nop_row_proc(gx_device_printer *pdev, byte *data, int len, gp_file *f)
642 {
643 return 0;
644 }
645
646 /* Print a page using a given row printing routine. */
647 static int
pbm_print_page_loop(gx_device_printer * pdev,char magic,gp_file * pstream,int (* row_proc)(gx_device_printer *,byte *,int,gp_file *))648 pbm_print_page_loop(gx_device_printer * pdev, char magic, gp_file * pstream,
649 int (*row_proc) (gx_device_printer *, byte *, int, gp_file *))
650 {
651 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
652 uint raster = gdev_prn_raster_chunky(pdev);
653 byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
654 int lnum = 0;
655 int code = 0;
656 int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
657 !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
658
659 if (data == 0)
660 return_error(gs_error_VMerror);
661 if (!output_is_nul) {
662 /* Hack. This should be done in the callers. */
663 if (magic == '9') {
664 if (gp_fprintf(pstream, "%11d %11d %11d %11d %11d ",
665 0, 0, 0, pdev->width, pdev->height) < 0) {
666 code = gs_note_error(gs_error_ioerror);
667 goto punt;
668 }
669 } else if (magic == '7') {
670 int ncomps = pdev->color_info.num_components;
671 if (gp_fprintf(pstream, "P%c\n", magic) < 0) {
672 code = gs_note_error(gs_error_ioerror);
673 goto punt;
674 }
675 if (gp_fprintf(pstream, "WIDTH %d\n", pdev->width) < 0) {
676 code = gs_note_error(gs_error_ioerror);
677 goto punt;
678 }
679 if (gp_fprintf(pstream, "HEIGHT %d\n", pdev->height) < 0) {
680 code = gs_note_error(gs_error_ioerror);
681 goto punt;
682 }
683 if (gp_fprintf(pstream, "DEPTH %d\n", ncomps) < 0) {
684 code = gs_note_error(gs_error_ioerror);
685 goto punt;
686 }
687 if (gp_fprintf(pstream, "MAXVAL %d\n", 255) < 0) { /* force MAXVAL to 255 */
688 code = gs_note_error(gs_error_ioerror);
689 goto punt;
690 }
691 if (gp_fprintf(pstream, "TUPLTYPE %s\n",
692 (ncomps == 4) ? "CMYK" :
693 ((ncomps == 3) ? "RGB" : "GRAYSCALE")) < 0) {
694 code = gs_note_error(gs_error_ioerror);
695 goto punt;
696 }
697 if (bdev->comment[0]) {
698 if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
699 code = gs_note_error(gs_error_ioerror);
700 goto punt;
701 }
702 } else {
703 if (gp_fprintf(pstream, "# Image generated by %s\n", gs_product) < 0) {
704 code = gs_note_error(gs_error_ioerror);
705 goto punt;
706 }
707 }
708 if (gp_fprintf(pstream, "ENDHDR\n") < 0) {
709 code = gs_note_error(gs_error_ioerror);
710 goto punt;
711 }
712 } else {
713 if (gp_fprintf(pstream, "P%c\n", magic) < 0) {
714 code = gs_note_error(gs_error_ioerror);
715 goto punt;
716 }
717 if (bdev->comment[0]) {
718 if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
719 code = gs_note_error(gs_error_ioerror);
720 goto punt;
721 }
722 } else {
723 if (gp_fprintf(pstream, "# Image generated by %s (device=%s)\n",
724 gs_product, pdev->dname) < 0) {
725 code = gs_note_error(gs_error_ioerror);
726 goto punt;
727 }
728 }
729 if (gp_fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
730 code = gs_note_error(gs_error_ioerror);
731 goto punt;
732 }
733 }
734 switch (magic) {
735 case '1': /* pbm */
736 case '4': /* pbmraw */
737 case '7': /* pam */
738 case '9': /* plan9bm */
739 break;
740 case '3': /* pkm */
741 case '6': /* pkmraw */
742 if (gp_fprintf(pstream, "%d\n", 255) < 0) {
743 code = gs_note_error(gs_error_ioerror);
744 goto punt;
745 }
746 break;
747 default:
748 if (gp_fprintf(pstream, "%d\n", pdev->color_info.max_gray) < 0) {
749 code = gs_note_error(gs_error_ioerror);
750 goto punt;
751 }
752 }
753 }
754 if (output_is_nul)
755 row_proc = nop_row_proc;
756 for (; lnum < pdev->height; lnum++) {
757 byte *row;
758
759 code = gdev_prn_get_bits(pdev, lnum, data, &row);
760 if (code < 0)
761 break;
762 code = (*row_proc) (pdev, row, pdev->color_info.depth, pstream);
763 if (code < 0)
764 break;
765 }
766 punt:
767 gs_free_object(pdev->memory, data, "pbm_print_page_loop");
768 return (code < 0 ? code : 0);
769 }
770
771 /* ------ Individual page printing routines ------ */
772
773 /* Print a monobit page. */
774 static int
pbm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)775 pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
776 gp_file * pstream)
777 {
778 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
779
780 if (bdev->is_raw) {
781 uint n = (pdev->width + 7) >> 3;
782
783 if (gp_fwrite(data, 1, n, pstream) != n)
784 return_error(gs_error_ioerror);
785 } else {
786 byte *bp;
787 uint x, mask;
788
789 for (bp = data, x = 0, mask = 0x80; x < pdev->width;) {
790 if (gp_fputc((*bp & mask ? '1' : '0'), pstream) == EOF)
791 return_error(gs_error_ioerror);
792 if (++x == pdev->width || !(x & 63)) {
793 if (gp_fputc('\n', pstream) == EOF)
794 return_error(gs_error_ioerror);
795 }
796 if ((mask >>= 1) == 0)
797 bp++, mask = 0x80;
798 }
799 }
800 return 0;
801 }
802 static int
pbm_print_page(gx_device_printer * pdev,gp_file * pstream)803 pbm_print_page(gx_device_printer * pdev, gp_file * pstream)
804 {
805 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
806
807 return pbm_print_page_loop(pdev, bdev->magic, pstream, pbm_print_row);
808 }
809
810 /* Print a gray-mapped page. */
811 static int
pgm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)812 pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
813 gp_file * pstream)
814 { /* Note that bpp <= 8 for raw format, bpp <= 16 for plain. */
815 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
816 uint mask = (1 << depth) - 1;
817 /*
818 * If we're writing planes for a CMYK device, we have 0 = white,
819 * mask = black, which is the opposite of the pgm convention.
820 */
821 uint invert = (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE);
822 byte *bp;
823 uint x;
824 int shift;
825
826 if (bdev->is_raw && depth == 8) {
827 if (invert) {
828 for (bp = data, x = 0; x < pdev->width; bp++, x++) {
829 if (gp_fputc((byte)~*bp, pstream) == EOF)
830 return_error(gs_error_ioerror);
831 }
832 } else {
833 if (gp_fwrite(data, 1, pdev->width, pstream) != pdev->width)
834 return_error(gs_error_ioerror);
835 }
836 } else
837 for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
838 uint pixel;
839
840 if (shift < 0) { /* bpp = 16 */
841 pixel = ((uint) * bp << 8) + bp[1];
842 bp += 2;
843 } else {
844 pixel = (*bp >> shift) & mask;
845 if ((shift -= depth) < 0)
846 bp++, shift += 8;
847 }
848 ++x;
849 pixel ^= invert;
850 if (bdev->is_raw) {
851 if (gp_fputc(pixel, pstream) == EOF)
852 return_error(gs_error_ioerror);
853 } else {
854 if (gp_fprintf(pstream, "%d%c", pixel,
855 (x == pdev->width || !(x & 15) ? '\n' : ' ')) < 0)
856 return_error(gs_error_ioerror);
857 }
858 }
859 return 0;
860 }
861 static int
pxm_pbm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)862 pxm_pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
863 gp_file * pstream)
864 { /* Compress a PGM or PPM row to a PBM row. */
865 /* This doesn't have to be very fast. */
866 /* Note that we have to invert the data as well. */
867 int delta = (depth + 7) >> 3;
868 byte *src = data + delta - 1; /* always big-endian */
869 byte *dest = data;
870 int x;
871 byte out_mask = 0x80;
872 byte out = 0;
873
874 if (depth >= 8) { /* One or more bytes per source pixel. */
875 for (x = 0; x < pdev->width; x++, src += delta) {
876 if (!(*src & 1))
877 out |= out_mask;
878 out_mask >>= 1;
879 if (!out_mask)
880 out_mask = 0x80,
881 *dest++ = out,
882 out = 0;
883 }
884 } else { /* Multiple source pixels per byte. */
885 byte in_mask = 0x100 >> depth;
886
887 for (x = 0; x < pdev->width; x++) {
888 if (!(*src & in_mask))
889 out |= out_mask;
890 in_mask >>= depth;
891 if (!in_mask)
892 in_mask = 0x100 >> depth,
893 src++;
894 out_mask >>= 1;
895 if (!out_mask)
896 out_mask = 0x80,
897 *dest++ = out,
898 out = 0;
899 }
900 }
901 if (out_mask != 0x80)
902 *dest = out;
903 return pbm_print_row(pdev, data, 1, pstream);
904 }
905 static int
pgm_print_page(gx_device_printer * pdev,gp_file * pstream)906 pgm_print_page(gx_device_printer * pdev, gp_file * pstream)
907 {
908 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
909
910 return (bdev->uses_color == 0 && bdev->optimize ?
911 pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
912 pxm_pbm_print_row) :
913 pbm_print_page_loop(pdev, bdev->magic, pstream,
914 pgm_print_row));
915 }
916
917 /* Print a color-mapped page. */
918 static int
ppgm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream,bool color)919 ppgm_print_row(gx_device_printer * pdev, byte * data, int depth,
920 gp_file * pstream, bool color)
921 { /* If color=false, write only one value per pixel; */
922 /* if color=true, write 3 values per pixel. */
923 /* Note that depth <= 24 for raw format, depth <= 32 for plain. */
924 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
925 uint bpe = depth / 3; /* bits per r/g/b element */
926 uint mask = (1 << bpe) - 1;
927 byte *bp;
928 uint x;
929 uint eol_mask = (color ? 7 : 15);
930 int shift;
931
932 if (bdev->is_raw && depth == 24 && color) {
933 uint n = pdev->width * (depth / 8);
934
935 if (gp_fwrite(data, 1, n, pstream) != n)
936 return_error(gs_error_ioerror);
937 } else {
938 for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
939 bits32 pixel = 0;
940 uint r, g, b;
941
942 switch (depth >> 3) {
943 case 4:
944 pixel = (bits32) * bp << 24;
945 bp++;
946 /* falls through */
947 case 3:
948 pixel += (bits32) * bp << 16;
949 bp++;
950 /* falls through */
951 case 2:
952 pixel += (uint) * bp << 8;
953 bp++;
954 /* falls through */
955 case 1:
956 pixel += *bp;
957 bp++;
958 break;
959 case 0: /* bpp == 4, bpe == 1 */
960 pixel = *bp >> shift;
961 if ((shift -= depth) < 0)
962 bp++, shift += 8;
963 break;
964 }
965 ++x;
966 b = pixel & mask;
967 pixel >>= bpe;
968 g = pixel & mask;
969 pixel >>= bpe;
970 r = pixel & mask;
971 if (bdev->is_raw) {
972 if (color) {
973 if (gp_fputc(r, pstream) == EOF)
974 return_error(gs_error_ioerror);
975 if (gp_fputc(g, pstream) == EOF)
976 return_error(gs_error_ioerror);
977 }
978 if (gp_fputc(b, pstream) == EOF)
979 return_error(gs_error_ioerror);
980 } else {
981 if (color) {
982 if (gp_fprintf(pstream, "%d %d ", r, g) < 0)
983 return_error(gs_error_ioerror);
984 }
985 if (gp_fprintf(pstream, "%d%c", b,
986 (x == pdev->width || !(x & eol_mask) ?
987 '\n' : ' ')) < 0)
988 return_error(gs_error_ioerror);
989 }
990 }
991 }
992 return 0;
993 }
994 static int
ppm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)995 ppm_print_row(gx_device_printer * pdev, byte * data, int depth,
996 gp_file * pstream)
997 {
998 return ppgm_print_row(pdev, data, depth, pstream, true);
999 }
1000 static int
ppm_pgm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)1001 ppm_pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
1002 gp_file * pstream)
1003 {
1004 return ppgm_print_row(pdev, data, depth, pstream, false);
1005 }
1006 static int
ppm_print_page(gx_device_printer * pdev,gp_file * pstream)1007 ppm_print_page(gx_device_printer * pdev, gp_file * pstream)
1008 {
1009 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1010
1011 return (bdev->uses_color >= 2 || !bdev->optimize ?
1012 pbm_print_page_loop(pdev, bdev->magic, pstream,
1013 ppm_print_row) :
1014 bdev->uses_color == 1 ?
1015 pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
1016 ppm_pgm_print_row) :
1017 pbm_print_page_loop(pdev, (char)((int)bdev->magic - 2), pstream,
1018 pxm_pbm_print_row));
1019 }
1020
1021 static int
pam_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)1022 pam_print_row(gx_device_printer * pdev, byte * data, int depth,
1023 gp_file * pstream)
1024 {
1025 if (depth == 32) {
1026 uint n = pdev->width * (depth / 8);
1027
1028 if (gp_fwrite(data, 1, n, pstream) != n)
1029 return_error(gs_error_ioerror);
1030 }
1031 return 0;
1032 }
1033
1034 static int
pam_print_page(gx_device_printer * pdev,gp_file * pstream)1035 pam_print_page(gx_device_printer * pdev, gp_file * pstream)
1036 {
1037 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1038
1039 return pbm_print_page_loop(pdev, bdev->magic, pstream,
1040 pam_print_row);
1041 }
1042
1043 static int
pnmcmyk_print_page(gx_device_printer * pdev,gp_file * pstream)1044 pnmcmyk_print_page(gx_device_printer *pdev, gp_file *pstream)
1045 {
1046 if (pdev->icc_struct->graydetection == true && pdev->icc_struct->pageneutralcolor == true) {
1047 /* Here we need to convert the data from CMYK to K (gray) then print */
1048 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1049 uint raster = gdev_prn_raster_chunky(pdev); /* enough space for the CMYK data */
1050 byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
1051 int lnum = 0;
1052 int code = 0;
1053 int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
1054 !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
1055 int (*row_proc) (gx_device_printer *, byte *, int, gp_file *);
1056
1057 if (data == NULL)
1058 return_error(gs_error_VMerror);
1059 if (!output_is_nul) {
1060 if (gp_fprintf(pstream, "P5\n") < 0) { /* PGM raw */
1061 code = gs_note_error(gs_error_ioerror);
1062 goto punt;
1063 }
1064 if (bdev->comment[0]) {
1065 if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
1066 code = gs_note_error(gs_error_ioerror);
1067 goto punt;
1068 }
1069 } else {
1070 if (gp_fprintf(pstream, "# Image generated by %s (device=%s)\n",
1071 gs_product, pdev->dname) < 0) {
1072 code = gs_note_error(gs_error_ioerror);
1073 goto punt;
1074 }
1075 }
1076 if (gp_fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
1077 code = gs_note_error(gs_error_ioerror);
1078 goto punt;
1079 }
1080 if (gp_fprintf(pstream, "255\n") < 0) {
1081 code = gs_note_error(gs_error_ioerror);
1082 goto punt;
1083 }
1084 row_proc = pgm_print_row;
1085 } else
1086 row_proc = nop_row_proc;
1087
1088
1089 for (; lnum < pdev->height; lnum++) {
1090 byte *row, *row_end;
1091 byte *pcmyk, *pgray; /* scan pointers through the row */
1092
1093 code = gdev_prn_get_bits(pdev, lnum, data, &row);
1094 if (code < 0)
1095 break;
1096 /* convert the CMYK to Gray */
1097 pgray = row; /* destination for converted color */
1098 row_end = row + (4 * pdev->width);
1099 for (pcmyk = row; pcmyk < row_end;) {
1100 int32_t cmy;
1101 byte k;
1102
1103 /* For now we assume that the CMYK may have gone through an ICC profile */
1104 /* so we do a more complex conversion from CMYK to K. If we are using */
1105 /* FastColor, we may be able to do this more efficiently. */
1106 cmy = ((255 - *pcmyk++) * lum_red_weight);
1107 cmy += ((255 - *pcmyk++) * lum_green_weight);
1108 cmy += ((255 - *pcmyk++) * lum_blue_weight);
1109 cmy += (lum_all_weights / 2);
1110 cmy /= lum_all_weights;
1111
1112 k = *pcmyk++;
1113 if (k > cmy)
1114 k = 0; /* additive black */
1115 else
1116 k = cmy - k; /* additive gray */
1117 *pgray++ = k; /* store it */
1118 }
1119 /* we converted to normal "additive" gray (white == 1) so set */
1120 /* the color_info.polarity so that pgm_print_row doesn't invert */
1121 pdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
1122 code = (*row_proc) (pdev, row, 8, pstream);
1123 pdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE; /* restore actual polarity */
1124 if (code < 0)
1125 break;
1126 }
1127 punt:
1128 gs_free_object(pdev->memory, data, "pbm_print_page_loop");
1129 return (code < 0 ? code : 0);
1130 }
1131 /* otherwise, just spit out the CMYK 32-bit PAM format */
1132 return pam_print_page(pdev, pstream);
1133 }
1134
1135 static int
pam4_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)1136 pam4_print_row(gx_device_printer * pdev, byte * data, int depth,
1137 gp_file * pstream)
1138 {
1139 int w, s;
1140 if (depth == 4) {
1141 for (w = pdev->width; w > 0;) {
1142 byte C = *data++;
1143 for (s = 7; s >= 0; s -= 4)
1144 {
1145 gp_fputc(((C>>s )&1)*0xff, pstream);
1146 gp_fputc(((C>>(s-1))&1)*0xff, pstream);
1147 gp_fputc(((C>>(s-2))&1)*0xff, pstream);
1148 gp_fputc(((C>>(s-3))&1)*0xff, pstream);
1149 w--;
1150 if (w == 0)
1151 break;
1152 }
1153 }
1154 }
1155 return 0;
1156 }
1157
1158 static int
pam4_print_page(gx_device_printer * pdev,gp_file * pstream)1159 pam4_print_page(gx_device_printer * pdev, gp_file * pstream)
1160 {
1161 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1162
1163 return pbm_print_page_loop(pdev, bdev->magic, pstream,
1164 pam4_print_row);
1165 }
1166
1167 /* Print a faux CMYK page. */
1168 /* Print a row where each pixel occupies 4 bits (depth == 4). */
1169 /* In this case, we also know pdev->color_info.max_color == 1. */
1170 static int
pkm_print_row_4(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)1171 pkm_print_row_4(gx_device_printer * pdev, byte * data, int depth,
1172 gp_file * pstream)
1173 {
1174 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1175 byte *bp;
1176 uint x;
1177 byte rv[16], gv[16], bv[16], i;
1178
1179 /* Precompute all the possible pixel values. */
1180 for (i = 0; i < 16; ++i) {
1181 gx_color_value rgb[3];
1182
1183 cmyk_1bit_map_color_rgb((gx_device *)pdev, (gx_color_index)i, rgb);
1184 rv[i] = rgb[0] / gx_max_color_value * 0xff;
1185 gv[i] = rgb[1] / gx_max_color_value * 0xff;
1186 bv[i] = rgb[2] / gx_max_color_value * 0xff;
1187 }
1188 /*
1189 * Contrary to what the documentation implies, gcc compiles putc
1190 * as a procedure call. This is ridiculous, but since we can't
1191 * change it, we buffer groups of pixels ourselves and use fwrite.
1192 */
1193 if (bdev->is_raw) {
1194 #ifdef PACIFY_VALGRIND
1195 if ((pdev->width & 1) != 0) {
1196 data[pdev->width>>1] &= 0xf0;
1197 }
1198 #endif
1199 for (bp = data, x = 0; x < pdev->width;) {
1200 byte raw[50 * 3]; /* 50 is arbitrary, but must be even */
1201 int end = min(x + sizeof(raw) / 3, pdev->width);
1202 byte *outp = raw;
1203
1204 for (; x < end; bp++, outp += 6, x += 2) {
1205 uint b = *bp;
1206 uint pixel = b >> 4;
1207
1208 outp[0] = rv[pixel], outp[1] = gv[pixel], outp[2] = bv[pixel];
1209 pixel = b & 0xf;
1210 outp[3] = rv[pixel], outp[4] = gv[pixel], outp[5] = bv[pixel];
1211 }
1212 /* x might overshoot the width by 1 pixel. */
1213 if (x > end)
1214 outp -= 3;
1215 if (gp_fwrite(raw, 1, outp - raw, pstream) != outp - raw)
1216 return_error(gs_error_ioerror);
1217 }
1218 } else {
1219 int shift;
1220
1221 for (bp = data, x = 0, shift = 4; x < pdev->width;) {
1222 int pixel = (*bp >> shift) & 0xf;
1223
1224 shift ^= 4;
1225 bp += shift >> 2;
1226 ++x;
1227 if (gp_fprintf(pstream, "%d %d %d%c", rv[pixel], gv[pixel], bv[pixel],
1228 (x == pdev->width || !(x & 7) ?
1229 '\n' : ' ')) < 0)
1230 return_error(gs_error_ioerror);
1231 }
1232 }
1233 return 0;
1234 }
1235 /* Print a row where each pixel occupies 1 or more bytes (depth >= 8). */
1236 /* Note that the output is scaled up to 255 max value. */
1237 static int
pkm_print_row(gx_device_printer * pdev,byte * data,int depth,gp_file * pstream)1238 pkm_print_row(gx_device_printer * pdev, byte * data, int depth,
1239 gp_file * pstream)
1240 {
1241 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1242 byte *bp;
1243 uint x;
1244
1245 for (bp = data, x = 0; x < pdev->width;) {
1246 bits32 pixel = 0;
1247 gx_color_value rgb[3];
1248 uint r, g, b;
1249
1250 switch (depth >> 3) {
1251 case 4:
1252 pixel = (bits32) * bp << 24;
1253 bp++;
1254 /* falls through */
1255 case 3:
1256 pixel += (bits32) * bp << 16;
1257 bp++;
1258 /* falls through */
1259 case 2:
1260 pixel += (uint) * bp << 8;
1261 bp++;
1262 /* falls through */
1263 case 1:
1264 pixel += *bp;
1265 bp++;
1266 }
1267 ++x;
1268 pkm_map_color_rgb((gx_device *) pdev, pixel, rgb);
1269 r = rgb[0] * 0xff / gx_max_color_value;
1270 g = rgb[1] * 0xff / gx_max_color_value;
1271 b = rgb[2] * 0xff / gx_max_color_value;
1272 if (bdev->is_raw) {
1273 if (gp_fputc(r, pstream) == EOF)
1274 return_error(gs_error_ioerror);
1275 if (gp_fputc(g, pstream) == EOF)
1276 return_error(gs_error_ioerror);
1277 if (gp_fputc(b, pstream) == EOF)
1278 return_error(gs_error_ioerror);
1279 } else {
1280 if (gp_fprintf(pstream, "%d %d %d%c", r, g, b,
1281 (x == pdev->width || !(x & 7) ?
1282 '\n' : ' ')) < 0)
1283 return_error(gs_error_ioerror);
1284 }
1285 }
1286 return 0;
1287 }
1288 static int
pkm_print_page(gx_device_printer * pdev,gp_file * pstream)1289 pkm_print_page(gx_device_printer * pdev, gp_file * pstream)
1290 {
1291 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1292
1293 return pbm_print_page_loop(pdev, bdev->magic, pstream,
1294 (pdev->color_info.depth < 8 ?
1295 pkm_print_row_4 :
1296 pkm_print_row));
1297 }
1298
1299 /* Print individual separations on a single file. */
1300 static int
psm_print_page(gx_device_printer * pdev,gp_file * pstream)1301 psm_print_page(gx_device_printer * pdev, gp_file * pstream)
1302 {
1303 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
1304 /*
1305 * Allocate a large enough buffer for full pixels, on the theory that we
1306 * don't know how many bits will be allocated to each component. (This
1307 * is for didactic purposes only: we know perfectly well that each
1308 * component will have 1/N of the bits.)
1309 */
1310 uint max_raster = bitmap_raster(pdev->width * pdev->color_info.depth);
1311 byte *data = gs_alloc_bytes(pdev->memory, max_raster, "pksm_print_page");
1312 int code = 0;
1313 unsigned char plane;
1314
1315 if (data == 0)
1316 return_error(gs_error_VMerror);
1317 for (plane = 0; plane < pdev->color_info.num_components; ++plane) {
1318 int lnum, band_end;
1319 /*
1320 * The following initialization is unnecessary: lnum == band_end on
1321 * the first pass through the loop below, so marked will always be
1322 * set before it is used. We initialize marked solely to suppress
1323 * bogus warning messages from certain compilers.
1324 */
1325 gx_color_index marked = 0;
1326 gx_render_plane_t render_plane;
1327 int plane_depth;
1328 int plane_shift;
1329 gx_color_index plane_mask;
1330 int raster;
1331
1332 gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
1333 plane_depth = render_plane.depth;
1334 plane_shift = render_plane.shift;
1335 plane_mask = ((gx_color_index)1 << plane_depth) - 1;
1336 raster = bitmap_raster(pdev->width * plane_depth);
1337 if (gp_fprintf(pstream, "P%c\n", bdev->magic + (plane_depth > 1)) < 0) {
1338 code = gs_note_error(gs_error_ioerror);
1339 goto punt;
1340 }
1341 if (bdev->comment[0]) {
1342 if (gp_fprintf(pstream, "# %s\n", bdev->comment) < 0) {
1343 code = gs_note_error(gs_error_ioerror);
1344 goto punt;
1345 }
1346 } else {
1347 if (gp_fprintf(pstream, "# Image generated by %s (device=%s)\n",
1348 gs_product, pdev->dname) < 0) {
1349 code = gs_note_error(gs_error_ioerror);
1350 goto punt;
1351 }
1352 }
1353 if (gp_fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
1354 code = gs_note_error(gs_error_ioerror);
1355 goto punt;
1356 }
1357 if (plane_depth > 1) {
1358 if (gp_fprintf(pstream, "%d\n", pdev->color_info.max_gray) < 0) {
1359 code = gs_note_error(gs_error_ioerror);
1360 goto punt;
1361 }
1362 }
1363 for (lnum = band_end = 0; lnum < pdev->height; lnum++) {
1364 byte *row;
1365
1366 if (lnum == band_end) {
1367 gx_color_usage_t color_usage;
1368 int band_start;
1369 int band_height =
1370 gdev_prn_color_usage((gx_device *)pdev, lnum, 1,
1371 &color_usage, &band_start);
1372
1373 band_end = band_start + band_height;
1374 marked = color_usage.or & (plane_mask << plane_shift);
1375 if (!marked)
1376 memset(data, 0, raster);
1377 #ifdef DEBUG
1378 if (plane == 0)
1379 if_debug4m(':', pdev->memory,
1380 "[:]%4d - %4d mask = 0x%lx, slow_rop = %d\n",
1381 lnum, band_end - 1, (ulong)color_usage.or,
1382 color_usage.slow_rop);
1383 #endif
1384 }
1385 if (marked) {
1386 gx_render_plane_t render_plane;
1387 uint actual_raster;
1388
1389 render_plane.index = plane;
1390 code = gdev_prn_get_lines(pdev, lnum, 1, data, raster,
1391 &row, &actual_raster,
1392 &render_plane);
1393 if (code < 0)
1394 break;
1395 } else
1396 row = data;
1397 code =
1398 (plane_depth == 1 ?
1399 pbm_print_row(pdev, row, plane_depth, pstream) :
1400 pgm_print_row(pdev, row, plane_depth, pstream));
1401 if (code < 0)
1402 break;
1403 }
1404 }
1405 punt:
1406 gs_free_object(pdev->memory, data, "pksm_print_page");
1407 return (code < 0 ? code : 0);
1408 }
1409