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