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