/src/ghostpdl/gpdl/pngtop.c
Line | Count | Source |
1 | | /* Copyright (C) 2019-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 | | /* pngtop.c */ |
17 | | /* Top-level API implementation of "PNG" Language Interface */ |
18 | | |
19 | | #include "pltop.h" |
20 | | #include "gserrors.h" |
21 | | #include "gxdevice.h" |
22 | | #include "gsstate.h" |
23 | | #include "strimpl.h" |
24 | | #include "gscoord.h" |
25 | | #include "gsicc_manage.h" |
26 | | #include "gspaint.h" |
27 | | #include "plmain.h" |
28 | | #include "png_.h" |
29 | | #include "setjmp_.h" |
30 | | |
31 | | /* Forward decls */ |
32 | | |
33 | | /************************************************************/ |
34 | | /******** Language wrapper implementation (see pltop.h) *****/ |
35 | | /************************************************************/ |
36 | | |
37 | | typedef enum |
38 | | { |
39 | | ii_state_identifying = 0, |
40 | | ii_state_png, |
41 | | ii_state_png_decode, |
42 | | ii_state_flush |
43 | | } ii_state; |
44 | | |
45 | | /* |
46 | | * Png interpreter instance |
47 | | */ |
48 | | typedef struct png_interp_instance_s { |
49 | | gs_memory_t *memory; |
50 | | gs_memory_t *cmemory; |
51 | | gx_device *dev; |
52 | | gx_device *nulldev; |
53 | | |
54 | | gs_color_space *gray; |
55 | | gs_color_space *rgb; |
56 | | |
57 | | /* Png parser state machine */ |
58 | | ii_state state; |
59 | | |
60 | | int pages; |
61 | | |
62 | | uint8_t bpp; |
63 | | uint8_t cs; |
64 | | uint32_t width; |
65 | | uint32_t height; |
66 | | uint32_t xresolution; |
67 | | uint32_t yresolution; |
68 | | int interlaced; |
69 | | |
70 | | uint32_t num_comps; |
71 | | uint32_t byte_width; |
72 | | uint32_t y; |
73 | | uint32_t passes; |
74 | | |
75 | | uint32_t bytes_available_on_entry; |
76 | | |
77 | | gs_image_t image; |
78 | | gs_image_enum *penum; |
79 | | gs_gstate *pgs; |
80 | | |
81 | | png_structp png; |
82 | | png_infop png_info; |
83 | | size_t buffer_full; |
84 | | size_t buffer_max; |
85 | | byte *buffer; |
86 | | size_t file_pos; |
87 | | |
88 | | byte *samples; |
89 | | |
90 | | } png_interp_instance_t; |
91 | | |
92 | | static int |
93 | | png_detect_language(const char *s, int len) |
94 | 17.7k | { |
95 | 17.7k | const byte *hdr = (const byte *)s; |
96 | 17.7k | if (len >= 8) { |
97 | 17.3k | if (hdr[0] == 137 && |
98 | 6 | hdr[1] == 80 && |
99 | 0 | hdr[2] == 78 && |
100 | 0 | hdr[3] == 71 && |
101 | 0 | hdr[4] == 13 && |
102 | 0 | hdr[5] == 10 && |
103 | 0 | hdr[6] == 26 && |
104 | 0 | hdr[7] == 10) |
105 | 0 | return 100; |
106 | 17.3k | } |
107 | | |
108 | 17.7k | return 0; |
109 | 17.7k | } |
110 | | |
111 | | static const pl_interp_characteristics_t png_characteristics = { |
112 | | "PNG", |
113 | | png_detect_language, |
114 | | }; |
115 | | |
116 | | /* Get implementation's characteristics */ |
117 | | static const pl_interp_characteristics_t * /* always returns a descriptor */ |
118 | | png_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */ |
119 | 37.5k | { |
120 | 37.5k | return &png_characteristics; |
121 | 37.5k | } |
122 | | |
123 | | static void |
124 | | png_deallocate(png_interp_instance_t *png) |
125 | 8.09k | { |
126 | 8.09k | if (png == NULL) |
127 | 0 | return; |
128 | | |
129 | 8.09k | rc_decrement_cs(png->gray, "png_deallocate"); |
130 | 8.09k | rc_decrement_cs(png->rgb, "png_deallocate"); |
131 | | |
132 | 8.09k | if (png->pgs != NULL) |
133 | 8.09k | gs_gstate_free_chain(png->pgs); |
134 | 8.09k | gs_free_object(png->memory, png, "png_impl_allocate_interp_instance"); |
135 | 8.09k | } |
136 | | |
137 | | /* Deallocate a interpreter instance */ |
138 | | static int |
139 | | png_impl_deallocate_interp_instance(pl_interp_implementation_t *impl) |
140 | 8.09k | { |
141 | 8.09k | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
142 | | |
143 | 8.09k | png_deallocate(png); |
144 | 8.09k | impl->interp_client_data = NULL; |
145 | | |
146 | 8.09k | return 0; |
147 | 8.09k | } |
148 | | |
149 | | /* Do per-instance interpreter allocation/init. */ |
150 | | static int |
151 | | png_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem) |
152 | 8.09k | { |
153 | 8.09k | int code; |
154 | 8.09k | png_interp_instance_t *png |
155 | 8.09k | = (png_interp_instance_t *)gs_alloc_bytes(mem, |
156 | 8.09k | sizeof(png_interp_instance_t), |
157 | 8.09k | "png_impl_allocate_interp_instance"); |
158 | 8.09k | if (!png) |
159 | 0 | return_error(gs_error_VMerror); |
160 | 8.09k | memset(png, 0, sizeof(*png)); |
161 | | |
162 | 8.09k | png->memory = mem; |
163 | 8.09k | png->pgs = gs_gstate_alloc(mem); |
164 | 8.09k | if (png->pgs == NULL) |
165 | 0 | goto failVM; |
166 | | |
167 | | /* Push one save level onto the stack to assuage the memory handling */ |
168 | 8.09k | code = gs_gsave(png->pgs); |
169 | 8.09k | if (code < 0) |
170 | 0 | goto fail; |
171 | | |
172 | 8.09k | code = gsicc_init_iccmanager(png->pgs); |
173 | 8.09k | if (code < 0) |
174 | 0 | goto fail; |
175 | | |
176 | 8.09k | png->gray = gs_cspace_new_ICC(mem, png->pgs, 1); |
177 | 8.09k | png->rgb = gs_cspace_new_ICC(mem, png->pgs, 3); |
178 | | |
179 | 8.09k | impl->interp_client_data = png; |
180 | | |
181 | 8.09k | return 0; |
182 | | |
183 | 0 | failVM: |
184 | 0 | code = gs_note_error(gs_error_VMerror); |
185 | 0 | fail: |
186 | 0 | (void)png_deallocate(png); |
187 | 0 | return code; |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * Get the allocator with which to allocate a device |
192 | | */ |
193 | | static gs_memory_t * |
194 | | png_impl_get_device_memory(pl_interp_implementation_t *impl) |
195 | 0 | { |
196 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
197 | |
|
198 | 0 | return png->dev ? png->dev->memory : NULL; |
199 | 0 | } |
200 | | |
201 | | /* Prepare interp instance for the next "job" */ |
202 | | static int |
203 | | png_impl_init_job(pl_interp_implementation_t *impl, |
204 | | gx_device *device) |
205 | 0 | { |
206 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
207 | |
|
208 | 0 | png->dev = device; |
209 | 0 | png->state = ii_state_identifying; |
210 | |
|
211 | 0 | return 0; |
212 | 0 | } |
213 | | |
214 | | /* Do any setup for parser per-cursor */ |
215 | | static int /* ret 0 or +ve if ok, else -ve error code */ |
216 | | png_impl_process_begin(pl_interp_implementation_t * impl) |
217 | 0 | { |
218 | 0 | return 0; |
219 | 0 | } |
220 | | |
221 | | /* Ensure we have 'required' bytes to read, and further ensure |
222 | | * that we have no UEL's within those bytes. */ |
223 | | static int |
224 | | ensure_bytes(png_interp_instance_t *png, stream_cursor_read *pr, int required) |
225 | 0 | { |
226 | 0 | int n; |
227 | 0 | const uint8_t *p = pr->ptr+1; |
228 | 0 | const uint8_t *q; |
229 | 0 | int avail; |
230 | | |
231 | | /* Find out how many bytes we need to check */ |
232 | 0 | n = pr->limit - pr->ptr; |
233 | 0 | if (n > required) |
234 | 0 | n = required; |
235 | | |
236 | | /* Make sure there are no UELs in that block */ |
237 | 0 | q = p + n; |
238 | 0 | while (p != q) { |
239 | 0 | while (p != q && *p != '\033') |
240 | 0 | p++; |
241 | 0 | if (p == q) |
242 | 0 | break; |
243 | 0 | avail = pr->limit - pr->ptr; |
244 | 0 | if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) { |
245 | | /* At least a partial match to a UEL */ |
246 | 0 | return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit; |
247 | 0 | } |
248 | 0 | p++; |
249 | 0 | } |
250 | | |
251 | | /* If we have enough bytes, great, if not, get some more */ |
252 | 0 | return (n < required) ? gs_error_NeedInput : 0; |
253 | 0 | } |
254 | | |
255 | | static int |
256 | | flush_to_uel(stream_cursor_read *pr) |
257 | 0 | { |
258 | 0 | const uint8_t *p = pr->ptr+1; |
259 | 0 | const uint8_t *q = pr->limit+1; |
260 | 0 | int avail; |
261 | |
|
262 | 0 | while (p != q) { |
263 | 0 | while (p != q && *p != '\033') |
264 | 0 | p++; |
265 | 0 | if (p == q) |
266 | 0 | break; |
267 | 0 | avail = pr->limit - pr->ptr; |
268 | 0 | if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) { |
269 | | /* At least a partial match to a UEL. Bin everything to |
270 | | * the start of the match. */ |
271 | 0 | pr->ptr = p-1; |
272 | 0 | if (avail == 9) /* Complete match. Exit! */ |
273 | 0 | return gs_error_InterpreterExit; |
274 | | /* Partial match. Get more data. */ |
275 | 0 | return gs_error_NeedInput; |
276 | 0 | } |
277 | 0 | p++; |
278 | 0 | } |
279 | | |
280 | 0 | pr->ptr = pr->limit; |
281 | |
|
282 | 0 | return 0; |
283 | 0 | } |
284 | | |
285 | | static int |
286 | | bytes_until_uel(const stream_cursor_read *pr) |
287 | 0 | { |
288 | 0 | const uint8_t *p = pr->ptr+1; |
289 | 0 | const uint8_t *q = pr->limit+1; |
290 | 0 | int avail; |
291 | |
|
292 | 0 | while (p != q) { |
293 | 0 | while (p != q && *p != '\033') |
294 | 0 | p++; |
295 | 0 | if (p == q) |
296 | 0 | break; |
297 | 0 | avail = q - p; |
298 | 0 | if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) { |
299 | | /* At least a partial match to a UEL. Everything up to |
300 | | * the start of the match is up for grabs. */ |
301 | 0 | return p - (pr->ptr+1); |
302 | 0 | } |
303 | 0 | p++; |
304 | 0 | } |
305 | | |
306 | 0 | return pr->limit - pr->ptr; |
307 | 0 | } |
308 | | |
309 | | OPTIMIZE_SETJMP |
310 | | static void |
311 | | my_png_error(png_structp png_ptr, png_const_charp error_msg) |
312 | 0 | { |
313 | | /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */ |
314 | |
|
315 | 0 | png_longjmp(png_ptr, 1); |
316 | 0 | } |
317 | | |
318 | | static void |
319 | | my_png_warning(png_structp png_ptr, png_const_charp warning_msg) |
320 | 0 | { |
321 | | /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */ |
322 | 0 | } |
323 | | |
324 | | static png_voidp |
325 | | my_png_malloc(png_structp png_ptr, png_alloc_size_t size) |
326 | 0 | { |
327 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr); |
328 | |
|
329 | 0 | if (sizeof(void *) == 8) { |
330 | | /* gs_alloc_bytes returns blocks aligned to 8 on 64bit platforms. |
331 | | * PNG (on Windows at least) requires blocks aligned to 16. */ |
332 | 0 | unsigned char *block = gs_alloc_bytes(png->memory, size+16, "my_png_malloc"); |
333 | 0 | intptr_t num_bytes_padded; |
334 | |
|
335 | 0 | if (block == NULL) |
336 | 0 | return NULL; |
337 | 0 | num_bytes_padded = 16-(((intptr_t)block) & 15); |
338 | 0 | block += num_bytes_padded; |
339 | 0 | block[-1] = num_bytes_padded; |
340 | |
|
341 | 0 | return block; |
342 | 0 | } |
343 | 0 | return gs_alloc_bytes(png->memory, size, "my_png_malloc"); |
344 | 0 | } |
345 | | |
346 | | static void |
347 | | my_png_free(png_structp png_ptr, png_voidp ptr) |
348 | 0 | { |
349 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr); |
350 | |
|
351 | 0 | if (sizeof(void *) == 8) { |
352 | 0 | unsigned char *block = ptr; |
353 | 0 | if (block == NULL) |
354 | 0 | return; |
355 | 0 | block -= block[-1]; |
356 | 0 | ptr = (void *)block; |
357 | 0 | } |
358 | 0 | gs_free_object(png->memory, ptr, "my_png_free"); |
359 | 0 | } |
360 | | |
361 | | static void |
362 | | my_png_read(png_structp png_ptr, png_bytep data, png_size_t length) |
363 | 0 | { |
364 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)png_get_io_ptr(png_ptr); |
365 | 0 | if (length + png->file_pos > png->buffer_full) |
366 | 0 | png_error(png_ptr, "Overread!"); |
367 | | |
368 | 0 | memcpy(data, &png->buffer[png->file_pos], length); |
369 | 0 | png->file_pos += length; |
370 | 0 | } |
371 | | |
372 | | OPTIMIZE_SETJMP |
373 | | static int |
374 | | do_impl_process(png_interp_instance_t *png, stream_cursor_read * pr, bool eof) |
375 | 0 | { |
376 | 0 | int code = 0; |
377 | 0 | ii_state ostate; |
378 | 0 | size_t bytes_in; |
379 | 0 | int advanced; |
380 | | |
381 | | /* Loop until we stop 'advancing'. */ |
382 | 0 | do |
383 | 0 | { |
384 | 0 | ostate = png->state; |
385 | 0 | bytes_in = pr->limit - pr->ptr; |
386 | 0 | advanced = 0; |
387 | 0 | switch(png->state) |
388 | 0 | { |
389 | 0 | case ii_state_identifying: |
390 | 0 | { |
391 | 0 | const byte *hdr; |
392 | | /* Try and get us 8 bytes */ |
393 | 0 | code = ensure_bytes(png, pr, 8); |
394 | 0 | if (code < 0) |
395 | 0 | return code; |
396 | 0 | hdr = pr->ptr+1; |
397 | 0 | if (hdr[0] == 137 && |
398 | 0 | hdr[1] == 80 && |
399 | 0 | hdr[2] == 78 && |
400 | 0 | hdr[3] == 71 && |
401 | 0 | hdr[4] == 13 && |
402 | 0 | hdr[5] == 10 && |
403 | 0 | hdr[6] == 26 && |
404 | 0 | hdr[7] == 10) { |
405 | 0 | png->state = ii_state_png; |
406 | 0 | break; |
407 | 0 | } |
408 | 0 | png->state = ii_state_flush; |
409 | 0 | break; |
410 | 0 | } |
411 | 0 | case ii_state_png: |
412 | 0 | { |
413 | | /* Gather data into a buffer */ |
414 | 0 | int bytes = bytes_until_uel(pr); |
415 | |
|
416 | 0 | if (bytes == 0 && pr->limit - pr->ptr > 9) { |
417 | | /* No bytes until UEL, and there is space for a UEL in the buffer */ |
418 | 0 | png->state = ii_state_png_decode; |
419 | 0 | png->file_pos = 0; |
420 | 0 | break; |
421 | 0 | } |
422 | 0 | if (bytes == 0 && eof) { |
423 | | /* No bytes until UEL, and we are at eof */ |
424 | 0 | png->state = ii_state_png_decode; |
425 | 0 | png->file_pos = 0; |
426 | 0 | break; |
427 | 0 | } |
428 | | |
429 | 0 | if (png->buffer_full + bytes > png->buffer_max) { |
430 | | /* Need to expand our buffer */ |
431 | 0 | size_t proposed = png->buffer_full*2; |
432 | 0 | if (proposed == 0) |
433 | 0 | proposed = 32768; |
434 | 0 | while (proposed < png->buffer_full + bytes) |
435 | 0 | proposed *= 2; |
436 | |
|
437 | 0 | if (png->buffer == NULL) { |
438 | 0 | png->buffer = gs_alloc_bytes(png->memory, proposed, "png_buffer"); |
439 | 0 | if (png->buffer == NULL) { |
440 | 0 | png->state = ii_state_flush; |
441 | 0 | break; |
442 | 0 | } |
443 | 0 | } else { |
444 | 0 | void *new_buf = gs_resize_object(png->memory, png->buffer, proposed, "png_buffer"); |
445 | 0 | if (new_buf == NULL) { |
446 | 0 | png->state = ii_state_flush; |
447 | 0 | break; |
448 | 0 | } |
449 | 0 | png->buffer = new_buf; |
450 | 0 | } |
451 | 0 | png->buffer_max = proposed; |
452 | 0 | } |
453 | | |
454 | 0 | memcpy(&png->buffer[png->buffer_full], pr->ptr+1, bytes); |
455 | 0 | png->buffer_full += bytes; |
456 | 0 | pr->ptr += bytes; |
457 | 0 | break; |
458 | 0 | } |
459 | 0 | case ii_state_png_decode: |
460 | 0 | { |
461 | 0 | gs_color_space *cs; |
462 | 0 | float scale, xext, yext, xoffset, yoffset; |
463 | |
|
464 | 0 | png->png = png_create_read_struct_2( |
465 | 0 | PNG_LIBPNG_VER_STRING, |
466 | 0 | (png_voidp)png, |
467 | 0 | my_png_error, |
468 | 0 | my_png_warning, |
469 | 0 | (png_voidp)png, |
470 | 0 | my_png_malloc, |
471 | 0 | my_png_free); |
472 | 0 | if (png->png == NULL) { |
473 | 0 | png->state = ii_state_flush; |
474 | 0 | break; |
475 | 0 | } |
476 | | |
477 | 0 | png->png_info = png_create_info_struct(png->png); |
478 | 0 | if (!png->png_info) { |
479 | 0 | png->state = ii_state_flush; |
480 | 0 | break; |
481 | 0 | } |
482 | | |
483 | 0 | png_set_read_fn(png->png, png, my_png_read); |
484 | 0 | png_set_alpha_mode(png->png, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); |
485 | |
|
486 | 0 | if (setjmp(png_jmpbuf(png->png))) { |
487 | 0 | png->state = ii_state_flush; |
488 | 0 | break; |
489 | 0 | } |
490 | | |
491 | | /* We use the "low level" interface to libpng to allow us to |
492 | | * optimise memory usage. Any errors will longjmp. */ |
493 | 0 | png_read_info(png->png, png->png_info); |
494 | |
|
495 | 0 | png_set_expand_16(png->png); |
496 | 0 | png_set_alpha_mode(png->png, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); |
497 | 0 | { |
498 | 0 | int bpc, color; |
499 | 0 | static const png_color_16 bg = { 0, 65535, 65535, 65535, 65535 }; |
500 | 0 | png_get_IHDR(png->png, png->png_info, |
501 | 0 | &png->width, |
502 | 0 | &png->height, |
503 | 0 | &bpc, |
504 | 0 | &color, |
505 | 0 | &png->interlaced, |
506 | 0 | NULL /* compression */, |
507 | 0 | NULL /* filter */); |
508 | 0 | switch (color) { |
509 | 0 | case PNG_COLOR_TYPE_GRAY_ALPHA: |
510 | 0 | case PNG_COLOR_TYPE_GRAY: |
511 | 0 | png->num_comps = 1; |
512 | 0 | break; |
513 | 0 | case PNG_COLOR_TYPE_PALETTE: |
514 | 0 | png_set_palette_to_rgb(png->png); |
515 | 0 | png->num_comps = 3; |
516 | 0 | break; |
517 | 0 | case PNG_COLOR_TYPE_RGB: |
518 | 0 | case PNG_COLOR_TYPE_RGB_ALPHA: |
519 | 0 | png->num_comps = 3; |
520 | 0 | break; |
521 | 0 | default: |
522 | 0 | png->state = ii_state_flush; |
523 | 0 | png_longjmp(png->png, 1); |
524 | 0 | break; |
525 | 0 | } |
526 | 0 | png->passes = png_set_interlace_handling(png->png); |
527 | 0 | png_set_background(png->png, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1); |
528 | 0 | } |
529 | | |
530 | 0 | png->xresolution = png_get_x_pixels_per_inch(png->png, png->png_info); |
531 | 0 | png->yresolution = png_get_y_pixels_per_inch(png->png, png->png_info); |
532 | 0 | if (png->xresolution == 0) |
533 | 0 | png->xresolution = png->yresolution; |
534 | 0 | if (png->yresolution == 0) |
535 | 0 | png->yresolution = png->xresolution; |
536 | 0 | if (png->xresolution == 0) |
537 | 0 | png->xresolution = png->yresolution = 72; |
538 | |
|
539 | 0 | cs = (png->num_comps == 1 ? png->gray : png->rgb); |
540 | | |
541 | | /* Read the updated info */ |
542 | 0 | png_read_update_info(png->png, png->png_info); |
543 | 0 | png->bpp = png_get_bit_depth(png->png, png->png_info) * png->num_comps; |
544 | | |
545 | | /* Scale to fit, if too large. */ |
546 | 0 | scale = 1.0f; |
547 | 0 | if (png->width * png->dev->HWResolution[0] > png->dev->width * png->xresolution) |
548 | 0 | scale = ((float)png->dev->width * png->xresolution) / (png->width * png->dev->HWResolution[0]); |
549 | 0 | if (scale * png->height * png->dev->HWResolution[1] > png->dev->height * png->yresolution) |
550 | 0 | scale = ((float)png->dev->height * png->yresolution) / (png->height * png->dev->HWResolution[1]); |
551 | | |
552 | | /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */ |
553 | 0 | xext = ((float)png->width * 72 * scale / png->xresolution); |
554 | 0 | xoffset = (png->dev->width * 72 / png->dev->HWResolution[0] - xext)/2; |
555 | 0 | yext = ((float)png->height * 72 * scale / png->yresolution); |
556 | 0 | yoffset = (png->dev->height * 72 / png->dev->HWResolution[1] - yext)/2; |
557 | | |
558 | | /* Now we've got the data from the image header, we can |
559 | | * make the gs image instance */ |
560 | 0 | png->byte_width = png_get_rowbytes(png->png, png->png_info); |
561 | |
|
562 | 0 | png->nulldev = gs_currentdevice(png->pgs); |
563 | 0 | rc_increment(png->nulldev); |
564 | 0 | code = gs_setdevice_no_erase(png->pgs, png->dev); |
565 | 0 | if (code < 0) { |
566 | 0 | png->state = ii_state_flush; |
567 | 0 | break; |
568 | 0 | } |
569 | 0 | gs_initmatrix(png->pgs); |
570 | | |
571 | | /* By default the ctm is set to: |
572 | | * xres/72 0 |
573 | | * 0 -yres/72 |
574 | | * 0 dev->height * yres/72 |
575 | | * i.e. it moves the origin from being top right to being bottom left. |
576 | | * We want to move it back, as without this, the image will be displayed |
577 | | * upside down. |
578 | | */ |
579 | 0 | code = gs_translate(png->pgs, 0.0, png->dev->height * 72 / png->dev->HWResolution[1]); |
580 | 0 | if (code >= 0) |
581 | 0 | code = gs_translate(png->pgs, xoffset, -yoffset); |
582 | 0 | if (code >= 0) |
583 | 0 | code = gs_scale(png->pgs, scale, -scale); |
584 | | /* At this point, the ctm is set to: |
585 | | * xres/72 0 |
586 | | * 0 yres/72 |
587 | | * 0 0 |
588 | | */ |
589 | 0 | if (code >= 0) |
590 | 0 | code = gs_erasepage(png->pgs); |
591 | 0 | if (code < 0) { |
592 | 0 | png->state = ii_state_flush; |
593 | 0 | break; |
594 | 0 | } |
595 | | |
596 | 0 | if (SIZE_MAX / png->byte_width < (png->interlaced ? png->height : 1)) |
597 | 0 | { |
598 | 0 | code = gs_note_error(gs_error_VMerror); |
599 | 0 | png->state = ii_state_flush; |
600 | 0 | break; |
601 | 0 | } |
602 | | |
603 | 0 | png->samples = gs_alloc_bytes(png->memory, |
604 | 0 | (size_t)png->byte_width * (png->interlaced ? png->height : 1), |
605 | 0 | "png_impl_process(samples)"); |
606 | 0 | if (png->samples == NULL) { |
607 | 0 | png->state = ii_state_flush; |
608 | 0 | break; |
609 | 0 | } |
610 | | |
611 | 0 | memset(&png->image, 0, sizeof(png->image)); |
612 | 0 | gs_image_t_init(&png->image, cs); |
613 | 0 | png->image.BitsPerComponent = png->bpp/png->num_comps; |
614 | 0 | png->image.Width = png->width; |
615 | 0 | png->image.Height = png->height; |
616 | |
|
617 | 0 | png->image.ImageMatrix.xx = png->xresolution / 72.0f; |
618 | 0 | png->image.ImageMatrix.yy = png->yresolution / 72.0f; |
619 | |
|
620 | 0 | png->penum = gs_image_enum_alloc(png->memory, "png_impl_process(penum)"); |
621 | 0 | if (png->penum == NULL) { |
622 | 0 | code = gs_note_error(gs_error_VMerror); |
623 | 0 | png->state = ii_state_flush; |
624 | 0 | return code; |
625 | 0 | } |
626 | | |
627 | 0 | code = gs_image_init(png->penum, |
628 | 0 | &png->image, |
629 | 0 | false, |
630 | 0 | false, |
631 | 0 | png->pgs); |
632 | 0 | if (code < 0) { |
633 | 0 | png->state = ii_state_flush; |
634 | 0 | return code; |
635 | 0 | } |
636 | | |
637 | 0 | { |
638 | 0 | int i, j; |
639 | |
|
640 | 0 | if (png->interlaced) { |
641 | | /* Collect the results from all but the last pass */ |
642 | 0 | for (j = png->passes-1; j > 0; j--) |
643 | 0 | for (i = 0; i < png->height; i++) |
644 | 0 | png_read_row(png->png, &png->samples[i*png->byte_width], NULL); |
645 | | |
646 | | /* And actually process the last pass */ |
647 | 0 | for (i = 0; i < png->height; i++) { |
648 | 0 | uint used; |
649 | |
|
650 | 0 | png_read_row(png->png, &png->samples[i*png->byte_width], NULL); |
651 | |
|
652 | 0 | code = gs_image_next(png->penum, &png->samples[i*png->byte_width], png->byte_width, &used); |
653 | 0 | if (code < 0) { |
654 | 0 | png->state = ii_state_flush; |
655 | 0 | break; |
656 | 0 | } |
657 | 0 | } |
658 | 0 | } else { |
659 | 0 | for (i = 0; i < png->height; i++) { |
660 | 0 | uint used; |
661 | |
|
662 | 0 | png_read_row(png->png, png->samples, NULL); |
663 | |
|
664 | 0 | code = gs_image_next(png->penum, png->samples, png->byte_width, &used); |
665 | 0 | if (code < 0) { |
666 | 0 | png->state = ii_state_flush; |
667 | 0 | break; |
668 | 0 | } |
669 | 0 | } |
670 | 0 | } |
671 | 0 | } |
672 | |
|
673 | 0 | code = gs_image_cleanup_and_free_enum(png->penum, png->pgs); |
674 | 0 | png->penum = NULL; |
675 | 0 | if (code < 0) { |
676 | 0 | png->state = ii_state_flush; |
677 | 0 | break; |
678 | 0 | } |
679 | 0 | code = pl_finish_page(png->memory->gs_lib_ctx->top_of_system, |
680 | 0 | png->pgs, 1, true); |
681 | 0 | if (code < 0) { |
682 | 0 | png->state = ii_state_flush; |
683 | 0 | break; |
684 | 0 | } |
685 | | |
686 | 0 | png->state = ii_state_flush; |
687 | 0 | break; |
688 | 0 | } |
689 | 0 | default: |
690 | 0 | case ii_state_flush: |
691 | 0 | if (png->png) |
692 | 0 | { |
693 | 0 | png_destroy_read_struct(&png->png, &png->png_info, NULL); |
694 | 0 | png->png = NULL; |
695 | 0 | png->png_info = NULL; |
696 | 0 | } |
697 | |
|
698 | 0 | if (png->penum) { |
699 | 0 | (void)gs_image_cleanup_and_free_enum(png->penum, png->pgs); |
700 | 0 | png->penum = NULL; |
701 | 0 | } |
702 | |
|
703 | 0 | gs_free_object(png->memory, png->buffer, "png_impl_process(buffer)"); |
704 | 0 | png->buffer = NULL; |
705 | 0 | gs_free_object(png->memory, png->samples, "png_impl_process(samples)"); |
706 | 0 | png->samples = NULL; |
707 | | /* We want to bin any data we get up to, but not including |
708 | | * a UEL. */ |
709 | 0 | return flush_to_uel(pr); |
710 | 0 | } |
711 | 0 | advanced |= (ostate != png->state); |
712 | 0 | advanced |= (bytes_in != pr->limit - pr->ptr); |
713 | 0 | } while (advanced); |
714 | | |
715 | 0 | return code; |
716 | 0 | } |
717 | | |
718 | | static int |
719 | | png_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) |
720 | 0 | { |
721 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
722 | |
|
723 | 0 | return do_impl_process(png, pr, 0); |
724 | 0 | } |
725 | | |
726 | | static int |
727 | | png_impl_process_end(pl_interp_implementation_t * impl) |
728 | 0 | { |
729 | 0 | return 0; |
730 | 0 | } |
731 | | |
732 | | /* Not implemented */ |
733 | | static int |
734 | | png_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor) |
735 | 0 | { |
736 | 0 | const byte *p = cursor->ptr; |
737 | 0 | const byte *rlimit = cursor->limit; |
738 | | |
739 | | /* Skip to, but leave UEL in buffer for PJL to find later */ |
740 | 0 | for (; p < rlimit; ++p) |
741 | 0 | if (p[1] == '\033') { |
742 | 0 | uint avail = rlimit - p; |
743 | |
|
744 | 0 | if (memcmp(p + 1, "\033%-12345X", min(avail, 9))) |
745 | 0 | continue; |
746 | 0 | if (avail < 9) |
747 | 0 | break; |
748 | 0 | cursor->ptr = p; |
749 | 0 | return 1; /* found eoj */ |
750 | 0 | } |
751 | 0 | cursor->ptr = p; |
752 | 0 | return 0; /* need more */ |
753 | 0 | } |
754 | | |
755 | | /* Parser action for end-of-file */ |
756 | | static int |
757 | | png_impl_process_eof(pl_interp_implementation_t *impl) |
758 | 0 | { |
759 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
760 | 0 | stream_cursor_read cursor; |
761 | |
|
762 | 0 | cursor.ptr = NULL; |
763 | 0 | cursor.limit = NULL; |
764 | |
|
765 | 0 | return do_impl_process(png, &cursor, 1); |
766 | 0 | } |
767 | | |
768 | | /* Report any errors after running a job */ |
769 | | static int |
770 | | png_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */ |
771 | | int code, /* prev termination status */ |
772 | | long file_position, /* file position of error, -1 if unknown */ |
773 | | bool force_to_cout /* force errors to cout */ |
774 | | ) |
775 | 0 | { |
776 | 0 | return 0; |
777 | 0 | } |
778 | | |
779 | | /* Wrap up interp instance after a "job" */ |
780 | | static int |
781 | | png_impl_dnit_job(pl_interp_implementation_t *impl) |
782 | 0 | { |
783 | 0 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
784 | |
|
785 | 0 | if (png->nulldev) { |
786 | 0 | int code = gs_setdevice(png->pgs, png->nulldev); |
787 | 0 | png->dev = NULL; |
788 | 0 | rc_decrement(png->nulldev, "png_impl_dnit_job(nulldevice)"); |
789 | 0 | png->nulldev = NULL; |
790 | 0 | return code; |
791 | 0 | } |
792 | 0 | return 0; |
793 | 0 | } |
794 | | |
795 | | /* Parser implementation descriptor */ |
796 | | const pl_interp_implementation_t png_implementation = { |
797 | | png_impl_characteristics, |
798 | | png_impl_allocate_interp_instance, |
799 | | png_impl_get_device_memory, |
800 | | NULL, /* png_impl_set_param */ |
801 | | NULL, /* png_impl_add_path */ |
802 | | NULL, /* png_impl_post_args_init */ |
803 | | png_impl_init_job, |
804 | | NULL, /* png_impl_run_prefix_commands */ |
805 | | NULL, /* png_impl_process_file */ |
806 | | png_impl_process_begin, |
807 | | png_impl_process, |
808 | | png_impl_process_end, |
809 | | png_impl_flush_to_eoj, |
810 | | png_impl_process_eof, |
811 | | png_impl_report_errors, |
812 | | png_impl_dnit_job, |
813 | | png_impl_deallocate_interp_instance, |
814 | | NULL, /* png_impl_reset */ |
815 | | NULL /* interp_client_data */ |
816 | | }; |