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