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