/src/ghostpdl/gpdl/pngtop.c
Line | Count | Source |
1 | | /* Copyright (C) 2019-2026 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 | 22.7k | { |
95 | 22.7k | const byte *hdr = (const byte *)s; |
96 | 22.7k | if (len >= 8) { |
97 | 22.2k | if (hdr[0] == 137 && |
98 | 371 | hdr[1] == 80 && |
99 | 364 | hdr[2] == 78 && |
100 | 361 | hdr[3] == 71 && |
101 | 360 | hdr[4] == 13 && |
102 | 359 | hdr[5] == 10 && |
103 | 359 | hdr[6] == 26 && |
104 | 358 | hdr[7] == 10) |
105 | 357 | return 100; |
106 | 22.2k | } |
107 | | |
108 | 22.3k | return 0; |
109 | 22.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 | 48.4k | { |
120 | 48.4k | return &png_characteristics; |
121 | 48.4k | } |
122 | | |
123 | | static void |
124 | | png_deallocate(png_interp_instance_t *png) |
125 | 11.0k | { |
126 | 11.0k | if (png == NULL) |
127 | 0 | return; |
128 | | |
129 | 11.0k | rc_decrement_cs(png->gray, "png_deallocate"); |
130 | 11.0k | rc_decrement_cs(png->rgb, "png_deallocate"); |
131 | | |
132 | 11.0k | if (png->pgs != NULL) |
133 | 11.0k | gs_gstate_free_chain(png->pgs); |
134 | 11.0k | gs_free_object(png->memory, png, "png_impl_allocate_interp_instance"); |
135 | 11.0k | } |
136 | | |
137 | | /* Deallocate a interpreter instance */ |
138 | | static int |
139 | | png_impl_deallocate_interp_instance(pl_interp_implementation_t *impl) |
140 | 11.0k | { |
141 | 11.0k | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
142 | | |
143 | 11.0k | png_deallocate(png); |
144 | 11.0k | impl->interp_client_data = NULL; |
145 | | |
146 | 11.0k | return 0; |
147 | 11.0k | } |
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 | 11.0k | { |
153 | 11.0k | int code; |
154 | 11.0k | png_interp_instance_t *png |
155 | 11.0k | = (png_interp_instance_t *)gs_alloc_bytes(mem, |
156 | 11.0k | sizeof(png_interp_instance_t), |
157 | 11.0k | "png_impl_allocate_interp_instance"); |
158 | 11.0k | if (!png) |
159 | 0 | return_error(gs_error_VMerror); |
160 | 11.0k | memset(png, 0, sizeof(*png)); |
161 | | |
162 | 11.0k | png->memory = mem; |
163 | 11.0k | png->pgs = gs_gstate_alloc(mem); |
164 | 11.0k | if (png->pgs == NULL) |
165 | 0 | goto failVM; |
166 | | |
167 | | /* Push one save level onto the stack to assuage the memory handling */ |
168 | 11.0k | code = gs_gsave(png->pgs); |
169 | 11.0k | if (code < 0) |
170 | 0 | goto fail; |
171 | | |
172 | 11.0k | code = gsicc_init_iccmanager(png->pgs); |
173 | 11.0k | if (code < 0) |
174 | 0 | goto fail; |
175 | | |
176 | 11.0k | png->gray = gs_cspace_new_ICC(mem, png->pgs, 1); |
177 | 11.0k | png->rgb = gs_cspace_new_ICC(mem, png->pgs, 3); |
178 | | |
179 | 11.0k | impl->interp_client_data = png; |
180 | | |
181 | 11.0k | 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 | 350 | { |
206 | 350 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
207 | | |
208 | 350 | png->dev = device; |
209 | 350 | png->state = ii_state_identifying; |
210 | | |
211 | 350 | return 0; |
212 | 350 | } |
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 | 350 | { |
218 | 350 | return 0; |
219 | 350 | } |
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 | 350 | { |
226 | 350 | int n; |
227 | 350 | const uint8_t *p = pr->ptr+1; |
228 | 350 | const uint8_t *q; |
229 | 350 | int avail; |
230 | | |
231 | | /* Find out how many bytes we need to check */ |
232 | 350 | n = pr->limit - pr->ptr; |
233 | 350 | if (n > required) |
234 | 349 | n = required; |
235 | | |
236 | | /* Make sure there are no UELs in that block */ |
237 | 350 | q = p + n; |
238 | 350 | while (p != q) { |
239 | 3.15k | while (p != q && *p != '\033') |
240 | 2.80k | p++; |
241 | 350 | if (p == q) |
242 | 350 | 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 | 350 | return (n < required) ? gs_error_NeedInput : 0; |
253 | 350 | } |
254 | | |
255 | | static int |
256 | | flush_to_uel(stream_cursor_read *pr) |
257 | 350 | { |
258 | 350 | const uint8_t *p = pr->ptr+1; |
259 | 350 | const uint8_t *q = pr->limit+1; |
260 | 350 | int avail; |
261 | | |
262 | 350 | 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 | 350 | pr->ptr = pr->limit; |
281 | | |
282 | 350 | return 0; |
283 | 350 | } |
284 | | |
285 | | static int |
286 | | bytes_until_uel(const stream_cursor_read *pr) |
287 | 1.73k | { |
288 | 1.73k | const uint8_t *p = pr->ptr+1; |
289 | 1.73k | const uint8_t *q = pr->limit+1; |
290 | 1.73k | int avail; |
291 | | |
292 | 3.86k | while (p != q) { |
293 | 864k | while (p != q && *p != '\033') |
294 | 862k | p++; |
295 | 2.80k | if (p == q) |
296 | 660 | break; |
297 | 2.14k | avail = q - p; |
298 | 2.14k | 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 | 15 | return p - (pr->ptr+1); |
302 | 15 | } |
303 | 2.12k | p++; |
304 | 2.12k | } |
305 | | |
306 | 1.72k | return pr->limit - pr->ptr; |
307 | 1.73k | } |
308 | | |
309 | | OPTIMIZE_SETJMP |
310 | | static void |
311 | | my_png_error(png_structp png_ptr, png_const_charp error_msg) |
312 | 344 | { |
313 | | /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */ |
314 | | |
315 | 344 | png_longjmp(png_ptr, 1); |
316 | 344 | } |
317 | | |
318 | | static void |
319 | | my_png_warning(png_structp png_ptr, png_const_charp warning_msg) |
320 | 15.5k | { |
321 | | /* png_interp_instance_t *png = (png_interp_instance_t *)png_get_error_ptr(png_ptr); */ |
322 | 15.5k | } |
323 | | |
324 | | static png_voidp |
325 | | my_png_malloc(png_structp png_ptr, png_alloc_size_t size) |
326 | 9.98k | { |
327 | 9.98k | png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr); |
328 | | |
329 | 9.98k | 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 | 9.98k | unsigned char *block = gs_alloc_bytes(png->memory, size+16, "my_png_malloc"); |
333 | 9.98k | intptr_t num_bytes_padded; |
334 | | |
335 | 9.98k | if (block == NULL) |
336 | 0 | return NULL; |
337 | 9.98k | num_bytes_padded = 16-(((intptr_t)block) & 15); |
338 | 9.98k | block += num_bytes_padded; |
339 | 9.98k | block[-1] = num_bytes_padded; |
340 | | |
341 | 9.98k | return block; |
342 | 9.98k | } |
343 | 0 | return gs_alloc_bytes(png->memory, size, "my_png_malloc"); |
344 | 9.98k | } |
345 | | |
346 | | static void |
347 | | my_png_free(png_structp png_ptr, png_voidp ptr) |
348 | 9.98k | { |
349 | 9.98k | png_interp_instance_t *png = (png_interp_instance_t *)png_get_mem_ptr(png_ptr); |
350 | | |
351 | 9.98k | if (sizeof(void *) == 8) { |
352 | 9.98k | unsigned char *block = ptr; |
353 | 9.98k | if (block == NULL) |
354 | 0 | return; |
355 | 9.98k | block -= block[-1]; |
356 | 9.98k | ptr = (void *)block; |
357 | 9.98k | } |
358 | 9.98k | gs_free_object(png->memory, ptr, "my_png_free"); |
359 | 9.98k | } |
360 | | |
361 | | static void |
362 | | my_png_read(png_structp png_ptr, png_bytep data, png_size_t length) |
363 | 45.0k | { |
364 | 45.0k | png_interp_instance_t *png = (png_interp_instance_t *)png_get_io_ptr(png_ptr); |
365 | 45.0k | if (length + png->file_pos > png->buffer_full) |
366 | 281 | png_error(png_ptr, "Overread!"); |
367 | | |
368 | 44.7k | memcpy(data, &png->buffer[png->file_pos], length); |
369 | 44.7k | png->file_pos += length; |
370 | 44.7k | } |
371 | | |
372 | | OPTIMIZE_SETJMP |
373 | | static int |
374 | | do_impl_process(png_interp_instance_t *png, stream_cursor_read * pr, bool eof) |
375 | 1.07k | { |
376 | 1.07k | int code = 0, bytes; |
377 | 1.07k | ii_state ostate; |
378 | 1.07k | size_t bytes_in; |
379 | 1.07k | int advanced; |
380 | | |
381 | | /* Loop until we stop 'advancing'. */ |
382 | 1.07k | do |
383 | 2.78k | { |
384 | 2.78k | ostate = png->state; |
385 | 2.78k | bytes_in = pr->limit - pr->ptr; |
386 | 2.78k | advanced = 0; |
387 | 2.78k | switch(png->state) |
388 | 2.78k | { |
389 | 350 | case ii_state_identifying: |
390 | 350 | { |
391 | 350 | const byte *hdr; |
392 | | /* Try and get us 8 bytes */ |
393 | 350 | code = ensure_bytes(png, pr, 8); |
394 | 350 | if (code < 0) |
395 | 0 | return code; |
396 | 350 | hdr = pr->ptr+1; |
397 | 350 | if (hdr[0] == 137 && |
398 | 350 | hdr[1] == 80 && |
399 | 350 | hdr[2] == 78 && |
400 | 350 | hdr[3] == 71 && |
401 | 350 | hdr[4] == 13 && |
402 | 350 | hdr[5] == 10 && |
403 | 350 | hdr[6] == 26 && |
404 | 350 | hdr[7] == 10) { |
405 | 350 | png->state = ii_state_png; |
406 | 350 | break; |
407 | 350 | } |
408 | 0 | png->state = ii_state_flush; |
409 | 0 | break; |
410 | 350 | } |
411 | 1.73k | case ii_state_png: |
412 | 1.73k | { |
413 | | /* Gather data into a buffer */ |
414 | 1.73k | bytes = bytes_until_uel(pr); |
415 | | |
416 | 1.73k | 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 | 1.73k | if (bytes == 0 && eof) { |
423 | | /* No bytes until UEL, and we are at eof */ |
424 | 350 | png->state = ii_state_png_decode; |
425 | 350 | png->file_pos = 0; |
426 | 350 | break; |
427 | 350 | } |
428 | | |
429 | 1.38k | if (png->buffer_full + bytes > png->buffer_max) { |
430 | | /* Need to expand our buffer */ |
431 | 352 | size_t proposed = png->buffer_full*2; |
432 | 352 | if (proposed == 0) |
433 | 350 | proposed = 32768; |
434 | 352 | while (proposed < png->buffer_full + bytes) |
435 | 0 | proposed *= 2; |
436 | | |
437 | 352 | if (png->buffer == NULL) { |
438 | 350 | png->buffer = gs_alloc_bytes(png->memory, proposed, "png_buffer"); |
439 | 350 | if (png->buffer == NULL) { |
440 | 0 | png->state = ii_state_flush; |
441 | 0 | break; |
442 | 0 | } |
443 | 350 | } else { |
444 | 2 | void *new_buf = gs_resize_object(png->memory, png->buffer, proposed, "png_buffer"); |
445 | 2 | if (new_buf == NULL) { |
446 | 0 | png->state = ii_state_flush; |
447 | 0 | break; |
448 | 0 | } |
449 | 2 | png->buffer = new_buf; |
450 | 2 | } |
451 | 352 | png->buffer_max = proposed; |
452 | 352 | } |
453 | | |
454 | 1.38k | memcpy(&png->buffer[png->buffer_full], pr->ptr+1, bytes); |
455 | 1.38k | png->buffer_full += bytes; |
456 | 1.38k | pr->ptr += bytes; |
457 | 1.38k | break; |
458 | 1.38k | } |
459 | 350 | case ii_state_png_decode: |
460 | 350 | { |
461 | 350 | gs_color_space *cs; |
462 | 350 | float scale, xext, yext, xoffset, yoffset; |
463 | | |
464 | 350 | png->png = png_create_read_struct_2( |
465 | 350 | PNG_LIBPNG_VER_STRING, |
466 | 350 | (png_voidp)png, |
467 | 350 | my_png_error, |
468 | 350 | my_png_warning, |
469 | 350 | (png_voidp)png, |
470 | 350 | my_png_malloc, |
471 | 350 | my_png_free); |
472 | 350 | if (png->png == NULL) { |
473 | 0 | png->state = ii_state_flush; |
474 | 0 | break; |
475 | 0 | } |
476 | | |
477 | 350 | png->png_info = png_create_info_struct(png->png); |
478 | 350 | if (!png->png_info) { |
479 | 0 | png->state = ii_state_flush; |
480 | 0 | break; |
481 | 0 | } |
482 | | |
483 | 350 | png_set_read_fn(png->png, png, my_png_read); |
484 | 350 | png_set_alpha_mode(png->png, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); |
485 | | |
486 | 350 | if (setjmp(png_jmpbuf(png->png))) { |
487 | 344 | png->state = ii_state_flush; |
488 | 344 | break; |
489 | 344 | } |
490 | | |
491 | | /* We use the "low level" interface to libpng to allow us to |
492 | | * optimise memory usage. Any errors will longjmp. */ |
493 | 6 | png_read_info(png->png, png->png_info); |
494 | | |
495 | 6 | png_set_expand_16(png->png); |
496 | 6 | png_set_alpha_mode(png->png, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); |
497 | 6 | { |
498 | 6 | int bpc, color; |
499 | 6 | static const png_color_16 bg = { 0, 65535, 65535, 65535, 65535 }; |
500 | 6 | png_get_IHDR(png->png, png->png_info, |
501 | 6 | &png->width, |
502 | 6 | &png->height, |
503 | 6 | &bpc, |
504 | 6 | &color, |
505 | 6 | &png->interlaced, |
506 | 6 | NULL /* compression */, |
507 | 6 | NULL /* filter */); |
508 | 6 | switch (color) { |
509 | 2 | case PNG_COLOR_TYPE_GRAY_ALPHA: |
510 | 17 | case PNG_COLOR_TYPE_GRAY: |
511 | 17 | png->num_comps = 1; |
512 | 17 | break; |
513 | 4 | case PNG_COLOR_TYPE_PALETTE: |
514 | 4 | png_set_palette_to_rgb(png->png); |
515 | 4 | png->num_comps = 3; |
516 | 4 | break; |
517 | 15 | case PNG_COLOR_TYPE_RGB: |
518 | 27 | case PNG_COLOR_TYPE_RGB_ALPHA: |
519 | 27 | png->num_comps = 3; |
520 | 27 | break; |
521 | 0 | default: |
522 | 0 | png->state = ii_state_flush; |
523 | 0 | png_longjmp(png->png, 1); |
524 | 0 | break; |
525 | 6 | } |
526 | 48 | png->passes = png_set_interlace_handling(png->png); |
527 | 48 | png_set_background(png->png, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1); |
528 | 48 | } |
529 | | |
530 | 0 | png->xresolution = png_get_x_pixels_per_inch(png->png, png->png_info); |
531 | 48 | png->yresolution = png_get_y_pixels_per_inch(png->png, png->png_info); |
532 | 48 | if (png->xresolution == 0) |
533 | 43 | png->xresolution = png->yresolution; |
534 | 48 | if (png->yresolution == 0) |
535 | 43 | png->yresolution = png->xresolution; |
536 | 48 | if (png->xresolution == 0) |
537 | 43 | png->xresolution = png->yresolution = 72; |
538 | | |
539 | 48 | cs = (png->num_comps == 1 ? png->gray : png->rgb); |
540 | | |
541 | | /* Read the updated info */ |
542 | 48 | png_read_update_info(png->png, png->png_info); |
543 | 48 | png->bpp = png_get_bit_depth(png->png, png->png_info) * png->num_comps; |
544 | | |
545 | | /* Scale to fit, if too large. */ |
546 | 48 | scale = 1.0f; |
547 | 48 | if (png->width * png->dev->HWResolution[0] > png->dev->width * png->xresolution) |
548 | 6 | scale = ((float)png->dev->width * png->xresolution) / (png->width * png->dev->HWResolution[0]); |
549 | 48 | if (scale * png->height * png->dev->HWResolution[1] > png->dev->height * png->yresolution) |
550 | 3 | 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 | 48 | xext = ((float)png->width * 72 * scale / png->xresolution); |
554 | 48 | xoffset = (png->dev->width * 72 / png->dev->HWResolution[0] - xext)/2; |
555 | 48 | yext = ((float)png->height * 72 * scale / png->yresolution); |
556 | 48 | 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 | 48 | png->byte_width = png_get_rowbytes(png->png, png->png_info); |
561 | | |
562 | 48 | png->nulldev = gs_currentdevice(png->pgs); |
563 | 48 | rc_increment(png->nulldev); |
564 | 48 | code = gs_setdevice_no_erase(png->pgs, png->dev); |
565 | 48 | if (code < 0) { |
566 | 0 | png->state = ii_state_flush; |
567 | 0 | break; |
568 | 0 | } |
569 | 48 | 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 | 48 | code = gs_translate(png->pgs, 0.0, png->dev->height * 72 / png->dev->HWResolution[1]); |
580 | 48 | if (code >= 0) |
581 | 48 | code = gs_translate(png->pgs, xoffset, -yoffset); |
582 | 48 | if (code >= 0) |
583 | 48 | 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 | 48 | if (code >= 0) |
590 | 48 | code = gs_erasepage(png->pgs); |
591 | 48 | if (code < 0) { |
592 | 0 | png->state = ii_state_flush; |
593 | 0 | break; |
594 | 0 | } |
595 | | |
596 | 48 | 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 | 48 | png->samples = gs_alloc_bytes(png->memory, |
604 | 48 | (size_t)png->byte_width * (png->interlaced ? png->height : 1), |
605 | 48 | "png_impl_process(samples)"); |
606 | 48 | if (png->samples == NULL) { |
607 | 0 | png->state = ii_state_flush; |
608 | 0 | break; |
609 | 0 | } |
610 | | |
611 | 48 | memset(&png->image, 0, sizeof(png->image)); |
612 | 48 | gs_image_t_init(&png->image, cs); |
613 | 48 | png->image.BitsPerComponent = png->bpp/png->num_comps; |
614 | 48 | png->image.Width = png->width; |
615 | 48 | png->image.Height = png->height; |
616 | | |
617 | 48 | png->image.ImageMatrix.xx = png->xresolution / 72.0f; |
618 | 48 | png->image.ImageMatrix.yy = png->yresolution / 72.0f; |
619 | | |
620 | 48 | png->penum = gs_image_enum_alloc(png->memory, "png_impl_process(penum)"); |
621 | 48 | 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 | 48 | code = gs_image_init(png->penum, |
628 | 48 | &png->image, |
629 | 48 | false, |
630 | 48 | false, |
631 | 48 | png->pgs); |
632 | 48 | if (code < 0) { |
633 | 0 | png->state = ii_state_flush; |
634 | 0 | return code; |
635 | 0 | } |
636 | | |
637 | 48 | { |
638 | 48 | int i, j; |
639 | | |
640 | 48 | if (png->interlaced) { |
641 | | /* Collect the results from all but the last pass */ |
642 | 63 | for (j = png->passes-1; j > 0; j--) |
643 | 4.12k | for (i = 0; i < png->height; i++) |
644 | 4.06k | png_read_row(png->png, &png->samples[i*png->byte_width], NULL); |
645 | | |
646 | | /* And actually process the last pass */ |
647 | 67 | for (i = 0; i < png->height; i++) { |
648 | 56 | uint used; |
649 | | |
650 | 56 | png_read_row(png->png, &png->samples[i*png->byte_width], NULL); |
651 | | |
652 | 56 | code = gs_image_next(png->penum, &png->samples[i*png->byte_width], png->byte_width, &used); |
653 | 56 | if (code < 0) { |
654 | 0 | png->state = ii_state_flush; |
655 | 0 | break; |
656 | 0 | } |
657 | 56 | } |
658 | 37 | } else { |
659 | 4.11k | for (i = 0; i < png->height; i++) { |
660 | 4.07k | uint used; |
661 | | |
662 | 4.07k | png_read_row(png->png, png->samples, NULL); |
663 | | |
664 | 4.07k | code = gs_image_next(png->penum, png->samples, png->byte_width, &used); |
665 | 4.07k | if (code < 0) { |
666 | 0 | png->state = ii_state_flush; |
667 | 0 | break; |
668 | 0 | } |
669 | 4.07k | } |
670 | 37 | } |
671 | 48 | } |
672 | | |
673 | 48 | code = gs_image_cleanup_and_free_enum(png->penum, png->pgs); |
674 | 48 | png->penum = NULL; |
675 | 48 | if (code < 0) { |
676 | 0 | png->state = ii_state_flush; |
677 | 0 | break; |
678 | 0 | } |
679 | 48 | code = pl_finish_page(png->memory->gs_lib_ctx->top_of_system, |
680 | 48 | png->pgs, 1, true); |
681 | 48 | if (code < 0) { |
682 | 0 | png->state = ii_state_flush; |
683 | 0 | break; |
684 | 0 | } |
685 | | |
686 | 48 | png->state = ii_state_flush; |
687 | 48 | break; |
688 | 48 | } |
689 | 0 | default: |
690 | 350 | case ii_state_flush: |
691 | 350 | if (png->png) |
692 | 350 | { |
693 | 350 | png_destroy_read_struct(&png->png, &png->png_info, NULL); |
694 | 350 | png->png = NULL; |
695 | 350 | png->png_info = NULL; |
696 | 350 | } |
697 | | |
698 | 350 | if (png->penum) { |
699 | 42 | (void)gs_image_cleanup_and_free_enum(png->penum, png->pgs); |
700 | 42 | png->penum = NULL; |
701 | 42 | } |
702 | | |
703 | 350 | gs_free_object(png->memory, png->buffer, "png_impl_process(buffer)"); |
704 | 350 | png->buffer = NULL; |
705 | 350 | gs_free_object(png->memory, png->samples, "png_impl_process(samples)"); |
706 | 350 | png->samples = NULL; |
707 | | /* We want to bin any data we get up to, but not including |
708 | | * a UEL. */ |
709 | 350 | return flush_to_uel(pr); |
710 | 2.78k | } |
711 | 2.43k | advanced |= (ostate != png->state); |
712 | 2.43k | advanced |= (bytes_in != pr->limit - pr->ptr); |
713 | 2.43k | } while (advanced); |
714 | | |
715 | 722 | if (!advanced && bytes == 0) |
716 | 722 | code = gs_note_error(gs_error_NeedInput); |
717 | | |
718 | 722 | return code; |
719 | 1.07k | } |
720 | | |
721 | | static int |
722 | | png_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) |
723 | 722 | { |
724 | 722 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
725 | | |
726 | 722 | return do_impl_process(png, pr, 0); |
727 | 722 | } |
728 | | |
729 | | static int |
730 | | png_impl_process_end(pl_interp_implementation_t * impl) |
731 | 350 | { |
732 | 350 | return 0; |
733 | 350 | } |
734 | | |
735 | | /* Not implemented */ |
736 | | static int |
737 | | png_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor) |
738 | 0 | { |
739 | 0 | const byte *p = cursor->ptr; |
740 | 0 | const byte *rlimit = cursor->limit; |
741 | | |
742 | | /* Skip to, but leave UEL in buffer for PJL to find later */ |
743 | 0 | for (; p < rlimit; ++p) |
744 | 0 | if (p[1] == '\033') { |
745 | 0 | uint avail = rlimit - p; |
746 | |
|
747 | 0 | if (memcmp(p + 1, "\033%-12345X", min(avail, 9))) |
748 | 0 | continue; |
749 | 0 | if (avail < 9) |
750 | 0 | break; |
751 | 0 | cursor->ptr = p; |
752 | 0 | return 1; /* found eoj */ |
753 | 0 | } |
754 | 0 | cursor->ptr = p; |
755 | 0 | return 0; /* need more */ |
756 | 0 | } |
757 | | |
758 | | /* Parser action for end-of-file */ |
759 | | static int |
760 | | png_impl_process_eof(pl_interp_implementation_t *impl) |
761 | 350 | { |
762 | 350 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
763 | 350 | stream_cursor_read cursor; |
764 | | |
765 | 350 | cursor.ptr = NULL; |
766 | 350 | cursor.limit = NULL; |
767 | | |
768 | 350 | return do_impl_process(png, &cursor, 1); |
769 | 350 | } |
770 | | |
771 | | /* Report any errors after running a job */ |
772 | | static int |
773 | | png_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */ |
774 | | int code, /* prev termination status */ |
775 | | long file_position, /* file position of error, -1 if unknown */ |
776 | | bool force_to_cout /* force errors to cout */ |
777 | | ) |
778 | 0 | { |
779 | 0 | return 0; |
780 | 0 | } |
781 | | |
782 | | /* Wrap up interp instance after a "job" */ |
783 | | static int |
784 | | png_impl_dnit_job(pl_interp_implementation_t *impl) |
785 | 350 | { |
786 | 350 | png_interp_instance_t *png = (png_interp_instance_t *)impl->interp_client_data; |
787 | | |
788 | 350 | if (png->nulldev) { |
789 | 48 | int code = gs_setdevice(png->pgs, png->nulldev); |
790 | 48 | png->dev = NULL; |
791 | 48 | rc_decrement(png->nulldev, "png_impl_dnit_job(nulldevice)"); |
792 | 48 | png->nulldev = NULL; |
793 | 48 | return code; |
794 | 48 | } |
795 | 302 | return 0; |
796 | 350 | } |
797 | | |
798 | | /* Parser implementation descriptor */ |
799 | | const pl_interp_implementation_t png_implementation = { |
800 | | png_impl_characteristics, |
801 | | png_impl_allocate_interp_instance, |
802 | | png_impl_get_device_memory, |
803 | | NULL, /* png_impl_set_param */ |
804 | | NULL, /* png_impl_add_path */ |
805 | | NULL, /* png_impl_post_args_init */ |
806 | | png_impl_init_job, |
807 | | NULL, /* png_impl_run_prefix_commands */ |
808 | | NULL, /* png_impl_process_file */ |
809 | | png_impl_process_begin, |
810 | | png_impl_process, |
811 | | png_impl_process_end, |
812 | | png_impl_flush_to_eoj, |
813 | | png_impl_process_eof, |
814 | | png_impl_report_errors, |
815 | | png_impl_dnit_job, |
816 | | png_impl_deallocate_interp_instance, |
817 | | NULL, /* png_impl_reset */ |
818 | | NULL /* interp_client_data */ |
819 | | }; |