/src/ghostpdl/devices/gdevpng.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 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 | | |
17 | | /* PNG (Portable Network Graphics) Format. Pronounced "ping". */ |
18 | | /* lpd 1999-09-24: changes PNG_NO_STDIO to PNG_NO_CONSOLE_IO for libpng |
19 | | versions 1.0.3 and later. */ |
20 | | /* lpd 1999-07-01: replaced remaining uses of gs_malloc and gs_free with |
21 | | gs_alloc_bytes and gs_free_object. */ |
22 | | /* lpd 1999-03-08: changed png.h to png_.h to allow compiling with only |
23 | | headers in /usr/include, no source code. */ |
24 | | /* lpd 1997-07-20: changed from using gs_malloc/png_xxx_int to png_create_xxx |
25 | | * for allocating structures, and from gs_free to png_write_destroy for |
26 | | * freeing them. */ |
27 | | /* lpd 1997-5-7: added PNG_LIBPNG_VER conditional for operand types of |
28 | | * dummy png_push_fill_buffer. */ |
29 | | /* lpd 1997-4-13: Added PNG_NO_STDIO to remove library access to stderr. */ |
30 | | /* lpd 1997-3-14: Added resolution (pHYs) to output. */ |
31 | | /* lpd 1996-6-24: Added #ifdef for compatibility with old libpng versions. */ |
32 | | /* lpd 1996-6-11: Edited to remove unnecessary color mapping code. */ |
33 | | /* lpd (L. Peter Deutsch) 1996-4-7: Modified for libpng 0.88. */ |
34 | | /* Original version by Russell Lang 1995-07-04 */ |
35 | | |
36 | | /* RJW: Include png header BEFORE the gs ones to avoid warnings. */ |
37 | | /* |
38 | | * libpng versions 1.0.3 and later allow disabling access to the stdxxx |
39 | | * files while retaining support for FILE * I/O. |
40 | | */ |
41 | | #define PNG_NO_CONSOLE_IO |
42 | | /* |
43 | | * Earlier libpng versions require disabling FILE * I/O altogether. |
44 | | * This produces a compiler warning about no prototype for png_init_io. |
45 | | * The right thing will happen at link time, since the library itself |
46 | | * is compiled with stdio support. Unfortunately, we can't do this |
47 | | * conditionally depending on PNG_LIBPNG_VER, because this is defined |
48 | | * in png.h. |
49 | | */ |
50 | | /*#define PNG_NO_STDIO*/ |
51 | | #include "png_.h" |
52 | | |
53 | | #include "gdevprn.h" |
54 | | #include "gdevmem.h" |
55 | | #include "gdevpccm.h" |
56 | | #include "gscdefs.h" |
57 | | #include "gxdownscale.h" |
58 | | #include "gxdevsop.h" |
59 | | #include "gscms.h" |
60 | | #include "setjmp_.h" |
61 | | |
62 | | /* ------ The device descriptors ------ */ |
63 | | |
64 | | /* |
65 | | * Default X and Y resolution. |
66 | | */ |
67 | | #define X_DPI 72 |
68 | | #define Y_DPI 72 |
69 | | |
70 | | static dev_proc_print_page(png_print_page); |
71 | | static dev_proc_print_page(png_print_page_monod); |
72 | | static dev_proc_open_device(pngalpha_open); |
73 | | static dev_proc_encode_color(pngalpha_encode_color); |
74 | | static dev_proc_decode_color(pngalpha_decode_color); |
75 | | static dev_proc_copy_alpha(pngalpha_copy_alpha); |
76 | | static dev_proc_fillpage(pngalpha_fillpage); |
77 | | static dev_proc_put_image(pngalpha_put_image); |
78 | | static dev_proc_get_params(pngalpha_get_params); |
79 | | static dev_proc_put_params(pngalpha_put_params); |
80 | | static dev_proc_create_buf_device(pngalpha_create_buf_device); |
81 | | static dev_proc_get_params(png_get_params_downscale); |
82 | | static dev_proc_put_params(png_put_params_downscale); |
83 | | static dev_proc_get_params(png_get_params_downscale_mfs); |
84 | | static dev_proc_put_params(png_put_params_downscale_mfs); |
85 | | static dev_proc_dev_spec_op(pngalpha_spec_op); |
86 | | |
87 | | typedef struct gx_device_png_s gx_device_png; |
88 | | struct gx_device_png_s { |
89 | | gx_device_common; |
90 | | gx_prn_device_common; |
91 | | gx_downscaler_params downscale; |
92 | | }; |
93 | | |
94 | | /* Monochrome. */ |
95 | | |
96 | | const gx_device_png gs_pngmono_device = |
97 | | { /* The print_page proc is compatible with allowing bg printing */ |
98 | | prn_device_body(gx_device_png, gdev_prn_initialize_device_procs_mono_bg, "pngmono", |
99 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
100 | | X_DPI, Y_DPI, |
101 | | 0, 0, 0, 0, /* margins */ |
102 | | 1, 1, 1, 1, 2, 2, png_print_page), |
103 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
104 | | }; |
105 | | |
106 | | |
107 | | /* 4-bit planar (EGA/VGA-style) color. */ |
108 | | |
109 | | /* Since the print_page doesn't alter the device, this device can print in the background */ |
110 | | static void |
111 | | png16_initialize_device_procs(gx_device *dev) |
112 | 0 | { |
113 | 0 | gdev_prn_initialize_device_procs_bg(dev); |
114 | |
|
115 | 0 | set_dev_proc(dev, map_rgb_color, pc_4bit_map_rgb_color); |
116 | 0 | set_dev_proc(dev, map_color_rgb, pc_4bit_map_color_rgb); |
117 | 0 | set_dev_proc(dev, encode_color, pc_4bit_map_rgb_color); |
118 | 0 | set_dev_proc(dev, decode_color, pc_4bit_map_color_rgb); |
119 | 0 | } |
120 | | |
121 | | const gx_device_png gs_png16_device = { |
122 | | prn_device_body(gx_device_png, png16_initialize_device_procs, "png16", |
123 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
124 | | X_DPI, Y_DPI, |
125 | | 0, 0, 0, 0, /* margins */ |
126 | | 3, 4, 1, 1, 2, 2, png_print_page), |
127 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
128 | | }; |
129 | | |
130 | | /* 8-bit (SuperVGA-style) color. */ |
131 | | /* (Uses a fixed palette of 3,3,2 bits.) */ |
132 | | |
133 | | /* Since the print_page doesn't alter the device, this device can print in the background */ |
134 | | static void |
135 | | png256_initialize_device_procs(gx_device *dev) |
136 | 0 | { |
137 | 0 | gdev_prn_initialize_device_procs_bg(dev); |
138 | |
|
139 | 0 | set_dev_proc(dev, map_rgb_color, pc_8bit_map_rgb_color); |
140 | 0 | set_dev_proc(dev, map_color_rgb, pc_8bit_map_color_rgb); |
141 | 0 | set_dev_proc(dev, encode_color, pc_8bit_map_rgb_color); |
142 | 0 | set_dev_proc(dev, decode_color, pc_8bit_map_color_rgb); |
143 | 0 | } |
144 | | |
145 | | const gx_device_png gs_png256_device = { |
146 | | prn_device_body(gx_device_png, png256_initialize_device_procs, "png256", |
147 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
148 | | X_DPI, Y_DPI, |
149 | | 0, 0, 0, 0, /* margins */ |
150 | | 3, 8, 5, 5, 6, 6, png_print_page), |
151 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
152 | | }; |
153 | | |
154 | | /* 8-bit gray */ |
155 | | |
156 | | /* Since the print_page doesn't alter the device, this device can print in the background */ |
157 | | static void |
158 | | pnggray_initialize_device_procs(gx_device *dev) |
159 | 0 | { |
160 | 0 | gdev_prn_initialize_device_procs_gray_bg(dev); |
161 | |
|
162 | 0 | set_dev_proc(dev, get_params, png_get_params_downscale); |
163 | 0 | set_dev_proc(dev, put_params, png_put_params_downscale); |
164 | 0 | set_dev_proc(dev, encode_color, gx_default_8bit_map_gray_color); |
165 | 0 | set_dev_proc(dev, decode_color, gx_default_8bit_map_color_gray); |
166 | 0 | } |
167 | | |
168 | | const gx_device_png gs_pnggray_device = |
169 | | {prn_device_body(gx_device_png, pnggray_initialize_device_procs, "pnggray", |
170 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
171 | | X_DPI, Y_DPI, |
172 | | 0, 0, 0, 0, /* margins */ |
173 | | 1, 8, 255, 0, 256, 0, png_print_page), |
174 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
175 | | }; |
176 | | |
177 | | /* Monochrome (with error diffusion) */ |
178 | | |
179 | | /* Since the print_page doesn't alter the device, this device can print in the background */ |
180 | | static void |
181 | | pngmonod_initialize_device_procs(gx_device *dev) |
182 | 0 | { |
183 | 0 | gdev_prn_initialize_device_procs_gray_bg(dev); |
184 | |
|
185 | 0 | set_dev_proc(dev, get_params, png_get_params_downscale_mfs); |
186 | 0 | set_dev_proc(dev, put_params, png_put_params_downscale_mfs); |
187 | 0 | set_dev_proc(dev, encode_color, gx_default_8bit_map_gray_color); |
188 | 0 | set_dev_proc(dev, decode_color, gx_default_8bit_map_color_gray); |
189 | 0 | } |
190 | | |
191 | | const gx_device_png gs_pngmonod_device = |
192 | | {prn_device_body(gx_device_png, pngmonod_initialize_device_procs, "pngmonod", |
193 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
194 | | X_DPI, Y_DPI, |
195 | | 0, 0, 0, 0, /* margins */ |
196 | | 1, 8, 255, 0, 256, 0, png_print_page_monod), |
197 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
198 | | }; |
199 | | |
200 | | /* 24-bit color. */ |
201 | | |
202 | | /* Since the print_page doesn't alter the device, this device can print in the background */ |
203 | | static void |
204 | | png16m_initialize_device_procs(gx_device *dev) |
205 | 9.20k | { |
206 | 9.20k | gdev_prn_initialize_device_procs_rgb_bg(dev); |
207 | | |
208 | 9.20k | set_dev_proc(dev, get_params, png_get_params_downscale); |
209 | 9.20k | set_dev_proc(dev, put_params, png_put_params_downscale); |
210 | | |
211 | | /* The prn macros used in previous versions of the code leave |
212 | | * encode_color and decode_color set to NULL (which are then rewritten |
213 | | * by the system to the default. For compatibility we do the same. */ |
214 | 9.20k | set_dev_proc(dev, encode_color, gx_default_rgb_map_rgb_color); |
215 | 9.20k | set_dev_proc(dev, decode_color, gx_default_rgb_map_color_rgb); |
216 | 9.20k | } |
217 | | |
218 | | const gx_device_png gs_png16m_device = |
219 | | {prn_device_body(gx_device_png, png16m_initialize_device_procs, "png16m", |
220 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
221 | | X_DPI, Y_DPI, |
222 | | 0, 0, 0, 0, /* margins */ |
223 | | 3, 24, 255, 255, 256, 256, png_print_page), |
224 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
225 | | }; |
226 | | |
227 | | /* 48 bit color. */ |
228 | | |
229 | | static void |
230 | | png48_initialize_device_procs(gx_device *dev) |
231 | 0 | { |
232 | 0 | gdev_prn_initialize_device_procs_rgb_bg(dev); |
233 | | |
234 | | /* The prn macros used in previous versions of the code leave |
235 | | * encode_color and decode_color set to NULL (which are then rewritten |
236 | | * by the system to the default. For compatibility we do the same. */ |
237 | 0 | set_dev_proc(dev, encode_color, gx_default_rgb_map_rgb_color); |
238 | 0 | set_dev_proc(dev, decode_color, gx_default_rgb_map_color_rgb); |
239 | 0 | } |
240 | | |
241 | | /* Since the print_page doesn't alter the device, this device can print in the background */ |
242 | | const gx_device_png gs_png48_device = |
243 | | {prn_device_body(gx_device_png, png48_initialize_device_procs, "png48", |
244 | | DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, |
245 | | X_DPI, Y_DPI, |
246 | | 0, 0, 0, 0, /* margins */ |
247 | | 3, 48, 0, 65535, 1, 65536, png_print_page), |
248 | | GX_DOWNSCALER_PARAMS_DEFAULTS |
249 | | }; |
250 | | |
251 | | /* 32-bit RGBA */ |
252 | | /* pngalpha device is 32-bit RGBA, with the alpha channel |
253 | | * indicating pixel coverage, not true transparency. |
254 | | * Anti-aliasing is enabled by default. |
255 | | * An erasepage will erase to transparent, not white. |
256 | | * It is intended to be used for creating web graphics with |
257 | | * a transparent background. |
258 | | */ |
259 | | typedef struct gx_device_pngalpha_s gx_device_pngalpha; |
260 | | struct gx_device_pngalpha_s { |
261 | | gx_device_common; |
262 | | gx_prn_device_common; |
263 | | gx_downscaler_params downscale; |
264 | | int background; |
265 | | }; |
266 | | |
267 | | static void |
268 | | pngalpha_initialize_device_procs(gx_device *dev) |
269 | 0 | { |
270 | 0 | gdev_prn_initialize_device_procs_bg(dev); |
271 | |
|
272 | 0 | set_dev_proc(dev, open_device, pngalpha_open); |
273 | 0 | set_dev_proc(dev, map_rgb_color, pngalpha_encode_color); |
274 | 0 | set_dev_proc(dev, map_color_rgb, pngalpha_decode_color); |
275 | 0 | set_dev_proc(dev, encode_color, pngalpha_encode_color); |
276 | 0 | set_dev_proc(dev, decode_color, pngalpha_decode_color); |
277 | 0 | set_dev_proc(dev, get_params, pngalpha_get_params); |
278 | 0 | set_dev_proc(dev, put_params, pngalpha_put_params); |
279 | 0 | set_dev_proc(dev, copy_alpha, pngalpha_copy_alpha); |
280 | 0 | set_dev_proc(dev, get_color_mapping_procs, gx_default_DevRGB_get_color_mapping_procs); |
281 | 0 | set_dev_proc(dev, get_color_comp_index, gx_default_DevRGB_get_color_comp_index); |
282 | 0 | set_dev_proc(dev, fillpage, pngalpha_fillpage); |
283 | 0 | set_dev_proc(dev, put_image, pngalpha_put_image); |
284 | 0 | set_dev_proc(dev, dev_spec_op, pngalpha_spec_op); |
285 | 0 | } |
286 | | |
287 | | const gx_device_pngalpha gs_pngalpha_device = { |
288 | | std_device_part1_(gx_device_pngalpha, |
289 | | pngalpha_initialize_device_procs, "pngalpha", |
290 | | &st_device_printer, open_init_closed), |
291 | | /* color_info */ |
292 | | {3 /* max components */, |
293 | | 3 /* number components */, |
294 | | GX_CINFO_POLARITY_ADDITIVE /* polarity */, |
295 | | 32 /* depth */, |
296 | | -1 /* gray index */, |
297 | | 255 /* max gray */, |
298 | | 255 /* max color */, |
299 | | 256 /* dither grays */, |
300 | | 256 /* dither colors */, |
301 | | { 4, 4 } /* antialias info text, graphics */, |
302 | | GX_CINFO_UNKNOWN_SEP_LIN /* separable_and_linear */, |
303 | | { 0 } /* component shift */, |
304 | | { 0 } /* component bits */, |
305 | | { 0 } /* component mask */, |
306 | | "DeviceRGB" /* process color name */, |
307 | | GX_CINFO_OPMSUPPORTED_UNKNOWN /* opmsupported */, |
308 | | 0 /* process_cmps */, |
309 | | 0 /* icc_locations */ |
310 | | }, |
311 | | std_device_part2_( |
312 | | (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5), |
313 | | (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5), |
314 | | X_DPI, Y_DPI), |
315 | | offset_margin_values(0, 0, 0, 0, 0, 0), |
316 | | std_device_part3_(), |
317 | | prn_device_body_rest_(png_print_page), |
318 | | GX_DOWNSCALER_PARAMS_DEFAULTS, |
319 | | 0xffffff /* white background */ |
320 | | }; |
321 | | |
322 | | const gx_device_pngalpha gs_png16malpha_device = { |
323 | | std_device_part1_(gx_device_pngalpha, |
324 | | pngalpha_initialize_device_procs, "png16malpha", |
325 | | &st_device_printer, open_init_closed), |
326 | | /* color_info */ |
327 | | {3 /* max components */, |
328 | | 3 /* number components */, |
329 | | GX_CINFO_POLARITY_ADDITIVE /* polarity */, |
330 | | 32 /* depth */, |
331 | | -1 /* gray index */, |
332 | | 255 /* max gray */, |
333 | | 255 /* max color */, |
334 | | 256 /* dither grays */, |
335 | | 256 /* dither colors */, |
336 | | { 1, 1 } /* antialias info text, graphics */, |
337 | | GX_CINFO_UNKNOWN_SEP_LIN /* separable_and_linear */, |
338 | | { 0 } /* component shift */, |
339 | | { 0 } /* component bits */, |
340 | | { 0 } /* component mask */, |
341 | | "DeviceRGB" /* process color name */, |
342 | | GX_CINFO_OPMSUPPORTED_UNKNOWN /* opmsupported */, |
343 | | 0 /* process_cmps */, |
344 | | 0 /* icc_locations */ |
345 | | }, |
346 | | std_device_part2_( |
347 | | (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5), |
348 | | (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5), |
349 | | X_DPI, Y_DPI), |
350 | | offset_margin_values(0, 0, 0, 0, 0, 0), |
351 | | std_device_part3_(), |
352 | | prn_device_body_rest_(png_print_page), |
353 | | GX_DOWNSCALER_PARAMS_DEFAULTS, |
354 | | 0xffffff /* white background */ |
355 | | }; |
356 | | |
357 | | /* ------ Private definitions ------ */ |
358 | | |
359 | | static int |
360 | | png_get_params_downscale(gx_device * dev, gs_param_list * plist) |
361 | 184k | { |
362 | 184k | gx_device_png *pdev = (gx_device_png *)dev; |
363 | 184k | int code, ecode; |
364 | | |
365 | 184k | ecode = 0; |
366 | 184k | if ((code = gx_downscaler_write_params(plist, &pdev->downscale, 0)) < 0) |
367 | 0 | ecode = code; |
368 | | |
369 | 184k | code = gdev_prn_get_params(dev, plist); |
370 | 184k | if (code < 0) |
371 | 0 | ecode = code; |
372 | | |
373 | 184k | return ecode; |
374 | 184k | } |
375 | | |
376 | | static int |
377 | | png_put_params_downscale(gx_device *dev, gs_param_list *plist) |
378 | 53.4k | { |
379 | 53.4k | gx_device_png *pdev = (gx_device_png *)dev; |
380 | 53.4k | int code, ecode; |
381 | | |
382 | 53.4k | ecode = gx_downscaler_read_params(plist, &pdev->downscale, 0); |
383 | | |
384 | 53.4k | code = gdev_prn_put_params(dev, plist); |
385 | 53.4k | if (code < 0) |
386 | 40 | ecode = code; |
387 | | |
388 | 53.4k | return ecode; |
389 | 53.4k | } |
390 | | |
391 | | static int |
392 | | png_get_params_downscale_mfs(gx_device *dev, gs_param_list *plist) |
393 | 0 | { |
394 | 0 | gx_device_png *pdev = (gx_device_png *)dev; |
395 | 0 | int code, ecode; |
396 | |
|
397 | 0 | ecode = gx_downscaler_write_params(plist, &pdev->downscale, |
398 | 0 | GX_DOWNSCALER_PARAMS_MFS); |
399 | |
|
400 | 0 | code = gdev_prn_get_params(dev, plist); |
401 | 0 | if (code < 0) |
402 | 0 | ecode = code; |
403 | |
|
404 | 0 | return ecode; |
405 | 0 | } |
406 | | |
407 | | static int |
408 | | png_put_params_downscale_mfs(gx_device *dev, gs_param_list *plist) |
409 | 0 | { |
410 | 0 | gx_device_png *pdev = (gx_device_png *)dev; |
411 | 0 | int code, ecode; |
412 | |
|
413 | 0 | ecode = gx_downscaler_read_params(plist, &pdev->downscale, |
414 | 0 | GX_DOWNSCALER_PARAMS_MFS); |
415 | |
|
416 | 0 | code = gdev_prn_put_params(dev, plist); |
417 | 0 | if (code < 0) |
418 | 0 | ecode = code; |
419 | |
|
420 | 0 | return ecode; |
421 | 0 | } |
422 | | |
423 | 121k | #define PNG_MEM_ALIGN 16 |
424 | | static png_voidp |
425 | | gdevpng_malloc(png_structp png, png_size_t size) |
426 | 60.8k | { |
427 | 60.8k | gs_memory_t *mem = png_get_mem_ptr(png); |
428 | 60.8k | uchar *unaligned; |
429 | 60.8k | uchar *aligned; |
430 | | |
431 | 60.8k | if (size == 0) |
432 | 0 | return NULL; |
433 | 60.8k | unaligned = gs_alloc_bytes(mem, size + PNG_MEM_ALIGN, "libpng"); |
434 | 60.8k | if (unaligned == NULL) |
435 | 0 | return NULL; |
436 | | |
437 | 60.8k | aligned = (uchar *)((intptr_t)(unaligned + PNG_MEM_ALIGN) & ~(PNG_MEM_ALIGN - 1)); |
438 | 60.8k | aligned[-1] = (uchar)(aligned - unaligned); |
439 | | |
440 | 60.8k | return aligned; |
441 | 60.8k | } |
442 | | |
443 | | static void |
444 | | gdevpng_free(png_structp png, png_voidp ptr) |
445 | 60.8k | { |
446 | 60.8k | gs_memory_t *mem = png_get_mem_ptr(png); |
447 | 60.8k | uchar *aligned = ptr; |
448 | 60.8k | if (aligned == NULL) |
449 | 0 | return; |
450 | 60.8k | gs_free_object(mem, aligned - aligned[-1], "libpng"); |
451 | 60.8k | } |
452 | | |
453 | | static void |
454 | | my_png_write(png_struct *png, png_bytep buf, png_size_t size) |
455 | 122k | { |
456 | 122k | gp_file *file = png_get_io_ptr(png); |
457 | | |
458 | 122k | (void)gp_fwrite(buf, 1, size, file); |
459 | 122k | } |
460 | | |
461 | | static void |
462 | | my_png_flush(png_struct *png) |
463 | 0 | { |
464 | 0 | gp_file *file = png_get_io_ptr(png); |
465 | |
|
466 | 0 | (void)gp_fflush(file); |
467 | 0 | } |
468 | | |
469 | | /* Write out a page in PNG format. */ |
470 | | /* This routine is used for all formats. */ |
471 | | OPTIMIZE_SETJMP |
472 | | static int |
473 | | do_png_print_page(gx_device_png * pdev, gp_file * file, bool monod) |
474 | 4.34k | { |
475 | 4.34k | gs_memory_t *mem = pdev->memory; |
476 | 4.34k | int raster = gdev_prn_raster(pdev); |
477 | 4.34k | gx_downscaler_t ds; |
478 | | |
479 | | /* PNG structures */ |
480 | 4.34k | byte *row = gs_alloc_bytes(mem, raster, "png raster buffer"); |
481 | 4.34k | png_struct *png_ptr = |
482 | 4.34k | png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, pdev->memory, gdevpng_malloc, gdevpng_free); |
483 | 4.34k | png_info *info_ptr = png_create_info_struct(png_ptr); |
484 | 4.34k | int depth = pdev->color_info.depth; |
485 | 4.34k | int y; |
486 | 4.34k | int code; /* return code */ |
487 | 4.34k | char software_key[80]; |
488 | 4.34k | char software_text[256]; |
489 | 4.34k | png_text text_png; |
490 | 4.34k | int dst_bpc, src_bpc; |
491 | 4.34k | bool errdiff = 0; |
492 | | |
493 | 4.34k | bool invert = false, endian_swap = false, bg_needed = false; |
494 | 4.34k | png_byte bit_depth = 0; |
495 | 4.34k | png_byte color_type = 0; |
496 | 4.34k | png_uint_32 x_pixels_per_unit; |
497 | 4.34k | png_uint_32 y_pixels_per_unit; |
498 | 4.34k | png_byte phys_unit_type; |
499 | 4.34k | png_color_16 background; |
500 | 4.34k | png_uint_32 width, height; |
501 | 4.34k | #if PNG_LIBPNG_VER_MINOR >= 5 |
502 | 4.34k | png_color palette[256]; |
503 | 4.34k | #endif |
504 | 4.34k | png_color *palettep; |
505 | 4.34k | png_uint_16 num_palette; |
506 | 4.34k | png_uint_32 valid = 0; |
507 | 4.34k | int upfactor, downfactor; |
508 | | |
509 | | /* Sanity check params */ |
510 | 4.34k | if (pdev->downscale.downscale_factor < 1) |
511 | 0 | pdev->downscale.downscale_factor = 1; |
512 | 4.34k | if (pdev->downscale.min_feature_size < 1) |
513 | 3.98k | pdev->downscale.min_feature_size = 1; |
514 | 362 | else if (pdev->downscale.min_feature_size > 2) |
515 | 0 | pdev->downscale.min_feature_size = 2; |
516 | | |
517 | | /* Slightly nasty, but it saves us duplicating this entire routine. */ |
518 | 4.34k | if (monod) { |
519 | 0 | errdiff = 1; |
520 | 0 | depth = 1; |
521 | 0 | } |
522 | | |
523 | 4.34k | if (row == 0 || png_ptr == 0 || info_ptr == 0) { |
524 | 0 | code = gs_note_error(gs_error_VMerror); |
525 | 0 | goto done; |
526 | 0 | } |
527 | | /* set error handling */ |
528 | 4.34k | #if PNG_LIBPNG_VER_MINOR >= 5 |
529 | 4.34k | code = setjmp(png_jmpbuf(png_ptr)); |
530 | | #else |
531 | | code = setjmp(png_ptr->jmpbuf); |
532 | | #endif |
533 | 4.34k | if (code) { |
534 | | /* If we get here, we had a problem reading the file */ |
535 | 0 | code = gs_note_error(gs_error_VMerror); |
536 | 0 | goto done; |
537 | 0 | } |
538 | 4.34k | code = 0; /* for normal path */ |
539 | | /* set up the output control */ |
540 | 4.34k | png_set_write_fn(png_ptr, file, my_png_write, my_png_flush); |
541 | | |
542 | | /* set the file information here */ |
543 | 4.34k | gx_downscaler_decode_factor(pdev->downscale.downscale_factor, |
544 | 4.34k | &upfactor, &downfactor); |
545 | | /* resolution is in pixels per meter vs. dpi */ |
546 | 4.34k | x_pixels_per_unit = |
547 | 4.34k | (png_uint_32) (pdev->HWResolution[0] * upfactor * (100.0 / 2.54) / downfactor + 0.5); |
548 | 4.34k | y_pixels_per_unit = |
549 | 4.34k | (png_uint_32) (pdev->HWResolution[1] * upfactor * (100.0 / 2.54) / downfactor + 0.5); |
550 | | |
551 | 4.34k | phys_unit_type = PNG_RESOLUTION_METER; |
552 | 4.34k | valid |= PNG_INFO_pHYs; |
553 | | |
554 | 4.34k | switch (depth) { |
555 | 0 | case 32: |
556 | 0 | bit_depth = 8; |
557 | 0 | color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
558 | 0 | invert = true; |
559 | |
|
560 | 0 | { gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev; |
561 | 0 | background.index = 0; |
562 | 0 | background.red = (ppdev->background >> 16) & 0xff; |
563 | 0 | background.green = (ppdev->background >> 8) & 0xff; |
564 | 0 | background.blue = (ppdev->background) & 0xff; |
565 | 0 | background.gray = 0; |
566 | 0 | bg_needed = true; |
567 | 0 | } |
568 | 0 | errdiff = 1; |
569 | 0 | break; |
570 | 0 | case 48: |
571 | 0 | bit_depth = 16; |
572 | 0 | color_type = PNG_COLOR_TYPE_RGB; |
573 | 0 | #if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN) |
574 | 0 | endian_swap = true; |
575 | 0 | #endif |
576 | 0 | break; |
577 | 4.34k | case 24: |
578 | 4.34k | bit_depth = 8; |
579 | 4.34k | color_type = PNG_COLOR_TYPE_RGB; |
580 | 4.34k | errdiff = 1; |
581 | 4.34k | break; |
582 | 0 | case 8: |
583 | 0 | bit_depth = 8; |
584 | 0 | if (gx_device_has_color(pdev)) { |
585 | 0 | color_type = PNG_COLOR_TYPE_PALETTE; |
586 | 0 | errdiff = 0; |
587 | 0 | } else { |
588 | 0 | color_type = PNG_COLOR_TYPE_GRAY; |
589 | 0 | errdiff = 1; |
590 | 0 | } |
591 | 0 | break; |
592 | 0 | case 4: |
593 | 0 | bit_depth = 4; |
594 | 0 | color_type = PNG_COLOR_TYPE_PALETTE; |
595 | 0 | break; |
596 | 0 | case 1: |
597 | 0 | bit_depth = 1; |
598 | 0 | color_type = PNG_COLOR_TYPE_GRAY; |
599 | | /* invert monochrome pixels */ |
600 | 0 | if (!monod) { |
601 | 0 | invert = true; |
602 | 0 | } |
603 | 0 | break; |
604 | 4.34k | } |
605 | | |
606 | | /* set the palette if there is one */ |
607 | 4.34k | if (color_type == PNG_COLOR_TYPE_PALETTE) { |
608 | 0 | int i; |
609 | 0 | int num_colors = 1 << depth; |
610 | 0 | gx_color_value rgb[3]; |
611 | |
|
612 | 0 | #if PNG_LIBPNG_VER_MINOR >= 5 |
613 | 0 | palettep = palette; |
614 | | #else |
615 | | palettep = |
616 | | (void *)gs_alloc_bytes(mem, 256 * sizeof(png_color), |
617 | | "png palette"); |
618 | | if (palettep == 0) { |
619 | | code = gs_note_error(gs_error_VMerror); |
620 | | goto done; |
621 | | } |
622 | | #endif |
623 | 0 | num_palette = num_colors; |
624 | 0 | valid |= PNG_INFO_PLTE; |
625 | 0 | for (i = 0; i < num_colors; i++) { |
626 | 0 | (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev, |
627 | 0 | (gx_color_index) i, rgb); |
628 | 0 | palettep[i].red = gx_color_value_to_byte(rgb[0]); |
629 | 0 | palettep[i].green = gx_color_value_to_byte(rgb[1]); |
630 | 0 | palettep[i].blue = gx_color_value_to_byte(rgb[2]); |
631 | 0 | } |
632 | 0 | } |
633 | 4.34k | else { |
634 | 4.34k | palettep = NULL; |
635 | 4.34k | num_palette = 0; |
636 | 4.34k | } |
637 | | /* add comment */ |
638 | | #ifdef CLUSTER |
639 | | strncpy(software_key, "GPL Ghostscript", sizeof(software_key)); |
640 | | strncpy(software_text, "GPL Ghostscript", sizeof(software_text)); |
641 | | #else |
642 | 4.34k | strncpy(software_key, "Software", sizeof(software_key)); |
643 | 4.34k | { |
644 | 4.34k | int major = (int)(gs_revision / 1000); |
645 | 4.34k | int minor = (int)(gs_revision - (major * 1000)) / 10; |
646 | 4.34k | int patch = gs_revision % 10; |
647 | | |
648 | 4.34k | gs_snprintf(software_text, sizeof(software_text), "%s %d.%02d.%d", gs_product, major, minor, patch); |
649 | 4.34k | } |
650 | 4.34k | #endif |
651 | 4.34k | text_png.compression = -1; /* uncompressed */ |
652 | 4.34k | text_png.key = software_key; |
653 | 4.34k | text_png.text = software_text; |
654 | 4.34k | text_png.text_length = strlen(software_text); |
655 | | |
656 | 4.34k | dst_bpc = bit_depth; |
657 | 4.34k | src_bpc = dst_bpc; |
658 | 4.34k | if (errdiff) |
659 | 4.34k | src_bpc = 8; |
660 | 0 | else |
661 | 0 | pdev->downscale.downscale_factor = upfactor = downfactor = 1; |
662 | 4.34k | width = pdev->width * upfactor / downfactor; |
663 | 4.34k | height = pdev->height * upfactor / downfactor; |
664 | | |
665 | 4.34k | #if PNG_LIBPNG_VER_MINOR >= 5 |
666 | 4.34k | png_set_pHYs(png_ptr, info_ptr, |
667 | 4.34k | x_pixels_per_unit, y_pixels_per_unit, phys_unit_type); |
668 | | |
669 | 4.34k | png_set_IHDR(png_ptr, info_ptr, |
670 | 4.34k | width, height, bit_depth, |
671 | 4.34k | color_type, PNG_INTERLACE_NONE, |
672 | 4.34k | PNG_COMPRESSION_TYPE_DEFAULT, |
673 | 4.34k | PNG_FILTER_TYPE_DEFAULT); |
674 | 4.34k | if (palettep) |
675 | 0 | png_set_PLTE(png_ptr, info_ptr, palettep, num_palette); |
676 | | |
677 | 4.34k | png_set_text(png_ptr, info_ptr, &text_png, 1); |
678 | | |
679 | 4.34k | if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE] != NULL) { |
680 | 4.34k | cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE]; |
681 | 4.34k | if (icc_profile->hash_is_valid && icc_profile->hashcode == ARTIFEX_sRGB_HASH) { |
682 | | /* sRGB case. Just use the tag */ |
683 | 4.34k | png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE); |
684 | 4.34k | } else { |
685 | | /* PNG can only be RGB or gray. No CIELAB :( */ |
686 | 0 | if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) { |
687 | 0 | if (icc_profile->num_comps == pdev->color_info.num_components && |
688 | 0 | !(pdev->icc_struct->usefastcolor)) { |
689 | 0 | png_set_iCCP(png_ptr, info_ptr, icc_profile->name, |
690 | 0 | PNG_COMPRESSION_TYPE_DEFAULT, icc_profile->buffer, |
691 | 0 | icc_profile->buffer_size); |
692 | 0 | } |
693 | 0 | } |
694 | 0 | } |
695 | 4.34k | } |
696 | | #else |
697 | | info_ptr->bit_depth = bit_depth; |
698 | | info_ptr->color_type = color_type; |
699 | | info_ptr->width = width; |
700 | | info_ptr->height = height; |
701 | | info_ptr->x_pixels_per_unit = x_pixels_per_unit; |
702 | | info_ptr->y_pixels_per_unit = y_pixels_per_unit; |
703 | | info_ptr->phys_unit_type = phys_unit_type; |
704 | | info_ptr->palette = palettep; |
705 | | info_ptr->num_palette = num_palette; |
706 | | info_ptr->valid |= valid; |
707 | | info_ptr->text = &text_png; |
708 | | info_ptr->num_text = 1; |
709 | | /* Set up the ICC information */ |
710 | | if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE] != NULL) { |
711 | | cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE]; |
712 | | if (icc_profile->hash_is_valid && icc_profile->hashcode == ARTIFEX_sRGB_HASH) { |
713 | | /* sRGB case. Just use the tag */ |
714 | | png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE); |
715 | | } else { |
716 | | /* PNG can only be RGB or gray. No CIELAB :( */ |
717 | | if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) { |
718 | | if (icc_profile->num_comps == pdev->color_info.num_components && |
719 | | !(pdev->icc_struct->usefastcolor)) { |
720 | | info_ptr->iccp_name = icc_profile->name; |
721 | | info_ptr->iccp_profile = icc_profile->buffer; |
722 | | info_ptr->iccp_proflen = icc_profile->buffer_size; |
723 | | info_ptr->valid |= PNG_INFO_iCCP; |
724 | | } |
725 | | } |
726 | | } |
727 | | } |
728 | | #endif |
729 | 4.34k | if (invert) { |
730 | 0 | if (depth == 32) |
731 | 0 | png_set_invert_alpha(png_ptr); |
732 | 0 | else |
733 | 0 | png_set_invert_mono(png_ptr); |
734 | 0 | } |
735 | 4.34k | if (bg_needed) { |
736 | 0 | png_set_bKGD(png_ptr, info_ptr, &background); |
737 | 0 | } |
738 | 4.34k | #if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN) |
739 | 4.34k | if (endian_swap) { |
740 | 0 | png_set_swap(png_ptr); |
741 | 0 | } |
742 | 4.34k | #endif |
743 | | |
744 | | /* write the file information */ |
745 | 4.34k | png_write_info(png_ptr, info_ptr); |
746 | | |
747 | 4.34k | #if PNG_LIBPNG_VER_MINOR >= 5 |
748 | | #else |
749 | | /* don't write the comments twice */ |
750 | | info_ptr->num_text = 0; |
751 | | info_ptr->text = NULL; |
752 | | #endif |
753 | | |
754 | | /* For simplicity of code, we always go through the downscaler. For |
755 | | * non-supported depths, it will pass through with minimal performance |
756 | | * hit. So ensure that we only trigger downscales when we need them. |
757 | | */ |
758 | 4.34k | code = gx_downscaler_init(&ds, (gx_device *)pdev, src_bpc, dst_bpc, |
759 | 4.34k | depth/dst_bpc, &pdev->downscale, NULL, 0); |
760 | 4.34k | if (code >= 0) |
761 | 4.34k | { |
762 | | #ifdef CLUSTER |
763 | | int bitlen = width*dst_bpc; |
764 | | int end = bitlen>>3; |
765 | | int mask = 255>>(bitlen&7); |
766 | | if (bitlen & 7) |
767 | | mask = ~mask; |
768 | | else |
769 | | end--; |
770 | | #endif |
771 | | /* Write the contents of the image. */ |
772 | 8.29M | for (y = 0; y < height; y++) { |
773 | 8.28M | gx_downscaler_getbits(&ds, row, y); |
774 | | #ifdef CLUSTER |
775 | | row[end] &= mask; |
776 | | #endif |
777 | 8.28M | png_write_rows(png_ptr, &row, 1); |
778 | 8.28M | } |
779 | 4.34k | gx_downscaler_fin(&ds); |
780 | 4.34k | } |
781 | | |
782 | | /* write the rest of the file */ |
783 | 4.34k | png_write_end(png_ptr, info_ptr); |
784 | | |
785 | 4.34k | #if PNG_LIBPNG_VER_MINOR >= 5 |
786 | | #else |
787 | | /* if you alloced the palette, free it here */ |
788 | | gs_free_object(mem, palettep, "png palette"); |
789 | | #endif |
790 | | |
791 | 4.34k | done: |
792 | | /* free the structures */ |
793 | 4.34k | png_destroy_write_struct(&png_ptr, &info_ptr); |
794 | 4.34k | gs_free_object(mem, row, "png raster buffer"); |
795 | | |
796 | 4.34k | return code; |
797 | 4.34k | } |
798 | | |
799 | | static int |
800 | | png_print_page(gx_device_printer * pdev, gp_file * file) |
801 | 4.34k | { |
802 | 4.34k | return do_png_print_page((gx_device_png *)pdev, file, 0); |
803 | 4.34k | } |
804 | | |
805 | | static int |
806 | | png_print_page_monod(gx_device_printer * pdev, gp_file * file) |
807 | 0 | { |
808 | 0 | return do_png_print_page((gx_device_png *)pdev, file, 1); |
809 | 0 | } |
810 | | |
811 | | #if PNG_LIBPNG_VER_MINOR < 5 |
812 | | |
813 | | /* |
814 | | * Patch around a static reference to a never-used procedure. |
815 | | * This could be avoided if we were willing to edit pngconf.h to |
816 | | * #undef PNG_PROGRESSIVE_READ_SUPPORTED |
817 | | */ |
818 | | #ifdef PNG_PROGRESSIVE_READ_SUPPORTED |
819 | | # if PNG_LIBPNG_VER >= 95 |
820 | | # define PPFB_LENGTH_T png_size_t |
821 | | # else |
822 | | # define PPFB_LENGTH_T png_uint_32 |
823 | | # endif |
824 | | void |
825 | | png_push_fill_buffer(png_structp, png_bytep, PPFB_LENGTH_T); |
826 | | void |
827 | | png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, |
828 | | PPFB_LENGTH_T length) |
829 | | { |
830 | | } |
831 | | #endif |
832 | | #endif |
833 | | |
834 | | static int |
835 | | pngalpha_open(gx_device * pdev) |
836 | 0 | { |
837 | 0 | gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev; |
838 | 0 | int code; |
839 | | /* We replace create_buf_device so we can replace copy_alpha |
840 | | * for memory device, but not clist. We also replace the fillpage |
841 | | * proc with our own to fill with transparent. |
842 | | */ |
843 | 0 | ppdev->printer_procs.buf_procs.create_buf_device = |
844 | 0 | pngalpha_create_buf_device; |
845 | 0 | code = gdev_prn_open(pdev); |
846 | 0 | return code; |
847 | 0 | } |
848 | | |
849 | | static int |
850 | | pngalpha_create_buf_device(gx_device **pbdev, gx_device *target, int y, |
851 | | const gx_render_plane_t *render_plane, gs_memory_t *mem, |
852 | | gx_color_usage_t *color_usage) |
853 | 0 | { |
854 | 0 | gx_device_printer *ptarget; |
855 | 0 | int code = gx_default_create_buf_device(pbdev, target, y, |
856 | 0 | render_plane, mem, color_usage); |
857 | | /* Now set copy_alpha to one that handles RGBA */ |
858 | | |
859 | | /* this is really pretty nasty. The pngalpha device is going to replace |
860 | | * the device methods in the memory rendering device with some of its own. |
861 | | * To me this seems fraught with peril, its making a lot of assumptions |
862 | | * about the compatibility of the devices! |
863 | | * This, of course, totally breaks device chaining, but since the memory |
864 | | * device wasn't going to pass on the intermediate method calls to the |
865 | | * 'terminating' device, we can work around it here. We simply descend |
866 | | * the chain of devices to the terminating device, and pull the methods |
867 | | * we need directly from that device. I don't know why we are using |
868 | | * 'orig_procs' either, but its safe to do so because this is only |
869 | | * done here for the PNG device, and we know that this is a gx_device_prn |
870 | | * based device. |
871 | | */ |
872 | 0 | while (target->child != NULL) |
873 | 0 | target = target->child; |
874 | |
|
875 | 0 | ptarget= (gx_device_printer *)target; |
876 | 0 | set_dev_proc(*pbdev, copy_alpha, ptarget->orig_procs.copy_alpha); |
877 | 0 | set_dev_proc(*pbdev, dev_spec_op, ptarget->orig_procs.dev_spec_op); |
878 | 0 | set_dev_proc(*pbdev, fillpage, pngalpha_fillpage); |
879 | 0 | return code; |
880 | 0 | } |
881 | | |
882 | | static int |
883 | | pngalpha_put_params(gx_device * pdev, gs_param_list * plist) |
884 | 0 | { |
885 | 0 | gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev; |
886 | 0 | int background; |
887 | 0 | int code, ecode; |
888 | | |
889 | | /* BackgroundColor in format 16#RRGGBB is used for bKGD chunk */ |
890 | 0 | switch(code = param_read_int(plist, "BackgroundColor", &background)) { |
891 | 0 | case 0: |
892 | 0 | ppdev->background = background & 0xffffff; |
893 | 0 | break; |
894 | 0 | case 1: /* not found */ |
895 | 0 | code = 0; |
896 | 0 | break; |
897 | 0 | default: |
898 | 0 | param_signal_error(plist, "BackgroundColor", code); |
899 | 0 | break; |
900 | 0 | } |
901 | | |
902 | 0 | if ((ecode = gx_downscaler_read_params(plist, &ppdev->downscale, 0)) < 0) |
903 | 0 | code = ecode; |
904 | |
|
905 | 0 | if (code == 0) { |
906 | 0 | code = gdev_prn_put_params(pdev, plist); |
907 | 0 | } |
908 | |
|
909 | 0 | return code; |
910 | 0 | } |
911 | | |
912 | | /* Get device parameters */ |
913 | | static int |
914 | | pngalpha_get_params(gx_device * pdev, gs_param_list * plist) |
915 | 0 | { |
916 | 0 | gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev; |
917 | 0 | int code = gdev_prn_get_params(pdev, plist); |
918 | 0 | int ecode; |
919 | 0 | if (code >= 0) |
920 | 0 | code = param_write_int(plist, "BackgroundColor", |
921 | 0 | &(ppdev->background)); |
922 | 0 | ecode = 0; |
923 | 0 | if ((ecode = gx_downscaler_write_params(plist, &ppdev->downscale, 0)) < 0) |
924 | 0 | code = ecode; |
925 | |
|
926 | 0 | return code; |
927 | 0 | } |
928 | | |
929 | | /* RGB mapping for 32-bit RGBA color devices */ |
930 | | |
931 | | static gx_color_index |
932 | | pngalpha_encode_color(gx_device * dev, const gx_color_value cv[]) |
933 | 0 | { |
934 | | /* low 7 are alpha, stored inverted to avoid white/opaque |
935 | | * being 0xffffffff which is also gx_no_color_index. |
936 | | * So 0xff is transparent and 0x00 is opaque. |
937 | | * We always return opaque colors (bits 0-7 = 0). |
938 | | * Return value is 0xRRGGBB00. |
939 | | */ |
940 | 0 | return |
941 | 0 | ((uint) gx_color_value_to_byte(cv[2]) << 8) + |
942 | 0 | ((ulong) gx_color_value_to_byte(cv[1]) << 16) + |
943 | 0 | ((ulong) gx_color_value_to_byte(cv[0]) << 24); |
944 | 0 | } |
945 | | |
946 | | /* Map a color index to a r-g-b color. */ |
947 | | static int |
948 | | pngalpha_decode_color(gx_device * dev, gx_color_index color, |
949 | | gx_color_value prgb[]) |
950 | 0 | { |
951 | 0 | prgb[0] = gx_color_value_from_byte((color >> 24) & 0xff); |
952 | 0 | prgb[1] = gx_color_value_from_byte((color >> 16) & 0xff); |
953 | 0 | prgb[2] = gx_color_value_from_byte((color >> 8) & 0xff); |
954 | 0 | return 0; |
955 | 0 | } |
956 | | |
957 | | /* fill the page fills with transparent */ |
958 | | static int |
959 | | pngalpha_fillpage(gx_device *dev, gs_gstate * pgs, gx_device_color *pdevc) |
960 | 0 | { |
961 | 0 | return (*dev_proc(dev, fill_rectangle))(dev, 0, 0, dev->width, dev->height, 0xffffffff); |
962 | 0 | } |
963 | | |
964 | | /* Handle the RGBA planes from the PDF 1.4 compositor */ |
965 | | static int |
966 | | pngalpha_put_image (gx_device *pdev, gx_device *mdev, const byte **buffers, int num_chan, int xstart, |
967 | | int ystart, int width, int height, int row_stride, |
968 | | int alpha_plane_index, int tag_plane_index) |
969 | 0 | { |
970 | 0 | gx_device_memory *pmemdev = (gx_device_memory *)mdev; |
971 | 0 | byte *buffer_prn; |
972 | 0 | int yend = ystart + height; |
973 | 0 | int xend = xstart + width; |
974 | 0 | int x, y; |
975 | 0 | int src_position, des_position; |
976 | |
|
977 | 0 | if (num_chan != 3 || alpha_plane_index <= 0) |
978 | 0 | return_error(gs_error_unknownerror); /* can't handle these cases */ |
979 | | |
980 | | /* Now we need to convert the 4 channels (RGBA) planar into what */ |
981 | | /* the do_png_print_page expects -- chunky inverted data. For that */ |
982 | | /* we need to find the underlying gx_device_memory buffer for the */ |
983 | | /* data (similar to bit_put_image, and borrwed from there). */ |
984 | | /* Drill down to get the appropriate memory buffer pointer */ |
985 | 0 | buffer_prn = pmemdev->base; |
986 | | /* Now go ahead and process the planes into chunky as the memory device needs */ |
987 | 0 | for ( y = ystart; y < yend; y++ ) { |
988 | 0 | src_position = (y - ystart) * row_stride; |
989 | 0 | des_position = y * pmemdev->raster + xstart * 4; |
990 | 0 | for ( x = xstart; x < xend; x++ ) { |
991 | 0 | buffer_prn[des_position++] = buffers[0][src_position]; |
992 | 0 | buffer_prn[des_position++] = buffers[1][src_position]; |
993 | 0 | buffer_prn[des_position++] = buffers[2][src_position]; |
994 | | /* Alpha data in low bits. Note that Alpha is inverted. */ |
995 | 0 | buffer_prn[des_position++] = (255 - buffers[alpha_plane_index][src_position]); |
996 | 0 | src_position += 1; |
997 | 0 | } |
998 | 0 | } |
999 | 0 | return height; /* we used all of the data */ |
1000 | 0 | } |
1001 | | |
1002 | | /* Implementation for 32-bit RGBA in a memory buffer */ |
1003 | | /* Derived from gx_default_copy_alpha, but now maintains alpha channel. */ |
1004 | | static int |
1005 | | pngalpha_copy_alpha(gx_device * dev, const byte * data, int data_x, |
1006 | | int raster, gx_bitmap_id id, int x, int y, int width, int height, |
1007 | | gx_color_index color, int depth) |
1008 | 0 | { /* This might be called with depth = 1.... */ |
1009 | 0 | if (depth == 1) |
1010 | 0 | return (*dev_proc(dev, copy_mono)) (dev, data, data_x, raster, id, |
1011 | 0 | x, y, width, height, |
1012 | 0 | gx_no_color_index, color); |
1013 | | /* |
1014 | | * Simulate alpha by weighted averaging of RGB values. |
1015 | | * This is very slow, but functionally correct. |
1016 | | */ |
1017 | 0 | { |
1018 | 0 | const byte *row; |
1019 | 0 | gs_memory_t *mem = dev->memory; |
1020 | 0 | int bpp = dev->color_info.depth; |
1021 | 0 | int ncomps = dev->color_info.num_components; |
1022 | 0 | uint in_size = gx_device_raster(dev, false); |
1023 | 0 | byte *lin; |
1024 | 0 | uint out_size; |
1025 | 0 | byte *lout; |
1026 | 0 | int code = 0; |
1027 | 0 | gx_color_value color_cv[GX_DEVICE_COLOR_MAX_COMPONENTS]; |
1028 | 0 | int ry; |
1029 | 0 | gs_int_rect rect; |
1030 | 0 | gs_get_bits_params_t params; |
1031 | |
|
1032 | 0 | fit_copy(dev, data, data_x, raster, id, x, y, width, height); |
1033 | 0 | row = data; |
1034 | 0 | out_size = bitmap_raster(width * bpp); |
1035 | 0 | lin = gs_alloc_bytes(mem, in_size, "copy_alpha(lin)"); |
1036 | 0 | lout = gs_alloc_bytes(mem, out_size, "copy_alpha(lout)"); |
1037 | 0 | if (lin == 0 || lout == 0) { |
1038 | 0 | code = gs_note_error(gs_error_VMerror); |
1039 | 0 | goto out; |
1040 | 0 | } |
1041 | 0 | (*dev_proc(dev, decode_color)) (dev, color, color_cv); |
1042 | 0 | rect.p.x = 0; |
1043 | 0 | rect.q.x = dev->width; |
1044 | 0 | params.x_offset = 0; |
1045 | 0 | params.raster = bitmap_raster(dev->width * dev->color_info.depth); |
1046 | 0 | for (ry = y; ry < y + height; row += raster, ++ry) { |
1047 | 0 | byte *line; |
1048 | 0 | int sx, rx; |
1049 | |
|
1050 | 0 | byte *l_dptr = lout; |
1051 | 0 | int l_dbit = 0; |
1052 | 0 | byte l_dbyte = ((l_dbit) ? (byte)(*(l_dptr) & (0xff00 >> (l_dbit))) : 0); |
1053 | 0 | int l_xprev = x; |
1054 | |
|
1055 | 0 | rect.p.y = ry; |
1056 | 0 | rect.q.y = ry+1; |
1057 | |
|
1058 | 0 | params.options = (GB_ALIGN_ANY | |
1059 | 0 | (GB_RETURN_COPY | GB_RETURN_POINTER) | |
1060 | 0 | GB_OFFSET_0 | |
1061 | 0 | GB_RASTER_STANDARD | GB_PACKING_CHUNKY | |
1062 | 0 | GB_COLORS_NATIVE | GB_ALPHA_NONE); |
1063 | 0 | params.data[0] = lin; |
1064 | 0 | code = (*dev_proc(dev, get_bits_rectangle))(dev, &rect, ¶ms); |
1065 | 0 | if (code < 0) |
1066 | 0 | break; |
1067 | 0 | line = params.data[0]; |
1068 | 0 | for (sx = data_x, rx = x; sx < data_x + width; ++sx, ++rx) { |
1069 | 0 | gx_color_index previous = gx_no_color_index; |
1070 | 0 | gx_color_index composite; |
1071 | 0 | uint32_t alpha2, alpha; |
1072 | |
|
1073 | 0 | switch(depth) |
1074 | 0 | { |
1075 | 0 | case 2: /* map 0 - 3 to 0 - 255 */ |
1076 | 0 | alpha = ((row[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 85; |
1077 | 0 | break; |
1078 | 0 | case 4: |
1079 | 0 | alpha2 = row[sx >> 1]; |
1080 | 0 | alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4) * 17; |
1081 | 0 | break; |
1082 | 0 | case 8: |
1083 | 0 | alpha = row[sx]; |
1084 | 0 | break; |
1085 | 0 | default: |
1086 | 0 | return_error(gs_error_rangecheck); |
1087 | 0 | } |
1088 | 0 | if (alpha == 255) { /* Just write the new color. */ |
1089 | 0 | composite = color; |
1090 | 0 | } else { |
1091 | 0 | if (previous == gx_no_color_index) { /* Extract the old color. */ |
1092 | 0 | const byte *src = line + (rx * (bpp >> 3)); |
1093 | 0 | previous = 0; |
1094 | 0 | previous += (gx_color_index) * src++ << 24; |
1095 | 0 | previous += (gx_color_index) * src++ << 16; |
1096 | 0 | previous += (gx_color_index) * src++ << 8; |
1097 | 0 | previous += *src++; |
1098 | 0 | } |
1099 | 0 | if (alpha == 0) { /* Just write the old color. */ |
1100 | 0 | composite = previous; |
1101 | 0 | } else { /* Blend values. */ |
1102 | 0 | gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS]; |
1103 | 0 | int i; |
1104 | 0 | uint32_t old_coverage; |
1105 | 0 | uint32_t new_coverage; |
1106 | |
|
1107 | 0 | (*dev_proc(dev, decode_color)) (dev, previous, cv); |
1108 | | /* decode color doesn't give us coverage */ |
1109 | 0 | cv[3] = previous & 0xff; |
1110 | 0 | old_coverage = 255 - cv[3]; |
1111 | 0 | new_coverage = |
1112 | 0 | (255 * alpha + old_coverage * (255 - alpha)) / 255; |
1113 | 0 | for (i=0; i<ncomps; i++) |
1114 | 0 | cv[i] = min(((255 * alpha * color_cv[i]) + |
1115 | 0 | (old_coverage * (255 - alpha ) * cv[i])) |
1116 | 0 | / (new_coverage * 255), gx_max_color_value); |
1117 | 0 | composite = |
1118 | 0 | (*dev_proc(dev, encode_color)) (dev, cv); |
1119 | | /* encode color doesn't include coverage */ |
1120 | 0 | composite |= (255 - new_coverage) & 0xff; |
1121 | | |
1122 | | /* composite can never be gx_no_color_index |
1123 | | * because pixel is never completely transparent |
1124 | | * (low byte != 0xff). |
1125 | | */ |
1126 | 0 | } |
1127 | 0 | } |
1128 | 0 | if (sizeof(composite) > 4) { |
1129 | 0 | if (sample_store_next64(composite, &l_dptr, &l_dbit, bpp, &l_dbyte) < 0) |
1130 | 0 | return_error(gs_error_rangecheck); |
1131 | 0 | } |
1132 | 0 | else { |
1133 | 0 | if (sample_store_next32(composite, &l_dptr, &l_dbit, bpp, &l_dbyte) < 0) |
1134 | 0 | return_error(gs_error_rangecheck); |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 | if ( rx > l_xprev ) { |
1138 | 0 | sample_store_flush(l_dptr, l_dbit, l_dbyte); |
1139 | 0 | code = (*dev_proc(dev, copy_color)) |
1140 | 0 | (dev, lout, l_xprev - x, raster, |
1141 | 0 | gx_no_bitmap_id, l_xprev, ry, rx - l_xprev, 1); |
1142 | 0 | if (code < 0) |
1143 | 0 | return code; |
1144 | 0 | } |
1145 | 0 | } |
1146 | 0 | out:gs_free_object(mem, lout, "copy_alpha(lout)"); |
1147 | 0 | gs_free_object(mem, lin, "copy_alpha(lin)"); |
1148 | 0 | return code; |
1149 | 0 | } |
1150 | 0 | } |
1151 | | |
1152 | | static int |
1153 | | pngalpha_spec_op(gx_device* pdev, int dso, void* ptr, int size) |
1154 | 0 | { |
1155 | 0 | switch (dso) |
1156 | 0 | { |
1157 | 0 | case gxdso_supports_alpha: |
1158 | 0 | return 1; |
1159 | 0 | } |
1160 | | |
1161 | 0 | return gdev_prn_dev_spec_op(pdev, dso, ptr, size); |
1162 | 0 | } |