1 /* Copyright (C) 1992, 2000 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gdevpbm.c,v 1.11 2004/05/26 04:10:58 dan Exp $ */
18 /* Portable Bit/Gray/PixMap drivers */
19 #include "gdevprn.h"
20 #include "gscdefs.h"
21 #include "gscspace.h" /* For pnm_begin_typed_image(..) */
22 #include "gxgetbit.h"
23 #include "gxlum.h"
24 #include "gxiparam.h" /* For pnm_begin_typed_image(..) */
25 #include "gdevmpla.h"
26 #include "gdevplnx.h"
27 #include "gdevppla.h"
28
29 /*
30 * Thanks are due to Jos Vos (jos@bull.nl) for an earlier P*M driver,
31 * on which this one is based; to Nigel Roles (ngr@cotswold.demon.co.uk),
32 * for the plan9bm changes; and to Leon Bottou (leonb@research.att.com)
33 * for the color detection code in pnm_begin_typed_image.
34 */
35
36 /*
37 * There are 8 (families of) drivers here, plus one less related one:
38 * pbm[raw] - outputs PBM (black and white).
39 * pgm[raw] - outputs PGM (gray-scale).
40 * pgnm[raw] - outputs PBM if the page contains only black and white,
41 * otherwise PGM.
42 * ppm[raw] - outputs PPM (RGB).
43 * pnm[raw] - outputs PBM if the page contains only black and white,
44 * otherwise PGM if the page contains only gray shades,
45 * otherwise PPM.
46 * pkm[raw] - computes internally in CMYK, outputs PPM (RGB).
47 * pksm[raw] - computes internally in CMYK, outputs 4 PBM pages.
48 * pam - outputs CMYK as PAM
49 * plan9bm - outputs Plan 9 bitmap format.
50 */
51
52 /*
53 * The code here is designed to work with variable depths for PGM and PPM.
54 * The code will work with any of the values in brackets, but the
55 * Ghostscript imager requires that depth be a power of 2 or be 24,
56 * so the actual allowed values are more limited.
57 * pgm, pgnm: 1, 2, 4, 8, 16. [1-16]
58 * pgmraw, pgnmraw: 1, 2, 4, 8. [1-8]
59 * ppm, pnm: 4(3x1), 8(3x2), 16(3x5), 24(3x8), 32(3x10). [3-32]
60 * ppmraw, pnmraw: 4(3x1), 8(3x2), 16(3x5), 24(3x8). [3-24]
61 * pkm, pkmraw: 4(4x1), 8(4x2), 16(4x4), 32(4x8). [4-32]
62 * pksm, pksmraw: ibid.
63 * pam: 32 (CMYK)
64 */
65
66 /* Structure for P*M devices, which extend the generic printer device. */
67
68 #define MAX_COMMENT 70 /* max user-supplied comment */
69 struct gx_device_pbm_s {
70 gx_device_common;
71 gx_prn_device_common;
72 /* Additional state for P*M devices */
73 char magic; /* n for "Pn" */
74 char comment[MAX_COMMENT + 1]; /* comment for head of file */
75 byte is_raw; /* 1 if raw format, 0 if plain */
76 byte optimize; /* 1 if optimization OK, 0 if not */
77 byte uses_color; /* 0 if image is black and white, */
78 /* 1 if gray (PGM or PPM only), */
79 /* 2 or 3 if colored (PPM only) */
80 bool UsePlanarBuffer; /* 0 if chunky buffer, 1 if planar */
81 dev_proc_copy_alpha((*save_copy_alpha));
82 dev_proc_begin_typed_image((*save_begin_typed_image));
83 };
84 typedef struct gx_device_pbm_s gx_device_pbm;
85
86 /* ------ The device descriptors ------ */
87
88 /*
89 * Default X and Y resolution.
90 */
91 #define X_DPI 72
92 #define Y_DPI 72
93
94 /* Macro for generating P*M device descriptors. */
95 #define pbm_prn_device(procs, dev_name, magic, is_raw, num_comp, depth, max_gray, max_rgb, optimize, x_dpi, y_dpi, print_page)\
96 { prn_device_body(gx_device_pbm, procs, dev_name,\
97 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi,\
98 0, 0, 0, 0,\
99 num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
100 print_page),\
101 magic,\
102 { 0 },\
103 is_raw,\
104 optimize,\
105 0, 0, 0\
106 }
107
108 /* For all but PBM, we need our own color mapping and alpha procedures. */
109 private dev_proc_map_rgb_color(pgm_map_rgb_color);
110 private dev_proc_map_rgb_color(ppm_map_rgb_color);
111 private dev_proc_map_color_rgb(pgm_map_color_rgb);
112 private dev_proc_map_color_rgb(ppm_map_color_rgb);
113 private dev_proc_map_cmyk_color(pkm_map_cmyk_color);
114 private dev_proc_map_color_rgb(pkm_map_color_rgb);
115 private dev_proc_get_params(ppm_get_params);
116 private dev_proc_put_params(ppm_put_params);
117 private dev_proc_copy_alpha(pnm_copy_alpha);
118 private dev_proc_begin_typed_image(pnm_begin_typed_image);
119
120 /* We need to initialize uses_color when opening the device, */
121 /* and after each showpage. */
122 private dev_proc_open_device(ppm_open);
123 private dev_proc_output_page(ppm_output_page);
124
125 /* And of course we need our own print-page routines. */
126 private dev_proc_print_page(pbm_print_page);
127 private dev_proc_print_page(pgm_print_page);
128 private dev_proc_print_page(ppm_print_page);
129 private dev_proc_print_page(pkm_print_page);
130 private dev_proc_print_page(psm_print_page);
131 private dev_proc_print_page(psm_print_page);
132 private dev_proc_print_page(pam_print_page);
133
134 private int pam_print_row(gx_device_printer * pdev, byte * data, int depth,
135 FILE * pstream);
136 private int pam_print_page(gx_device_printer * pdev, FILE * pstream);
137
138 /* The device procedures */
139
140 /* See gdevprn.h for the template for the following. */
141 #define pgpm_procs(p_open, p_get_params, p_map_rgb_color, p_map_color_rgb, p_map_cmyk_color) {\
142 p_open, NULL, NULL, ppm_output_page, gdev_prn_close,\
143 p_map_rgb_color, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
144 p_get_params, ppm_put_params,\
145 p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device\
146 }
147
148 private const gx_device_procs pbm_procs =
149 pgpm_procs(gdev_prn_open, gdev_prn_get_params,
150 gdev_prn_map_rgb_color, gdev_prn_map_color_rgb, NULL);
151 private const gx_device_procs pgm_procs =
152 pgpm_procs(ppm_open, gdev_prn_get_params,
153 pgm_map_rgb_color, pgm_map_color_rgb, NULL);
154 private const gx_device_procs ppm_procs =
155 pgpm_procs(ppm_open, ppm_get_params,
156 gx_default_rgb_map_rgb_color, ppm_map_color_rgb, NULL);
157 private const gx_device_procs pnm_procs =
158 pgpm_procs(ppm_open, ppm_get_params,
159 ppm_map_rgb_color, ppm_map_color_rgb, NULL);
160 private const gx_device_procs pkm_procs =
161 pgpm_procs(ppm_open, ppm_get_params,
162 NULL, cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color);
163 private const gx_device_procs pam_procs =
164 pgpm_procs(ppm_open, ppm_get_params,
165 NULL, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color);
166
167 /* The device descriptors themselves */
168 const gx_device_pbm gs_pbm_device =
169 pbm_prn_device(pbm_procs, "pbm", '1', 0, 1, 1, 1, 0, 0,
170 X_DPI, Y_DPI, pbm_print_page);
171 const gx_device_pbm gs_pbmraw_device =
172 pbm_prn_device(pbm_procs, "pbmraw", '4', 1, 1, 1, 1, 1, 0,
173 X_DPI, Y_DPI, pbm_print_page);
174 const gx_device_pbm gs_pgm_device =
175 pbm_prn_device(pgm_procs, "pgm", '2', 0, 1, 8, 255, 0, 0,
176 X_DPI, Y_DPI, pgm_print_page);
177 const gx_device_pbm gs_pgmraw_device =
178 pbm_prn_device(pgm_procs, "pgmraw", '5', 1, 1, 8, 255, 0, 0,
179 X_DPI, Y_DPI, pgm_print_page);
180 const gx_device_pbm gs_pgnm_device =
181 pbm_prn_device(pgm_procs, "pgnm", '2', 0, 1, 8, 255, 0, 1,
182 X_DPI, Y_DPI, pgm_print_page);
183 const gx_device_pbm gs_pgnmraw_device =
184 pbm_prn_device(pgm_procs, "pgnmraw", '5', 1, 1, 8, 255, 0, 1,
185 X_DPI, Y_DPI, pgm_print_page);
186 const gx_device_pbm gs_ppm_device =
187 pbm_prn_device(ppm_procs, "ppm", '3', 0, 3, 24, 255, 255, 0,
188 X_DPI, Y_DPI, ppm_print_page);
189 const gx_device_pbm gs_ppmraw_device =
190 pbm_prn_device(ppm_procs, "ppmraw", '6', 1, 3, 24, 255, 255, 0,
191 X_DPI, Y_DPI, ppm_print_page);
192 const gx_device_pbm gs_pnm_device =
193 pbm_prn_device(pnm_procs, "pnm", '3', 0, 3, 24, 255, 255, 1,
194 X_DPI, Y_DPI, ppm_print_page);
195 const gx_device_pbm gs_pnmraw_device =
196 pbm_prn_device(pnm_procs, "pnmraw", '6', 1, 3, 24, 255, 255, 1,
197 X_DPI, Y_DPI, ppm_print_page);
198 const gx_device_pbm gs_pkm_device =
199 pbm_prn_device(pkm_procs, "pkm", '3', 0, 4, 4, 1, 1, 0,
200 X_DPI, Y_DPI, pkm_print_page);
201 const gx_device_pbm gs_pkmraw_device =
202 pbm_prn_device(pkm_procs, "pkmraw", '6', 1, 4, 4, 1, 1, 0,
203 X_DPI, Y_DPI, pkm_print_page);
204 const gx_device_pbm gs_pksm_device =
205 pbm_prn_device(pkm_procs, "pksm", '1', 0, 4, 4, 1, 1, 0,
206 X_DPI, Y_DPI, psm_print_page);
207 const gx_device_pbm gs_pksmraw_device =
208 pbm_prn_device(pkm_procs, "pksmraw", '4', 1, 4, 4, 1, 1, 0,
209 X_DPI, Y_DPI, psm_print_page);
210 const gx_device_pbm gs_pam_device =
211 pbm_prn_device(pam_procs, "pam", '7', 1, 4, 32, 255, 255, 0,
212 X_DPI, Y_DPI, pam_print_page);
213
214 /* Plan 9 bitmaps default to 100 dpi. */
215 const gx_device_pbm gs_plan9bm_device =
216 pbm_prn_device(pbm_procs, "plan9bm", '9', 1, 1, 1, 1, 1, 1,
217 100, 100, pbm_print_page);
218
219 /* ------ Initialization ------ */
220
221 /* Set the copy_alpha and color mapping procedures if necessary. */
222 private void
ppm_set_dev_procs(gx_device * pdev)223 ppm_set_dev_procs(gx_device * pdev)
224 {
225 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
226
227 if (dev_proc(pdev, copy_alpha) != pnm_copy_alpha) {
228 bdev->save_copy_alpha = dev_proc(pdev, copy_alpha);
229 if (pdev->color_info.depth > 4)
230 set_dev_proc(pdev, copy_alpha, pnm_copy_alpha);
231 }
232 if (dev_proc(pdev, begin_typed_image) != pnm_begin_typed_image) {
233 bdev->save_begin_typed_image = dev_proc(pdev, begin_typed_image);
234 set_dev_proc(pdev, begin_typed_image, pnm_begin_typed_image);
235 }
236 if (bdev->color_info.num_components == 4) {
237 if (bdev->magic == 7) {
238 set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb);
239 set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
240 } else if (bdev->color_info.depth == 4) {
241 set_dev_proc(pdev, map_color_rgb, cmyk_1bit_map_color_rgb);
242 set_dev_proc(pdev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
243 } else {
244 set_dev_proc(pdev, map_color_rgb, pkm_map_color_rgb);
245 set_dev_proc(pdev, map_cmyk_color, pkm_map_cmyk_color);
246 }
247 }
248 }
249
250 /*
251 * Define a special open procedure that changes create_buf_device to use
252 * a planar device.
253 */
254
255 private int
ppm_open(gx_device * pdev)256 ppm_open(gx_device * pdev)
257 {
258 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
259 int code = gdev_prn_open_planar(pdev, bdev->UsePlanarBuffer);
260
261 if (code < 0)
262 return code;
263 pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
264 set_linear_color_bits_mask_shift(pdev);
265 bdev->uses_color = 0;
266 ppm_set_dev_procs(pdev);
267 return code;
268 }
269
270 /* Print a page, and reset uses_color if this is a showpage. */
271 private int
ppm_output_page(gx_device * pdev,int num_copies,int flush)272 ppm_output_page(gx_device * pdev, int num_copies, int flush)
273 {
274 int code = gdev_prn_output_page(pdev, num_copies, flush);
275 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
276
277 if (code < 0)
278 return code;
279 if (flush)
280 bdev->uses_color = 0;
281 return code;
282 }
283
284 /* ------ Color mapping routines ------ */
285
286 /* Map an RGB color to a PGM gray value. */
287 /* Keep track of whether the image is black-and-white or gray. */
288 private gx_color_index
pgm_map_rgb_color(gx_device * pdev,const gx_color_value cv[])289 pgm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
290 { /* We round the value rather than truncating it. */
291 gx_color_value gray;
292 /* TO_DO_DEVICEN - Kludge to emulate pre DeviceN math errors */
293 #if 1
294 gx_color_value r, g, b;
295
296 r = cv[0]; g = cv[0]; b = cv[0];
297 gray = ((r * (ulong) lum_red_weight) +
298 (g * (ulong) lum_green_weight) +
299 (b * (ulong) lum_blue_weight) +
300 (lum_all_weights / 2)) / lum_all_weights
301 * pdev->color_info.max_gray / gx_max_color_value;
302 #else /* Should be ... */
303 gray = cv[0] * pdev->color_info.max_gray / gx_max_color_value;
304 #endif
305
306 if (!(gray == 0 || gray == pdev->color_info.max_gray)) {
307 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
308
309 bdev->uses_color = 1;
310 }
311 return gray;
312 }
313
314 /* Map a PGM gray value back to an RGB color. */
315 private int
pgm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])316 pgm_map_color_rgb(gx_device * dev, gx_color_index color,
317 gx_color_value prgb[3])
318 {
319 gx_color_value gray =
320 color * gx_max_color_value / dev->color_info.max_gray;
321
322 prgb[0] = gray;
323 prgb[1] = gray;
324 prgb[2] = gray;
325 return 0;
326 }
327
328 /*
329 * Pre gs8.00 version of RGB mapping for 24-bit true (RGB) color devices
330 * It is kept here for backwards comparibility since the gs8.00 version
331 * has changed in functionality. The new one requires that the device be
332 * 'separable'. This routine is logically separable but does not require
333 * the various color_info fields associated with separability (comp_shift,
334 * comp_bits, and comp_mask) be setup.
335 */
336
337 private 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)338 gx_old_default_rgb_map_rgb_color(gx_device * dev,
339 gx_color_value r, gx_color_value g, gx_color_value b)
340 {
341 if (dev->color_info.depth == 24)
342 return gx_color_value_to_byte(b) +
343 ((uint) gx_color_value_to_byte(g) << 8) +
344 ((ulong) gx_color_value_to_byte(r) << 16);
345 else {
346 int bpc = dev->color_info.depth / 3;
347 int drop = sizeof(gx_color_value) * 8 - bpc;
348
349 return ((((r >> drop) << bpc) + (g >> drop)) << bpc) + (b >> drop);
350 }
351 }
352
353 /* Map an RGB color to a PPM color tuple. */
354 /* Keep track of whether the image is black-and-white, gray, or colored. */
355 private gx_color_index
ppm_map_rgb_color(gx_device * pdev,const gx_color_value cv[])356 ppm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
357 {
358 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
359 gx_color_index color =
360 gx_old_default_rgb_map_rgb_color(pdev, cv[0], cv[1], cv[2]);
361 uint bpc = pdev->color_info.depth / 3;
362 gx_color_index mask =
363 ((gx_color_index)1 << (pdev->color_info.depth - bpc)) - 1;
364 if (!(((color >> bpc) ^ color) & mask)) { /* gray shade */
365 if (color != 0 && (~color & mask))
366 bdev->uses_color |= 1;
367 } else /* color */
368 bdev->uses_color = 2;
369 return color;
370 }
371
372 /* Map a PPM color tuple back to an RGB color. */
373 private int
ppm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])374 ppm_map_color_rgb(gx_device * dev, gx_color_index color,
375 gx_color_value prgb[3])
376 {
377 uint bitspercolor = dev->color_info.depth / 3;
378 uint colormask = (1 << bitspercolor) - 1;
379 uint max_rgb = dev->color_info.max_color;
380
381 prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
382 (ulong) gx_max_color_value / max_rgb;
383 prgb[1] = ((color >> bitspercolor) & colormask) *
384 (ulong) gx_max_color_value / max_rgb;
385 prgb[2] = (color & colormask) *
386 (ulong) gx_max_color_value / max_rgb;
387 return 0;
388 }
389
390 /* Map a CMYK color to a pixel value. */
391 private gx_color_index
pkm_map_cmyk_color(gx_device * pdev,const gx_color_value cv[])392 pkm_map_cmyk_color(gx_device * pdev, const gx_color_value cv[])
393 {
394 uint bpc = pdev->color_info.depth >> 2;
395 uint max_value = pdev->color_info.max_color;
396 uint cc = cv[0] * max_value / gx_max_color_value;
397 uint mc = cv[1] * max_value / gx_max_color_value;
398 uint yc = cv[2] * max_value / gx_max_color_value;
399 uint kc = cv[3] * max_value / gx_max_color_value;
400 gx_color_index color =
401 (((((cc << bpc) + mc) << bpc) + yc) << bpc) + kc;
402
403 return (color == gx_no_color_index ? color ^ 1 : color);
404 }
405
406 /* Map a CMYK pixel value to RGB. */
407 private int
pkm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])408 pkm_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3])
409 {
410 int bpc = dev->color_info.depth >> 2;
411 gx_color_index cshift = color;
412 uint mask = (1 << bpc) - 1;
413 uint k = cshift & mask;
414 uint y = (cshift >>= bpc) & mask;
415 uint m = (cshift >>= bpc) & mask;
416 uint c = cshift >> bpc;
417 uint max_value = dev->color_info.max_color;
418 uint not_k = max_value - k;
419
420 #define CVALUE(c)\
421 ((gx_color_value)((ulong)(c) * gx_max_color_value / max_value))
422 /* We use our improved conversion rule.... */
423 rgb[0] = CVALUE((max_value - c) * not_k / max_value);
424 rgb[1] = CVALUE((max_value - m) * not_k / max_value);
425 rgb[2] = CVALUE((max_value - y) * not_k / max_value);
426 #undef CVALUE
427 return 0;
428 }
429
430 /* Augment get/put_params to add UsePlanarBuffer */
431
432 private int
ppm_get_params(gx_device * pdev,gs_param_list * plist)433 ppm_get_params(gx_device * pdev, gs_param_list * plist)
434 {
435 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
436
437 return gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
438 }
439
440 private int
ppm_put_params(gx_device * pdev,gs_param_list * plist)441 ppm_put_params(gx_device * pdev, gs_param_list * plist)
442 {
443 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
444 gx_device_color_info save_info;
445 int ncomps = pdev->color_info.num_components;
446 int bpc = pdev->color_info.depth / ncomps;
447 int ecode = 0;
448 int code;
449 long v;
450 const char *vname;
451
452 save_info = pdev->color_info;
453 if ((code = param_read_long(plist, (vname = "GrayValues"), &v)) != 1 ||
454 (code = param_read_long(plist, (vname = "RedValues"), &v)) != 1 ||
455 (code = param_read_long(plist, (vname = "GreenValues"), &v)) != 1 ||
456 (code = param_read_long(plist, (vname = "BlueValues"), &v)) != 1
457 ) {
458 if (code < 0)
459 ecode = code;
460 else if (v < 2 || v > (bdev->is_raw || ncomps > 1 ? 256 : 65536L))
461 param_signal_error(plist, vname,
462 ecode = gs_error_rangecheck);
463 else if (v == 2)
464 bpc = 1;
465 else if (v <= 4)
466 bpc = 2;
467 else if (v <= 16)
468 bpc = 4;
469 else if (v <= 32 && ncomps == 3)
470 bpc = 5;
471 else if (v <= 256)
472 bpc = 8;
473 else
474 bpc = 16;
475 if (ecode >= 0) {
476 static const byte depths[4][16] =
477 {
478 {1, 2, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 16},
479 {0},
480 {4, 8, 0, 16, 16, 0, 0, 24},
481 {4, 8, 0, 16, 0, 0, 0, 32},
482 };
483
484 pdev->color_info.depth = depths[ncomps - 1][bpc - 1];
485 pdev->color_info.max_gray = pdev->color_info.max_color =
486 (pdev->color_info.dither_grays =
487 pdev->color_info.dither_colors = (int)v) - 1;
488 }
489 }
490 if ((code = ecode) < 0 ||
491 (code = gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer)) < 0
492 )
493 pdev->color_info = save_info;
494 ppm_set_dev_procs(pdev);
495 return code;
496 }
497
498 /* Copy an alpha map, noting whether we may generate some non-black/white */
499 /* colors through blending. */
500 private 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)501 pnm_copy_alpha(gx_device * pdev, const byte * data, int data_x,
502 int raster, gx_bitmap_id id, int x, int y, int width, int height,
503 gx_color_index color, int depth)
504 {
505 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
506
507 if (pdev->color_info.depth < 24 ||
508 (color >> 8) == (color & 0xffff)
509 )
510 bdev->uses_color |= 1;
511 else
512 bdev->uses_color |= 2;
513 return (*bdev->save_copy_alpha) (pdev, data, data_x, raster, id,
514 x, y, width, height, color, depth);
515 }
516
517 /* Begin processing an image, noting whether we may generate some */
518 /* non-black/white colors in the process. */
519 private int
pnm_begin_typed_image(gx_device * dev,const gs_imager_state * pis,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)520 pnm_begin_typed_image(gx_device *dev,
521 const gs_imager_state *pis, const gs_matrix *pmat,
522 const gs_image_common_t *pim, const gs_int_rect *prect,
523 const gx_drawing_color *pdcolor,
524 const gx_clip_path *pcpath,
525 gs_memory_t *memory, gx_image_enum_common_t **pinfo)
526 {
527 gx_device_pbm * const bdev = (gx_device_pbm *)dev;
528
529 /* Conservatively guesses whether this operation causes color usage
530 that might not be otherwise captured by ppm_map_color_rgb. */
531 if (pim && pim->type) {
532 switch (pim->type->index) {
533 case 1: case 3: case 4: {
534 /* Use colorspace to handle image types 1,3,4 */
535 const gs_pixel_image_t *pim1 = (const gs_pixel_image_t *)pim;
536
537 if (pim1->ColorSpace) {
538 if (gs_color_space_get_index(pim1->ColorSpace) == gs_color_space_index_DeviceGray) {
539 if (pim1->BitsPerComponent > 1)
540 bdev->uses_color |= 1;
541 } else
542 bdev->uses_color = 2;
543 }
544 break;
545 }
546 default:
547 /* Conservatively handles other image types */
548 bdev->uses_color = 2;
549 }
550 }
551 /* Forward to saved routine */
552 return (*bdev->save_begin_typed_image)(dev, pis, pmat, pim, prect,
553 pdcolor, pcpath, memory, pinfo);
554 }
555
556
557 /* ------ Internal routines ------ */
558
559 /* Print a page using a given row printing routine. */
560 private int
pbm_print_page_loop(gx_device_printer * pdev,char magic,FILE * pstream,int (* row_proc)(gx_device_printer *,byte *,int,FILE *))561 pbm_print_page_loop(gx_device_printer * pdev, char magic, FILE * pstream,
562 int (*row_proc) (gx_device_printer *, byte *, int, FILE *))
563 {
564 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
565 uint raster = gdev_prn_raster(pdev);
566 byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
567 int lnum = 0;
568 int code = 0;
569
570 if (data == 0)
571 return_error(gs_error_VMerror);
572 /* Hack. This should be done in the callers. */
573 if (magic == '9')
574 fprintf(pstream, "%11d %11d %11d %11d %11d ",
575 0, 0, 0, pdev->width, pdev->height);
576 else if (magic == '7') {
577 int ncomps = pdev->color_info.num_components;
578 fprintf(pstream, "P%c\n", magic);
579 fprintf(pstream, "WIDTH %d\n", pdev->width);
580 fprintf(pstream, "HEIGHT %d\n", pdev->height);
581 fprintf(pstream, "DEPTH %d\n", ncomps);
582 fprintf(pstream, "MAXVAL %d\n", pdev->color_info.max_gray);
583 fprintf(pstream, "TUPLTYPE %s\n",
584 (ncomps == 4) ? "CMYK" :
585 ((ncomps == 3) ? "RGB" : "GRAYSCALE"));
586 if (bdev->comment[0])
587 fprintf(pstream, "# %s\n", bdev->comment);
588 else
589 fprintf(pstream, "# Image generated by %s (device=%s)\n",
590 gs_product, pdev->dname);
591 fprintf(pstream, "ENDHDR\n");
592 }
593 else {
594 fprintf(pstream, "P%c\n", magic);
595 if (bdev->comment[0])
596 fprintf(pstream, "# %s\n", bdev->comment);
597 else
598 fprintf(pstream, "# Image generated by %s (device=%s)\n",
599 gs_product, pdev->dname);
600 fprintf(pstream, "%d %d\n", pdev->width, pdev->height);
601 }
602 switch (magic) {
603 case '1': /* pbm */
604 case '4': /* pbmraw */
605 case '7': /* pam */
606 case '9': /* plan9bm */
607 break;
608 default:
609 fprintf(pstream, "%d\n", pdev->color_info.max_gray);
610 }
611 for (; lnum < pdev->height; lnum++) {
612 byte *row;
613
614 code = gdev_prn_get_bits(pdev, lnum, data, &row);
615 if (code < 0)
616 break;
617 code = (*row_proc) (pdev, row, pdev->color_info.depth, pstream);
618 if (code < 0)
619 break;
620 }
621 gs_free_object(pdev->memory, data, "pbm_print_page_loop");
622 return (code < 0 ? code : 0);
623 }
624
625 /* ------ Individual page printing routines ------ */
626
627 /* Print a monobit page. */
628 private int
pbm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)629 pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
630 FILE * pstream)
631 {
632 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
633
634 if (bdev->is_raw)
635 fwrite(data, 1, (pdev->width + 7) >> 3, pstream);
636 else {
637 byte *bp;
638 uint x, mask;
639
640 for (bp = data, x = 0, mask = 0x80; x < pdev->width;) {
641 putc((*bp & mask ? '1' : '0'), pstream);
642 if (++x == pdev->width || !(x & 63))
643 putc('\n', pstream);
644 if ((mask >>= 1) == 0)
645 bp++, mask = 0x80;
646 }
647 }
648 return 0;
649 }
650 private int
pbm_print_page(gx_device_printer * pdev,FILE * pstream)651 pbm_print_page(gx_device_printer * pdev, FILE * pstream)
652 {
653 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
654
655 return pbm_print_page_loop(pdev, bdev->magic, pstream, pbm_print_row);
656 }
657
658 /* Print a gray-mapped page. */
659 private int
pgm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)660 pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
661 FILE * pstream)
662 { /* Note that bpp <= 8 for raw format, bpp <= 16 for plain. */
663 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
664 uint mask = (1 << depth) - 1;
665 /*
666 * If we're writing planes for a CMYK device, we have 0 = white,
667 * mask = black, which is the opposite of the pgm convention.
668 */
669 uint invert = (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE);
670 byte *bp;
671 uint x;
672 int shift;
673
674 if (bdev->is_raw && depth == 8) {
675 if (invert) {
676 for (bp = data, x = 0; x < pdev->width; bp++, x++)
677 putc((byte)~*bp, pstream);
678 } else
679 fwrite(data, 1, pdev->width, pstream);
680 } else
681 for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
682 uint pixel;
683
684 if (shift < 0) { /* bpp = 16 */
685 pixel = ((uint) * bp << 8) + bp[1];
686 bp += 2;
687 } else {
688 pixel = (*bp >> shift) & mask;
689 if ((shift -= depth) < 0)
690 bp++, shift += 8;
691 }
692 ++x;
693 pixel ^= invert;
694 if (bdev->is_raw)
695 putc(pixel, pstream);
696 else
697 fprintf(pstream, "%d%c", pixel,
698 (x == pdev->width || !(x & 15) ? '\n' : ' '));
699 }
700 return 0;
701 }
702 private int
pxm_pbm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)703 pxm_pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
704 FILE * pstream)
705 { /* Compress a PGM or PPM row to a PBM row. */
706 /* This doesn't have to be very fast. */
707 /* Note that we have to invert the data as well. */
708 int delta = (depth + 7) >> 3;
709 byte *src = data + delta - 1; /* always big-endian */
710 byte *dest = data;
711 int x;
712 byte out_mask = 0x80;
713 byte out = 0;
714
715 if (depth >= 8) { /* One or more bytes per source pixel. */
716 for (x = 0; x < pdev->width; x++, src += delta) {
717 if (!(*src & 1))
718 out |= out_mask;
719 out_mask >>= 1;
720 if (!out_mask)
721 out_mask = 0x80,
722 *dest++ = out,
723 out = 0;
724 }
725 } else { /* Multiple source pixels per byte. */
726 byte in_mask = 0x100 >> depth;
727
728 for (x = 0; x < pdev->width; x++) {
729 if (!(*src & in_mask))
730 out |= out_mask;
731 in_mask >>= depth;
732 if (!in_mask)
733 in_mask = 0x100 >> depth,
734 src++;
735 out_mask >>= 1;
736 if (!out_mask)
737 out_mask = 0x80,
738 *dest++ = out,
739 out = 0;
740 }
741 }
742 if (out_mask != 0x80)
743 *dest = out;
744 return pbm_print_row(pdev, data, 1, pstream);
745 }
746 private int
pgm_print_page(gx_device_printer * pdev,FILE * pstream)747 pgm_print_page(gx_device_printer * pdev, FILE * pstream)
748 {
749 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
750
751 return (bdev->uses_color == 0 && bdev->optimize ?
752 pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
753 pxm_pbm_print_row) :
754 pbm_print_page_loop(pdev, bdev->magic, pstream,
755 pgm_print_row));
756 }
757
758 /* Print a color-mapped page. */
759 private int
ppgm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream,bool color)760 ppgm_print_row(gx_device_printer * pdev, byte * data, int depth,
761 FILE * pstream, bool color)
762 { /* If color=false, write only one value per pixel; */
763 /* if color=true, write 3 values per pixel. */
764 /* Note that depth <= 24 for raw format, depth <= 32 for plain. */
765 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
766 uint bpe = depth / 3; /* bits per r/g/b element */
767 uint mask = (1 << bpe) - 1;
768 byte *bp;
769 uint x;
770 uint eol_mask = (color ? 7 : 15);
771 int shift;
772
773 if (bdev->is_raw && depth == 24 && color)
774 fwrite(data, 1, pdev->width * (depth / 8), pstream);
775 else
776 for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
777 bits32 pixel = 0;
778 uint r, g, b;
779
780 switch (depth >> 3) {
781 case 4:
782 pixel = (bits32) * bp << 24;
783 bp++;
784 /* falls through */
785 case 3:
786 pixel += (bits32) * bp << 16;
787 bp++;
788 /* falls through */
789 case 2:
790 pixel += (uint) * bp << 8;
791 bp++;
792 /* falls through */
793 case 1:
794 pixel += *bp;
795 bp++;
796 break;
797 case 0: /* bpp == 4, bpe == 1 */
798 pixel = *bp >> shift;
799 if ((shift -= depth) < 0)
800 bp++, shift += 8;
801 break;
802 }
803 ++x;
804 b = pixel & mask;
805 pixel >>= bpe;
806 g = pixel & mask;
807 pixel >>= bpe;
808 r = pixel & mask;
809 if (bdev->is_raw) {
810 if (color) {
811 putc(r, pstream);
812 putc(g, pstream);
813 }
814 putc(b, pstream);
815 } else {
816 if (color)
817 fprintf(pstream, "%d %d ", r, g);
818 fprintf(pstream, "%d%c", b,
819 (x == pdev->width || !(x & eol_mask) ?
820 '\n' : ' '));
821 }
822 }
823 return 0;
824 }
825 private int
ppm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)826 ppm_print_row(gx_device_printer * pdev, byte * data, int depth,
827 FILE * pstream)
828 {
829 return ppgm_print_row(pdev, data, depth, pstream, true);
830 }
831 private int
ppm_pgm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)832 ppm_pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
833 FILE * pstream)
834 {
835 return ppgm_print_row(pdev, data, depth, pstream, false);
836 }
837 private int
ppm_print_page(gx_device_printer * pdev,FILE * pstream)838 ppm_print_page(gx_device_printer * pdev, FILE * pstream)
839 {
840 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
841
842 return (bdev->uses_color >= 2 || !bdev->optimize ?
843 pbm_print_page_loop(pdev, bdev->magic, pstream,
844 ppm_print_row) :
845 bdev->uses_color == 1 ?
846 pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
847 ppm_pgm_print_row) :
848 pbm_print_page_loop(pdev, (char)((int)bdev->magic - 2), pstream,
849 pxm_pbm_print_row));
850 }
851
852 private int
pam_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)853 pam_print_row(gx_device_printer * pdev, byte * data, int depth,
854 FILE * pstream)
855 {
856 if (depth == 32)
857 fwrite(data, 1, pdev->width * (depth / 8), pstream);
858 return 0;
859 }
860
861 private int
pam_print_page(gx_device_printer * pdev,FILE * pstream)862 pam_print_page(gx_device_printer * pdev, FILE * pstream)
863 {
864 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
865
866 return pbm_print_page_loop(pdev, bdev->magic, pstream,
867 pam_print_row);
868 }
869
870 /* Print a faux CMYK page. */
871 /* Print a row where each pixel occupies 4 bits (depth == 4). */
872 /* In this case, we also know pdev->color_info.max_color == 1. */
873 private int
pkm_print_row_4(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)874 pkm_print_row_4(gx_device_printer * pdev, byte * data, int depth,
875 FILE * pstream)
876 {
877 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
878 byte *bp;
879 uint x;
880 byte rv[16], gv[16], bv[16], i;
881
882 /* Precompute all the possible pixel values. */
883 for (i = 0; i < 16; ++i) {
884 gx_color_value rgb[3];
885
886 cmyk_1bit_map_color_rgb((gx_device *)pdev, (gx_color_index)i, rgb);
887 rv[i] = rgb[0] / gx_max_color_value;
888 gv[i] = rgb[1] / gx_max_color_value;
889 bv[i] = rgb[2] / gx_max_color_value;
890 }
891 /*
892 * Contrary to what the documentation implies, gcc compiles putc
893 * as a procedure call. This is ridiculous, but since we can't
894 * change it, we buffer groups of pixels ourselves and use fwrite.
895 */
896 if (bdev->is_raw) {
897 for (bp = data, x = 0; x < pdev->width;) {
898 byte raw[50 * 3]; /* 50 is arbitrary, but must be even */
899 int end = min(x + sizeof(raw) / 3, pdev->width);
900 byte *outp = raw;
901
902 for (; x < end; bp++, outp += 6, x += 2) {
903 uint b = *bp;
904 uint pixel = b >> 4;
905
906 outp[0] = rv[pixel], outp[1] = gv[pixel], outp[2] = bv[pixel];
907 pixel = b & 0xf;
908 outp[3] = rv[pixel], outp[4] = gv[pixel], outp[5] = bv[pixel];
909 }
910 /* x might overshoot the width by 1 pixel. */
911 if (x > end)
912 outp -= 3;
913 fwrite(raw, 1, outp - raw, pstream);
914 }
915 } else {
916 int shift;
917
918 for (bp = data, x = 0, shift = 4; x < pdev->width;) {
919 int pixel = (*bp >> shift) & 0xf;
920
921 shift ^= 4;
922 bp += shift >> 2;
923 ++x;
924 fprintf(pstream, "%d %d %d%c", rv[pixel], gv[pixel], bv[pixel],
925 (x == pdev->width || !(x & 7) ?
926 '\n' : ' '));
927 }
928 }
929 return 0;
930 }
931 /* Print a row where each pixel occupies 1 or more bytes (depth >= 8). */
932 private int
pkm_print_row(gx_device_printer * pdev,byte * data,int depth,FILE * pstream)933 pkm_print_row(gx_device_printer * pdev, byte * data, int depth,
934 FILE * pstream)
935 {
936 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
937 byte *bp;
938 uint x;
939 ulong max_value = pdev->color_info.max_color;
940
941 for (bp = data, x = 0; x < pdev->width;) {
942 bits32 pixel = 0;
943 gx_color_value rgb[3];
944 uint r, g, b;
945
946 switch (depth >> 3) {
947 case 4:
948 pixel = (bits32) * bp << 24;
949 bp++;
950 /* falls through */
951 case 3:
952 pixel += (bits32) * bp << 16;
953 bp++;
954 /* falls through */
955 case 2:
956 pixel += (uint) * bp << 8;
957 bp++;
958 /* falls through */
959 case 1:
960 pixel += *bp;
961 bp++;
962 }
963 ++x;
964 pkm_map_color_rgb((gx_device *) pdev, pixel, rgb);
965 r = rgb[0] * max_value / gx_max_color_value;
966 g = rgb[1] * max_value / gx_max_color_value;
967 b = rgb[2] * max_value / gx_max_color_value;
968 if (bdev->is_raw) {
969 putc(r, pstream);
970 putc(g, pstream);
971 putc(b, pstream);
972 } else {
973 fprintf(pstream, "%d %d %d%c", r, g, b,
974 (x == pdev->width || !(x & 7) ?
975 '\n' : ' '));
976 }
977 }
978 return 0;
979 }
980 private int
pkm_print_page(gx_device_printer * pdev,FILE * pstream)981 pkm_print_page(gx_device_printer * pdev, FILE * pstream)
982 {
983 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
984
985 return pbm_print_page_loop(pdev, bdev->magic, pstream,
986 (pdev->color_info.depth < 8 ?
987 pkm_print_row_4 :
988 pkm_print_row));
989 }
990
991 /* Print individual separations on a single file. */
992 private int
psm_print_page(gx_device_printer * pdev,FILE * pstream)993 psm_print_page(gx_device_printer * pdev, FILE * pstream)
994 {
995 gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
996 /*
997 * Allocate a large enough buffer for full pixels, on the theory that we
998 * don't know how many bits will be allocated to each component. (This
999 * is for didactic purposes only: we know perfectly well that each
1000 * component will have 1/N of the bits.)
1001 */
1002 uint max_raster = bitmap_raster(pdev->width * pdev->color_info.depth);
1003 byte *data = gs_alloc_bytes(pdev->memory, max_raster, "pksm_print_page");
1004 int code = 0;
1005 int plane;
1006
1007 if (data == 0)
1008 return_error(gs_error_VMerror);
1009 for (plane = 0; plane < pdev->color_info.num_components; ++plane) {
1010 int lnum, band_end;
1011 /*
1012 * The following initialization is unnecessary: lnum == band_end on
1013 * the first pass through the loop below, so marked will always be
1014 * set before it is used. We initialize marked solely to suppress
1015 * bogus warning messages from certain compilers.
1016 */
1017 gx_color_index marked = 0;
1018 gx_render_plane_t render_plane;
1019 int plane_depth;
1020 int plane_shift;
1021 gx_color_index plane_mask;
1022 int raster;
1023
1024 gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
1025 plane_depth = render_plane.depth;
1026 plane_shift = render_plane.shift;
1027 plane_mask = (1 << plane_depth) - 1;
1028 raster = bitmap_raster(pdev->width * plane_depth);
1029 fprintf(pstream, "P%c\n", bdev->magic + (plane_depth > 1));
1030 if (bdev->comment[0])
1031 fprintf(pstream, "# %s\n", bdev->comment);
1032 else
1033 fprintf(pstream, "# Image generated by %s (device=%s)\n",
1034 gs_product, pdev->dname);
1035 fprintf(pstream, "%d %d\n", pdev->width, pdev->height);
1036 if (plane_depth > 1)
1037 fprintf(pstream, "%d\n", pdev->color_info.max_gray);
1038 for (lnum = band_end = 0; lnum < pdev->height; lnum++) {
1039 byte *row;
1040
1041 if (lnum == band_end) {
1042 gx_colors_used_t colors_used;
1043 int band_start;
1044 int band_height =
1045 gdev_prn_colors_used((gx_device *)pdev, lnum, 1,
1046 &colors_used, &band_start);
1047
1048 band_end = band_start + band_height;
1049 marked = colors_used.or & (plane_mask << plane_shift);
1050 if (!marked)
1051 memset(data, 0, raster);
1052 #ifdef DEBUG
1053 if (plane == 0)
1054 if_debug4(':',
1055 "[:]%4d - %4d mask = 0x%lx, slow_rop = %d\n",
1056 lnum, band_end - 1, (ulong)colors_used.or,
1057 colors_used.slow_rop);
1058 #endif
1059 }
1060 if (marked) {
1061 gx_render_plane_t render_plane;
1062 uint actual_raster;
1063
1064 render_plane.index = plane;
1065 code = gdev_prn_get_lines(pdev, lnum, 1, data, raster,
1066 &row, &actual_raster,
1067 &render_plane);
1068 if (code < 0)
1069 break;
1070 } else
1071 row = data;
1072 code =
1073 (plane_depth == 1 ?
1074 pbm_print_row(pdev, row, plane_depth, pstream) :
1075 pgm_print_row(pdev, row, plane_depth, pstream));
1076 if (code < 0)
1077 break;
1078 }
1079 }
1080 gs_free_object(pdev->memory, data, "pksm_print_page");
1081 return (code < 0 ? code : 0);
1082 }
1083