Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (rpng.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #ifdef DEBUG | |
24 | #include <stdio.h> | |
25 | #endif | |
26 | #include <stdint.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
29 | ||
30 | #ifdef GEKKO | |
31 | #include <malloc.h> | |
32 | #endif | |
33 | ||
34 | #include <boolean.h> | |
35 | #include <formats/image.h> | |
36 | #include <formats/rpng.h> | |
37 | #include <streams/trans_stream.h> | |
38 | #include <string/stdstring.h> | |
39 | ||
40 | #include "rpng_internal.h" | |
41 | ||
42 | enum png_ihdr_color_type | |
43 | { | |
44 | PNG_IHDR_COLOR_GRAY = 0, | |
45 | PNG_IHDR_COLOR_RGB = 2, | |
46 | PNG_IHDR_COLOR_PLT = 3, | |
47 | PNG_IHDR_COLOR_GRAY_ALPHA = 4, | |
48 | PNG_IHDR_COLOR_RGBA = 6 | |
49 | }; | |
50 | ||
51 | enum png_line_filter | |
52 | { | |
53 | PNG_FILTER_NONE = 0, | |
54 | PNG_FILTER_SUB, | |
55 | PNG_FILTER_UP, | |
56 | PNG_FILTER_AVERAGE, | |
57 | PNG_FILTER_PAETH | |
58 | }; | |
59 | ||
60 | enum png_chunk_type | |
61 | { | |
62 | PNG_CHUNK_NOOP = 0, | |
63 | PNG_CHUNK_ERROR, | |
64 | PNG_CHUNK_IHDR, | |
65 | PNG_CHUNK_IDAT, | |
66 | PNG_CHUNK_PLTE, | |
67 | PNG_CHUNK_tRNS, | |
68 | PNG_CHUNK_IEND | |
69 | }; | |
70 | ||
71 | struct adam7_pass | |
72 | { | |
73 | unsigned x; | |
74 | unsigned y; | |
75 | unsigned stride_x; | |
76 | unsigned stride_y; | |
77 | }; | |
78 | ||
79 | struct idat_buffer | |
80 | { | |
81 | uint8_t *data; | |
82 | size_t size; | |
83 | }; | |
84 | ||
85 | enum rpng_process_flags | |
86 | { | |
87 | RPNG_PROCESS_FLAG_INFLATE_INITIALIZED = (1 << 0), | |
88 | RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED = (1 << 1), | |
89 | RPNG_PROCESS_FLAG_PASS_INITIALIZED = (1 << 2) | |
90 | }; | |
91 | ||
92 | struct rpng_process | |
93 | { | |
94 | uint32_t *data; | |
95 | uint32_t *palette; | |
96 | void *stream; | |
97 | const struct trans_stream_backend *stream_backend; | |
98 | uint8_t *prev_scanline; | |
99 | uint8_t *decoded_scanline; | |
100 | uint8_t *inflate_buf; | |
101 | size_t restore_buf_size; | |
102 | size_t adam7_restore_buf_size; | |
103 | size_t data_restore_buf_size; | |
104 | size_t inflate_buf_size; | |
105 | size_t avail_in; | |
106 | size_t avail_out; | |
107 | size_t total_out; | |
108 | size_t pass_size; | |
109 | struct png_ihdr ihdr; /* uint32_t alignment */ | |
110 | unsigned bpp; | |
111 | unsigned pitch; | |
112 | unsigned h; | |
113 | unsigned pass_width; | |
114 | unsigned pass_height; | |
115 | unsigned pass_pos; | |
116 | uint8_t flags; | |
117 | }; | |
118 | ||
119 | enum rpng_flags | |
120 | { | |
121 | RPNG_FLAG_HAS_IHDR = (1 << 0), | |
122 | RPNG_FLAG_HAS_IDAT = (1 << 1), | |
123 | RPNG_FLAG_HAS_IEND = (1 << 2), | |
124 | RPNG_FLAG_HAS_PLTE = (1 << 3), | |
125 | RPNG_FLAG_HAS_TRNS = (1 << 4) | |
126 | }; | |
127 | ||
128 | struct rpng | |
129 | { | |
130 | struct rpng_process *process; | |
131 | uint8_t *buff_data; | |
132 | uint8_t *buff_end; | |
133 | struct idat_buffer idat_buf; /* ptr alignment */ | |
134 | struct png_ihdr ihdr; /* uint32 alignment */ | |
135 | uint32_t palette[256]; | |
136 | uint8_t flags; | |
137 | }; | |
138 | ||
139 | static const struct adam7_pass rpng_passes[] = { | |
140 | { 0, 0, 8, 8 }, | |
141 | { 4, 0, 8, 8 }, | |
142 | { 0, 4, 4, 8 }, | |
143 | { 2, 0, 4, 4 }, | |
144 | { 0, 2, 2, 4 }, | |
145 | { 1, 0, 2, 2 }, | |
146 | { 0, 1, 1, 2 }, | |
147 | }; | |
148 | ||
149 | static INLINE uint32_t rpng_dword_be(const uint8_t *buf) | |
150 | { | |
151 | return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0); | |
152 | } | |
153 | ||
154 | #if defined(DEBUG) || defined(RPNG_TEST) | |
155 | static bool rpng_process_ihdr(struct png_ihdr *ihdr) | |
156 | { | |
157 | uint8_t ihdr_depth = ihdr->depth; | |
158 | ||
159 | switch (ihdr->color_type) | |
160 | { | |
161 | case PNG_IHDR_COLOR_RGB: | |
162 | case PNG_IHDR_COLOR_GRAY_ALPHA: | |
163 | case PNG_IHDR_COLOR_RGBA: | |
164 | if (ihdr_depth != 8 && ihdr_depth != 16) | |
165 | { | |
166 | fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); | |
167 | return false; | |
168 | } | |
169 | break; | |
170 | case PNG_IHDR_COLOR_GRAY: | |
171 | /* Valid bitdepths are: 1, 2, 4, 8, 16 */ | |
172 | if (ihdr_depth > 16 || (0x977F7FFF << ihdr_depth) & 0x80000000) | |
173 | { | |
174 | fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); | |
175 | return false; | |
176 | } | |
177 | break; | |
178 | case PNG_IHDR_COLOR_PLT: | |
179 | /* Valid bitdepths are: 1, 2, 4, 8 */ | |
180 | if (ihdr_depth > 8 || (0x977F7FFF << ihdr_depth) & 0x80000000) | |
181 | { | |
182 | fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); | |
183 | return false; | |
184 | } | |
185 | break; | |
186 | default: | |
187 | fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); | |
188 | return false; | |
189 | } | |
190 | ||
191 | #ifdef RPNG_TEST | |
192 | fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n", | |
193 | ihdr->width, ihdr->height, | |
194 | ihdr_depth, (ihdr->color_type == PNG_IHDR_COLOR_PLT) ? "yes" : "no", | |
195 | (ihdr->color_type & PNG_IHDR_COLOR_RGB) ? "yes" : "no", | |
196 | (ihdr->color_type & PNG_IHDR_COLOR_GRAY_ALPHA) ? "yes" : "no", | |
197 | ihdr->interlace == 1 ? "yes" : "no"); | |
198 | #endif | |
199 | ||
200 | return true; | |
201 | } | |
202 | #else | |
203 | static bool rpng_process_ihdr(struct png_ihdr *ihdr) | |
204 | { | |
205 | uint8_t ihdr_depth = ihdr->depth; | |
206 | ||
207 | switch (ihdr->color_type) | |
208 | { | |
209 | case PNG_IHDR_COLOR_RGB: | |
210 | case PNG_IHDR_COLOR_GRAY_ALPHA: | |
211 | case PNG_IHDR_COLOR_RGBA: | |
212 | if (ihdr_depth != 8 && ihdr_depth != 16) | |
213 | return false; | |
214 | break; | |
215 | case PNG_IHDR_COLOR_GRAY: | |
216 | /* Valid bitdepths are: 1, 2, 4, 8, 16 */ | |
217 | if (ihdr_depth > 16 || (0x977F7FFF << ihdr_depth) & 0x80000000) | |
218 | return false; | |
219 | break; | |
220 | case PNG_IHDR_COLOR_PLT: | |
221 | /* Valid bitdepths are: 1, 2, 4, 8 */ | |
222 | if (ihdr_depth > 8 || (0x977F7FFF << ihdr_depth) & 0x80000000) | |
223 | return false; | |
224 | break; | |
225 | default: | |
226 | return false; | |
227 | } | |
228 | ||
229 | return true; | |
230 | } | |
231 | #endif | |
232 | ||
233 | static void rpng_reverse_filter_copy_line_rgb(uint32_t *data, | |
234 | const uint8_t *decoded, unsigned width, unsigned bpp) | |
235 | { | |
236 | int i; | |
237 | ||
238 | bpp /= 8; | |
239 | ||
240 | for (i = 0; i < (int)width; i++) | |
241 | { | |
242 | uint32_t r, g, b; | |
243 | ||
244 | r = *decoded; | |
245 | decoded += bpp; | |
246 | g = *decoded; | |
247 | decoded += bpp; | |
248 | b = *decoded; | |
249 | decoded += bpp; | |
250 | data[i] = (0xffu << 24) | (r << 16) | (g << 8) | (b << 0); | |
251 | } | |
252 | } | |
253 | ||
254 | static void rpng_reverse_filter_copy_line_rgba(uint32_t *data, | |
255 | const uint8_t *decoded, unsigned width, unsigned bpp) | |
256 | { | |
257 | int i; | |
258 | ||
259 | bpp /= 8; | |
260 | ||
261 | for (i = 0; i < (int)width; i++) | |
262 | { | |
263 | uint32_t r, g, b, a; | |
264 | r = *decoded; | |
265 | decoded += bpp; | |
266 | g = *decoded; | |
267 | decoded += bpp; | |
268 | b = *decoded; | |
269 | decoded += bpp; | |
270 | a = *decoded; | |
271 | decoded += bpp; | |
272 | data[i] = (a << 24) | (r << 16) | (g << 8) | (b << 0); | |
273 | } | |
274 | } | |
275 | ||
276 | static void rpng_reverse_filter_copy_line_bw(uint32_t *data, | |
277 | const uint8_t *decoded, unsigned width, unsigned depth) | |
278 | { | |
279 | int i; | |
280 | unsigned bit; | |
281 | static const unsigned mul_table[] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; | |
282 | unsigned mul, mask; | |
283 | ||
284 | if (depth == 16) | |
285 | { | |
286 | for (i = 0; i < (int)width; i++) | |
287 | { | |
288 | uint32_t val = decoded[i << 1]; | |
289 | data[i] = (val * 0x010101) | (0xffu << 24); | |
290 | } | |
291 | return; | |
292 | } | |
293 | ||
294 | mul = mul_table[depth]; | |
295 | mask = (1 << depth) - 1; | |
296 | bit = 0; | |
297 | ||
298 | for (i = 0; i < (int)width; i++, bit += depth) | |
299 | { | |
300 | unsigned byte = bit >> 3; | |
301 | unsigned val = decoded[byte] >> (8 - depth - (bit & 7)); | |
302 | ||
303 | val &= mask; | |
304 | val *= mul; | |
305 | data[i] = (val * 0x010101) | (0xffu << 24); | |
306 | } | |
307 | } | |
308 | ||
309 | static void rpng_reverse_filter_copy_line_gray_alpha(uint32_t *data, | |
310 | const uint8_t *decoded, unsigned width, | |
311 | unsigned bpp) | |
312 | { | |
313 | int i; | |
314 | ||
315 | bpp /= 8; | |
316 | ||
317 | for (i = 0; i < (int)width; i++) | |
318 | { | |
319 | uint32_t gray, alpha; | |
320 | ||
321 | gray = *decoded; | |
322 | decoded += bpp; | |
323 | alpha = *decoded; | |
324 | decoded += bpp; | |
325 | ||
326 | data[i] = (gray * 0x010101) | (alpha << 24); | |
327 | } | |
328 | } | |
329 | ||
330 | static void rpng_reverse_filter_copy_line_plt(uint32_t *data, | |
331 | const uint8_t *decoded, unsigned width, | |
332 | unsigned depth, const uint32_t *palette) | |
333 | { | |
334 | switch (depth) | |
335 | { | |
336 | case 1: | |
337 | { | |
338 | int i; | |
339 | unsigned w = width / 8; | |
340 | for (i = 0; i < (int)w; i++, decoded++) | |
341 | { | |
342 | *data++ = palette[(*decoded >> 7) & 1]; | |
343 | *data++ = palette[(*decoded >> 6) & 1]; | |
344 | *data++ = palette[(*decoded >> 5) & 1]; | |
345 | *data++ = palette[(*decoded >> 4) & 1]; | |
346 | *data++ = palette[(*decoded >> 3) & 1]; | |
347 | *data++ = palette[(*decoded >> 2) & 1]; | |
348 | *data++ = palette[(*decoded >> 1) & 1]; | |
349 | *data++ = palette[*decoded & 1]; | |
350 | } | |
351 | ||
352 | switch (width & 7) | |
353 | { | |
354 | case 7: | |
355 | data[6] = palette[(*decoded >> 1) & 1]; | |
356 | case 6: | |
357 | data[5] = palette[(*decoded >> 2) & 1]; | |
358 | case 5: | |
359 | data[4] = palette[(*decoded >> 3) & 1]; | |
360 | case 4: | |
361 | data[3] = palette[(*decoded >> 4) & 1]; | |
362 | case 3: | |
363 | data[2] = palette[(*decoded >> 5) & 1]; | |
364 | case 2: | |
365 | data[1] = palette[(*decoded >> 6) & 1]; | |
366 | case 1: | |
367 | data[0] = palette[(*decoded >> 7) & 1]; | |
368 | break; | |
369 | } | |
370 | } | |
371 | break; | |
372 | ||
373 | case 2: | |
374 | { | |
375 | int i; | |
376 | unsigned w = width / 4; | |
377 | for (i = 0; i < (int)w; i++, decoded++) | |
378 | { | |
379 | *data++ = palette[(*decoded >> 6) & 3]; | |
380 | *data++ = palette[(*decoded >> 4) & 3]; | |
381 | *data++ = palette[(*decoded >> 2) & 3]; | |
382 | *data++ = palette[*decoded & 3]; | |
383 | } | |
384 | ||
385 | switch (width & 3) | |
386 | { | |
387 | case 3: | |
388 | data[2] = palette[(*decoded >> 2) & 3]; | |
389 | case 2: | |
390 | data[1] = palette[(*decoded >> 4) & 3]; | |
391 | case 1: | |
392 | data[0] = palette[(*decoded >> 6) & 3]; | |
393 | break; | |
394 | } | |
395 | } | |
396 | break; | |
397 | ||
398 | case 4: | |
399 | { | |
400 | int i; | |
401 | unsigned w = width / 2; | |
402 | for (i = 0; i < (int)w; i++, decoded++) | |
403 | { | |
404 | *data++ = palette[*decoded >> 4]; | |
405 | *data++ = palette[*decoded & 0x0f]; | |
406 | } | |
407 | ||
408 | if (width & 1) | |
409 | *data = palette[*decoded >> 4]; | |
410 | } | |
411 | break; | |
412 | ||
413 | case 8: | |
414 | { | |
415 | int i; | |
416 | for (i = 0; i < (int)width; i++, decoded++, data++) | |
417 | *data = palette[*decoded]; | |
418 | } | |
419 | break; | |
420 | } | |
421 | } | |
422 | ||
423 | static void rpng_pass_geom(const struct png_ihdr *ihdr, | |
424 | unsigned width, unsigned height, | |
425 | unsigned *bpp_out, unsigned *pitch_out, size_t *pass_size) | |
426 | { | |
427 | unsigned bpp = 0; | |
428 | unsigned pitch = 0; | |
429 | ||
430 | switch (ihdr->color_type) | |
431 | { | |
432 | case PNG_IHDR_COLOR_GRAY: | |
433 | bpp = (ihdr->depth + 7) / 8; | |
434 | pitch = (ihdr->width * ihdr->depth + 7) / 8; | |
435 | break; | |
436 | case PNG_IHDR_COLOR_RGB: | |
437 | bpp = (ihdr->depth * 3 + 7) / 8; | |
438 | pitch = (ihdr->width * ihdr->depth * 3 + 7) / 8; | |
439 | break; | |
440 | case PNG_IHDR_COLOR_PLT: | |
441 | bpp = (ihdr->depth + 7) / 8; | |
442 | pitch = (ihdr->width * ihdr->depth + 7) / 8; | |
443 | break; | |
444 | case PNG_IHDR_COLOR_GRAY_ALPHA: | |
445 | bpp = (ihdr->depth * 2 + 7) / 8; | |
446 | pitch = (ihdr->width * ihdr->depth * 2 + 7) / 8; | |
447 | break; | |
448 | case PNG_IHDR_COLOR_RGBA: | |
449 | bpp = (ihdr->depth * 4 + 7) / 8; | |
450 | pitch = (ihdr->width * ihdr->depth * 4 + 7) / 8; | |
451 | break; | |
452 | default: | |
453 | break; | |
454 | } | |
455 | ||
456 | if (pass_size) | |
457 | *pass_size = (pitch + 1) * ihdr->height; | |
458 | if (bpp_out) | |
459 | *bpp_out = bpp; | |
460 | if (pitch_out) | |
461 | *pitch_out = pitch; | |
462 | } | |
463 | ||
464 | static void rpng_reverse_filter_adam7_deinterlace_pass(uint32_t *data, | |
465 | const struct png_ihdr *ihdr, | |
466 | const uint32_t *input, unsigned pass_width, unsigned pass_height, | |
467 | const struct adam7_pass *pass) | |
468 | { | |
469 | unsigned x, y; | |
470 | ||
471 | data += pass->y * ihdr->width + pass->x; | |
472 | ||
473 | for (y = 0; y < pass_height; | |
474 | y++, data += ihdr->width * pass->stride_y, input += pass_width) | |
475 | { | |
476 | uint32_t *out = data; | |
477 | ||
478 | for (x = 0; x < pass_width; x++, out += pass->stride_x) | |
479 | *out = input[x]; | |
480 | } | |
481 | } | |
482 | ||
483 | static void rpng_reverse_filter_deinit(struct rpng_process *pngp) | |
484 | { | |
485 | if (!pngp) | |
486 | return; | |
487 | if (pngp->decoded_scanline) | |
488 | free(pngp->decoded_scanline); | |
489 | pngp->decoded_scanline = NULL; | |
490 | if (pngp->prev_scanline) | |
491 | free(pngp->prev_scanline); | |
492 | pngp->prev_scanline = NULL; | |
493 | ||
494 | pngp->flags &= ~RPNG_PROCESS_FLAG_PASS_INITIALIZED; | |
495 | pngp->h = 0; | |
496 | } | |
497 | ||
498 | static int rpng_reverse_filter_init(const struct png_ihdr *ihdr, | |
499 | struct rpng_process *pngp) | |
500 | { | |
501 | size_t pass_size; | |
502 | ||
503 | if ( !(pngp->flags & RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED) | |
504 | && ihdr->interlace) | |
505 | { | |
506 | if ( ihdr->width <= rpng_passes[pngp->pass_pos].x | |
507 | || ihdr->height <= rpng_passes[pngp->pass_pos].y) /* Empty pass */ | |
508 | return 1; | |
509 | ||
510 | pngp->pass_width = (ihdr->width - | |
511 | rpng_passes[pngp->pass_pos].x + rpng_passes[pngp->pass_pos].stride_x | |
512 | - 1) / rpng_passes[pngp->pass_pos].stride_x; | |
513 | pngp->pass_height = (ihdr->height - rpng_passes[pngp->pass_pos].y + | |
514 | rpng_passes[pngp->pass_pos].stride_y - 1) / rpng_passes[pngp->pass_pos].stride_y; | |
515 | ||
516 | if (!(pngp->data = (uint32_t*)malloc( | |
517 | pngp->pass_width * pngp->pass_height * sizeof(uint32_t)))) | |
518 | return -1; | |
519 | ||
520 | pngp->ihdr = *ihdr; | |
521 | pngp->ihdr.width = pngp->pass_width; | |
522 | pngp->ihdr.height = pngp->pass_height; | |
523 | ||
524 | rpng_pass_geom(&pngp->ihdr, pngp->pass_width, | |
525 | pngp->pass_height, NULL, NULL, &pngp->pass_size); | |
526 | ||
527 | if (pngp->pass_size > pngp->total_out) | |
528 | { | |
529 | free(pngp->data); | |
530 | pngp->data = NULL; | |
531 | return -1; | |
532 | } | |
533 | ||
534 | pngp->flags |= RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED; | |
535 | ||
536 | return 0; | |
537 | } | |
538 | ||
539 | if (pngp->flags & RPNG_PROCESS_FLAG_PASS_INITIALIZED) | |
540 | return 0; | |
541 | ||
542 | rpng_pass_geom(ihdr, ihdr->width, ihdr->height, &pngp->bpp, &pngp->pitch, &pass_size); | |
543 | ||
544 | if (pngp->total_out < pass_size) | |
545 | return -1; | |
546 | ||
547 | pngp->restore_buf_size = 0; | |
548 | pngp->data_restore_buf_size = 0; | |
549 | pngp->prev_scanline = (uint8_t*)calloc(1, pngp->pitch); | |
550 | pngp->decoded_scanline = (uint8_t*)calloc(1, pngp->pitch); | |
551 | ||
552 | if (!pngp->prev_scanline || !pngp->decoded_scanline) | |
553 | goto error; | |
554 | ||
555 | pngp->h = 0; | |
556 | pngp->flags |= RPNG_PROCESS_FLAG_PASS_INITIALIZED; | |
557 | ||
558 | return 0; | |
559 | ||
560 | error: | |
561 | rpng_reverse_filter_deinit(pngp); | |
562 | return -1; | |
563 | } | |
564 | ||
565 | static int rpng_reverse_filter_copy_line(uint32_t *data, | |
566 | const struct png_ihdr *ihdr, | |
567 | struct rpng_process *pngp, unsigned filter) | |
568 | { | |
569 | unsigned i; | |
570 | ||
571 | switch (filter) | |
572 | { | |
573 | case PNG_FILTER_NONE: | |
574 | memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch); | |
575 | break; | |
576 | case PNG_FILTER_SUB: | |
577 | memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch); | |
578 | for (i = pngp->bpp; i < pngp->pitch; i++) | |
579 | pngp->decoded_scanline[i] += pngp->decoded_scanline[i - pngp->bpp]; | |
580 | break; | |
581 | case PNG_FILTER_UP: | |
582 | memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch); | |
583 | for (i = 0; i < pngp->pitch; i++) | |
584 | pngp->decoded_scanline[i] += pngp->prev_scanline[i]; | |
585 | break; | |
586 | case PNG_FILTER_AVERAGE: | |
587 | memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch); | |
588 | for (i = 0; i < pngp->bpp; i++) | |
589 | { | |
590 | uint8_t avg = pngp->prev_scanline[i] >> 1; | |
591 | pngp->decoded_scanline[i] += avg; | |
592 | } | |
593 | for (i = pngp->bpp; i < pngp->pitch; i++) | |
594 | { | |
595 | uint8_t avg = (pngp->decoded_scanline[i - pngp->bpp] + pngp->prev_scanline[i]) >> 1; | |
596 | pngp->decoded_scanline[i] += avg; | |
597 | } | |
598 | break; | |
599 | case PNG_FILTER_PAETH: | |
600 | memcpy(pngp->decoded_scanline, pngp->inflate_buf, pngp->pitch); | |
601 | for (i = 0; i < pngp->bpp; i++) | |
602 | pngp->decoded_scanline[i] += pngp->prev_scanline[i]; | |
603 | for (i = pngp->bpp; i < pngp->pitch; i++) | |
604 | pngp->decoded_scanline[i] += paeth(pngp->decoded_scanline[i - pngp->bpp], | |
605 | pngp->prev_scanline[i], pngp->prev_scanline[i - pngp->bpp]); | |
606 | break; | |
607 | default: | |
608 | return IMAGE_PROCESS_ERROR_END; | |
609 | } | |
610 | ||
611 | switch (ihdr->color_type) | |
612 | { | |
613 | case PNG_IHDR_COLOR_GRAY: | |
614 | rpng_reverse_filter_copy_line_bw(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); | |
615 | break; | |
616 | case PNG_IHDR_COLOR_RGB: | |
617 | rpng_reverse_filter_copy_line_rgb(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); | |
618 | break; | |
619 | case PNG_IHDR_COLOR_PLT: | |
620 | rpng_reverse_filter_copy_line_plt( | |
621 | data, pngp->decoded_scanline, ihdr->width, | |
622 | ihdr->depth, pngp->palette); | |
623 | break; | |
624 | case PNG_IHDR_COLOR_GRAY_ALPHA: | |
625 | rpng_reverse_filter_copy_line_gray_alpha(data, pngp->decoded_scanline, ihdr->width, | |
626 | ihdr->depth); | |
627 | break; | |
628 | case PNG_IHDR_COLOR_RGBA: | |
629 | rpng_reverse_filter_copy_line_rgba(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); | |
630 | break; | |
631 | } | |
632 | ||
633 | memcpy(pngp->prev_scanline, pngp->decoded_scanline, pngp->pitch); | |
634 | ||
635 | return IMAGE_PROCESS_NEXT; | |
636 | } | |
637 | ||
638 | static int rpng_reverse_filter_regular_iterate( | |
639 | uint32_t **data, const struct png_ihdr *ihdr, | |
640 | struct rpng_process *pngp) | |
641 | { | |
642 | int ret = IMAGE_PROCESS_END; | |
643 | if (pngp->h < ihdr->height) | |
644 | { | |
645 | unsigned filter = *pngp->inflate_buf++; | |
646 | pngp->restore_buf_size += 1; | |
647 | ret = rpng_reverse_filter_copy_line(*data, | |
648 | ihdr, pngp, filter); | |
649 | if (ret == IMAGE_PROCESS_END || ret == IMAGE_PROCESS_ERROR_END) | |
650 | goto end; | |
651 | } | |
652 | else | |
653 | goto end; | |
654 | ||
655 | pngp->h++; | |
656 | pngp->inflate_buf += pngp->pitch; | |
657 | pngp->restore_buf_size += pngp->pitch; | |
658 | ||
659 | *data += ihdr->width; | |
660 | pngp->data_restore_buf_size += ihdr->width; | |
661 | ||
662 | return IMAGE_PROCESS_NEXT; | |
663 | ||
664 | end: | |
665 | rpng_reverse_filter_deinit(pngp); | |
666 | ||
667 | pngp->inflate_buf -= pngp->restore_buf_size; | |
668 | *data -= pngp->data_restore_buf_size; | |
669 | pngp->data_restore_buf_size = 0; | |
670 | return ret; | |
671 | } | |
672 | ||
673 | static int rpng_reverse_filter_adam7_iterate(uint32_t **data_, | |
674 | const struct png_ihdr *ihdr, | |
675 | struct rpng_process *pngp) | |
676 | { | |
677 | int ret = 0; | |
678 | bool to_next = pngp->pass_pos < ARRAY_SIZE(rpng_passes); | |
679 | uint32_t *data = *data_; | |
680 | ||
681 | if (!to_next) | |
682 | return IMAGE_PROCESS_END; | |
683 | ||
684 | if ((ret = rpng_reverse_filter_init(ihdr, pngp)) == 1) | |
685 | return IMAGE_PROCESS_NEXT; | |
686 | else if (ret == -1) | |
687 | return IMAGE_PROCESS_ERROR_END; | |
688 | ||
689 | if (rpng_reverse_filter_init(&pngp->ihdr, pngp) == -1) | |
690 | return IMAGE_PROCESS_ERROR; | |
691 | ||
692 | do | |
693 | { | |
694 | ret = rpng_reverse_filter_regular_iterate(&pngp->data, | |
695 | &pngp->ihdr, pngp); | |
696 | } while (ret == IMAGE_PROCESS_NEXT); | |
697 | ||
698 | if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END) | |
699 | return IMAGE_PROCESS_ERROR; | |
700 | ||
701 | pngp->inflate_buf += pngp->pass_size; | |
702 | pngp->adam7_restore_buf_size += pngp->pass_size; | |
703 | ||
704 | pngp->total_out -= pngp->pass_size; | |
705 | ||
706 | rpng_reverse_filter_adam7_deinterlace_pass(data, | |
707 | ihdr, pngp->data, pngp->pass_width, pngp->pass_height, | |
708 | &rpng_passes[pngp->pass_pos]); | |
709 | ||
710 | free(pngp->data); | |
711 | ||
712 | pngp->data = NULL; | |
713 | pngp->pass_width = 0; | |
714 | pngp->pass_height = 0; | |
715 | pngp->pass_size = 0; | |
716 | pngp->flags &= ~RPNG_PROCESS_FLAG_ADAM7_PASS_INITIALIZED; | |
717 | ||
718 | return IMAGE_PROCESS_NEXT; | |
719 | } | |
720 | ||
721 | static int rpng_reverse_filter_adam7(uint32_t **data_, | |
722 | const struct png_ihdr *ihdr, | |
723 | struct rpng_process *pngp) | |
724 | { | |
725 | int ret = rpng_reverse_filter_adam7_iterate(data_, | |
726 | ihdr, pngp); | |
727 | ||
728 | switch (ret) | |
729 | { | |
730 | case IMAGE_PROCESS_ERROR_END: | |
731 | case IMAGE_PROCESS_END: | |
732 | break; | |
733 | case IMAGE_PROCESS_NEXT: | |
734 | pngp->pass_pos++; | |
735 | return 0; | |
736 | case IMAGE_PROCESS_ERROR: | |
737 | if (pngp->data) | |
738 | { | |
739 | free(pngp->data); | |
740 | pngp->data = NULL; | |
741 | } | |
742 | pngp->inflate_buf -= pngp->adam7_restore_buf_size; | |
743 | pngp->adam7_restore_buf_size = 0; | |
744 | return -1; | |
745 | } | |
746 | ||
747 | pngp->inflate_buf -= pngp->adam7_restore_buf_size; | |
748 | pngp->adam7_restore_buf_size = 0; | |
749 | return ret; | |
750 | } | |
751 | ||
752 | static int rpng_load_image_argb_process_inflate_init( | |
753 | rpng_t *rpng, uint32_t **data) | |
754 | { | |
755 | bool zstatus; | |
756 | enum trans_stream_error terror; | |
757 | uint32_t rd, wn; | |
758 | struct rpng_process *process = (struct rpng_process*)rpng->process; | |
759 | bool to_continue = (process->avail_in > 0 | |
760 | && process->avail_out > 0); | |
761 | ||
762 | if (!to_continue) | |
763 | goto end; | |
764 | ||
765 | zstatus = process->stream_backend->trans(process->stream, false, &rd, &wn, &terror); | |
766 | ||
767 | if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL) | |
768 | goto error; | |
769 | ||
770 | process->avail_in -= rd; | |
771 | process->avail_out -= wn; | |
772 | process->total_out += wn; | |
773 | ||
774 | if (terror) | |
775 | return 0; | |
776 | ||
777 | end: | |
778 | process->stream_backend->stream_free(process->stream); | |
779 | process->stream = NULL; | |
780 | ||
781 | #ifdef GEKKO | |
782 | /* we often use these in textures, make sure they're 32-byte aligned */ | |
783 | *data = (uint32_t*)memalign(32, rpng->ihdr.width * | |
784 | rpng->ihdr.height * sizeof(uint32_t)); | |
785 | #else | |
786 | *data = (uint32_t*)malloc(rpng->ihdr.width * | |
787 | rpng->ihdr.height * sizeof(uint32_t)); | |
788 | #endif | |
789 | if (!*data) | |
790 | goto false_end; | |
791 | ||
792 | process->adam7_restore_buf_size = 0; | |
793 | process->restore_buf_size = 0; | |
794 | process->palette = rpng->palette; | |
795 | ||
796 | if (rpng->ihdr.interlace != 1) | |
797 | if (rpng_reverse_filter_init(&rpng->ihdr, process) == -1) | |
798 | goto false_end; | |
799 | ||
800 | process->flags |= RPNG_PROCESS_FLAG_INFLATE_INITIALIZED; | |
801 | return 1; | |
802 | ||
803 | error: | |
804 | false_end: | |
805 | process->flags &= ~RPNG_PROCESS_FLAG_INFLATE_INITIALIZED; | |
806 | return -1; | |
807 | } | |
808 | ||
809 | static bool rpng_realloc_idat(struct idat_buffer *buf, uint32_t chunk_size) | |
810 | { | |
811 | uint8_t *new_buffer = (uint8_t*)realloc(buf->data, buf->size + chunk_size); | |
812 | ||
813 | if (!new_buffer) | |
814 | return false; | |
815 | ||
816 | buf->data = new_buffer; | |
817 | return true; | |
818 | } | |
819 | ||
820 | static struct rpng_process *rpng_process_init(rpng_t *rpng) | |
821 | { | |
822 | uint8_t *inflate_buf = NULL; | |
823 | struct rpng_process *process = (struct rpng_process*)malloc(sizeof(*process)); | |
824 | ||
825 | if (!process) | |
826 | return NULL; | |
827 | ||
828 | process->flags = 0; | |
829 | process->prev_scanline = NULL; | |
830 | process->decoded_scanline = NULL; | |
831 | process->inflate_buf = NULL; | |
832 | ||
833 | process->ihdr.width = 0; | |
834 | process->ihdr.height = 0; | |
835 | process->ihdr.depth = 0; | |
836 | process->ihdr.color_type = 0; | |
837 | process->ihdr.compression = 0; | |
838 | process->ihdr.filter = 0; | |
839 | process->ihdr.interlace = 0; | |
840 | ||
841 | process->restore_buf_size = 0; | |
842 | process->adam7_restore_buf_size = 0; | |
843 | process->data_restore_buf_size = 0; | |
844 | process->inflate_buf_size = 0; | |
845 | process->avail_in = 0; | |
846 | process->avail_out = 0; | |
847 | process->total_out = 0; | |
848 | process->pass_size = 0; | |
849 | process->bpp = 0; | |
850 | process->pitch = 0; | |
851 | process->h = 0; | |
852 | process->pass_width = 0; | |
853 | process->pass_height = 0; | |
854 | process->pass_pos = 0; | |
855 | process->data = 0; | |
856 | process->palette = 0; | |
857 | process->stream = NULL; | |
858 | process->stream_backend = trans_stream_get_zlib_inflate_backend(); | |
859 | ||
860 | rpng_pass_geom(&rpng->ihdr, rpng->ihdr.width, | |
861 | rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size); | |
862 | if (rpng->ihdr.interlace == 1) /* To be sure. */ | |
863 | process->inflate_buf_size *= 2; | |
864 | ||
865 | process->stream = process->stream_backend->stream_new(); | |
866 | ||
867 | if (!process->stream) | |
868 | { | |
869 | free(process); | |
870 | return NULL; | |
871 | } | |
872 | ||
873 | inflate_buf = (uint8_t*)malloc(process->inflate_buf_size); | |
874 | if (!inflate_buf) | |
875 | goto error; | |
876 | ||
877 | process->inflate_buf = inflate_buf; | |
878 | process->avail_in = rpng->idat_buf.size; | |
879 | process->avail_out = process->inflate_buf_size; | |
880 | ||
881 | process->stream_backend->set_in( | |
882 | process->stream, | |
883 | rpng->idat_buf.data, | |
884 | (uint32_t)rpng->idat_buf.size); | |
885 | process->stream_backend->set_out( | |
886 | process->stream, | |
887 | process->inflate_buf, | |
888 | (uint32_t)process->inflate_buf_size); | |
889 | ||
890 | return process; | |
891 | ||
892 | error: | |
893 | if (process) | |
894 | { | |
895 | if (process->stream) | |
896 | process->stream_backend->stream_free(process->stream); | |
897 | free(process); | |
898 | } | |
899 | return NULL; | |
900 | } | |
901 | ||
902 | /** | |
903 | * rpng_read_chunk_header: | |
904 | * | |
905 | * Leaf function. | |
906 | * | |
907 | * @return The PNG type of the memory chunk (i.e. IHDR, IDAT, IEND, | |
908 | PLTE, and/or tRNS) | |
909 | **/ | |
910 | static enum png_chunk_type rpng_read_chunk_header( | |
911 | uint8_t *buf, uint32_t chunk_size) | |
912 | { | |
913 | int i; | |
914 | char type[4]; | |
915 | ||
916 | for (i = 0; i < 4; i++) | |
917 | { | |
918 | uint8_t byte = buf[i + 4]; | |
919 | ||
920 | /* All four bytes of the chunk type must be | |
921 | * ASCII letters (codes 65-90 and 97-122) */ | |
922 | if ((byte < 65) || ((byte > 90) && (byte < 97)) || (byte > 122)) | |
923 | return PNG_CHUNK_ERROR; | |
924 | type[i] = byte; | |
925 | } | |
926 | ||
927 | if ( | |
928 | type[0] == 'I' | |
929 | && type[1] == 'H' | |
930 | && type[2] == 'D' | |
931 | && type[3] == 'R' | |
932 | ) | |
933 | return PNG_CHUNK_IHDR; | |
934 | else if | |
935 | ( | |
936 | type[0] == 'I' | |
937 | && type[1] == 'D' | |
938 | && type[2] == 'A' | |
939 | && type[3] == 'T' | |
940 | ) | |
941 | return PNG_CHUNK_IDAT; | |
942 | else if | |
943 | ( | |
944 | type[0] == 'I' | |
945 | && type[1] == 'E' | |
946 | && type[2] == 'N' | |
947 | && type[3] == 'D' | |
948 | ) | |
949 | return PNG_CHUNK_IEND; | |
950 | else if | |
951 | ( | |
952 | type[0] == 'P' | |
953 | && type[1] == 'L' | |
954 | && type[2] == 'T' | |
955 | && type[3] == 'E' | |
956 | ) | |
957 | return PNG_CHUNK_PLTE; | |
958 | else if | |
959 | ( | |
960 | type[0] == 't' | |
961 | && type[1] == 'R' | |
962 | && type[2] == 'N' | |
963 | && type[3] == 'S' | |
964 | ) | |
965 | return PNG_CHUNK_tRNS; | |
966 | ||
967 | return PNG_CHUNK_NOOP; | |
968 | } | |
969 | ||
970 | bool rpng_iterate_image(rpng_t *rpng) | |
971 | { | |
972 | unsigned i; | |
973 | uint8_t *buf = (uint8_t*)rpng->buff_data; | |
974 | uint32_t chunk_size = 0; | |
975 | ||
976 | /* Check whether data buffer pointer is valid */ | |
977 | if (buf > rpng->buff_end) | |
978 | return false; | |
979 | ||
980 | /* Check whether reading the header will overflow | |
981 | * the data buffer */ | |
982 | if (rpng->buff_end - buf < 8) | |
983 | return false; | |
984 | ||
985 | chunk_size = rpng_dword_be(buf); | |
986 | ||
987 | /* Check whether chunk will overflow the data buffer */ | |
988 | if (buf + 8 + chunk_size > rpng->buff_end) | |
989 | return false; | |
990 | ||
991 | switch (rpng_read_chunk_header(buf, chunk_size)) | |
992 | { | |
993 | case PNG_CHUNK_NOOP: | |
994 | default: | |
995 | break; | |
996 | ||
997 | case PNG_CHUNK_ERROR: | |
998 | return false; | |
999 | ||
1000 | case PNG_CHUNK_IHDR: | |
1001 | if ( (rpng->flags & RPNG_FLAG_HAS_IHDR) | |
1002 | || (rpng->flags & RPNG_FLAG_HAS_IDAT) | |
1003 | || (rpng->flags & RPNG_FLAG_HAS_IEND)) | |
1004 | return false; | |
1005 | ||
1006 | if (chunk_size != 13) | |
1007 | return false; | |
1008 | ||
1009 | buf += 4 + 4; | |
1010 | ||
1011 | rpng->ihdr.width = rpng_dword_be(buf + 0); | |
1012 | rpng->ihdr.height = rpng_dword_be(buf + 4); | |
1013 | rpng->ihdr.depth = buf[8]; | |
1014 | rpng->ihdr.color_type = buf[9]; | |
1015 | rpng->ihdr.compression = buf[10]; | |
1016 | rpng->ihdr.filter = buf[11]; | |
1017 | rpng->ihdr.interlace = buf[12]; | |
1018 | ||
1019 | if ( rpng->ihdr.width == 0 | |
1020 | || rpng->ihdr.height == 0 | |
1021 | /* ensure multiplications don't overflow and wrap around, that'd give buffer overflow crashes */ | |
1022 | || (uint64_t)rpng->ihdr.width*rpng->ihdr.height*sizeof(uint32_t) >= 0x80000000) | |
1023 | return false; | |
1024 | ||
1025 | if (!rpng_process_ihdr(&rpng->ihdr)) | |
1026 | return false; | |
1027 | ||
1028 | if (rpng->ihdr.compression != 0) | |
1029 | { | |
1030 | #if defined(DEBUG) || defined(RPNG_TEST) | |
1031 | fprintf(stderr, "[RPNG]: Error in line %d.\n", __LINE__); | |
1032 | #endif | |
1033 | return false; | |
1034 | } | |
1035 | ||
1036 | rpng->flags |= RPNG_FLAG_HAS_IHDR; | |
1037 | break; | |
1038 | ||
1039 | case PNG_CHUNK_PLTE: | |
1040 | { | |
1041 | int i; | |
1042 | unsigned entries = chunk_size / 3; | |
1043 | ||
1044 | if (entries > 256) | |
1045 | return false; | |
1046 | if (chunk_size % 3) | |
1047 | return false; | |
1048 | ||
1049 | if ( !(rpng->flags & RPNG_FLAG_HAS_IHDR) | |
1050 | || (rpng->flags & RPNG_FLAG_HAS_PLTE) | |
1051 | || (rpng->flags & RPNG_FLAG_HAS_IEND) | |
1052 | || (rpng->flags & RPNG_FLAG_HAS_IDAT) | |
1053 | || (rpng->flags & RPNG_FLAG_HAS_TRNS)) | |
1054 | return false; | |
1055 | ||
1056 | buf += 8; | |
1057 | ||
1058 | for (i = 0; i < (int)entries; i++) | |
1059 | { | |
1060 | uint32_t r = buf[3 * i + 0]; | |
1061 | uint32_t g = buf[3 * i + 1]; | |
1062 | uint32_t b = buf[3 * i + 2]; | |
1063 | rpng->palette[i] = (r << 16) | (g << 8) | (b << 0) | (0xffu << 24); | |
1064 | } | |
1065 | ||
1066 | rpng->flags |= RPNG_FLAG_HAS_PLTE; | |
1067 | } | |
1068 | break; | |
1069 | ||
1070 | case PNG_CHUNK_tRNS: | |
1071 | if (rpng->flags & RPNG_FLAG_HAS_IDAT) | |
1072 | return false; | |
1073 | ||
1074 | if (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT) | |
1075 | { | |
1076 | int i; | |
1077 | uint32_t *palette; | |
1078 | /* we should compare with the number of palette entries */ | |
1079 | if (chunk_size > 256) | |
1080 | return false; | |
1081 | ||
1082 | buf += 8; | |
1083 | palette = rpng->palette; | |
1084 | ||
1085 | for (i = 0; i < (int)chunk_size; i++, buf++, palette++) | |
1086 | *palette = (*palette & 0x00ffffff) | (unsigned)*buf << 24; | |
1087 | } | |
1088 | /* TODO: support colorkey in grayscale and truecolor images */ | |
1089 | ||
1090 | rpng->flags |= RPNG_FLAG_HAS_TRNS; | |
1091 | break; | |
1092 | ||
1093 | case PNG_CHUNK_IDAT: | |
1094 | if ( !(rpng->flags & RPNG_FLAG_HAS_IHDR) | |
1095 | || (rpng->flags & RPNG_FLAG_HAS_IEND) | |
1096 | || (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT | |
1097 | && | |
1098 | !(rpng->flags & RPNG_FLAG_HAS_PLTE))) | |
1099 | return false; | |
1100 | ||
1101 | if (!rpng_realloc_idat(&rpng->idat_buf, chunk_size)) | |
1102 | return false; | |
1103 | ||
1104 | buf += 8; | |
1105 | ||
1106 | for (i = 0; i < chunk_size; i++) | |
1107 | rpng->idat_buf.data[i + rpng->idat_buf.size] = buf[i]; | |
1108 | ||
1109 | rpng->idat_buf.size += chunk_size; | |
1110 | ||
1111 | rpng->flags |= RPNG_FLAG_HAS_IDAT; | |
1112 | break; | |
1113 | ||
1114 | case PNG_CHUNK_IEND: | |
1115 | if ( !(rpng->flags & RPNG_FLAG_HAS_IHDR) | |
1116 | || !(rpng->flags & RPNG_FLAG_HAS_IDAT)) | |
1117 | return false; | |
1118 | ||
1119 | rpng->flags |= RPNG_FLAG_HAS_IEND; | |
1120 | return false; | |
1121 | } | |
1122 | ||
1123 | rpng->buff_data += chunk_size + 12; | |
1124 | ||
1125 | /* Check whether data buffer pointer is valid */ | |
1126 | if (rpng->buff_data > rpng->buff_end) | |
1127 | return false; | |
1128 | return true; | |
1129 | } | |
1130 | ||
1131 | int rpng_process_image(rpng_t *rpng, | |
1132 | void **_data, size_t size, unsigned *width, unsigned *height) | |
1133 | { | |
1134 | uint32_t **data = (uint32_t**)_data; | |
1135 | ||
1136 | if (!rpng->process) | |
1137 | { | |
1138 | struct rpng_process *process = rpng_process_init(rpng); | |
1139 | ||
1140 | if (!process) | |
1141 | goto error; | |
1142 | ||
1143 | rpng->process = process; | |
1144 | return IMAGE_PROCESS_NEXT; | |
1145 | } | |
1146 | ||
1147 | if (!(rpng->process->flags & RPNG_PROCESS_FLAG_INFLATE_INITIALIZED)) | |
1148 | { | |
1149 | if (rpng_load_image_argb_process_inflate_init(rpng, data) == -1) | |
1150 | goto error; | |
1151 | return IMAGE_PROCESS_NEXT; | |
1152 | } | |
1153 | ||
1154 | *width = rpng->ihdr.width; | |
1155 | *height = rpng->ihdr.height; | |
1156 | ||
1157 | if (rpng->ihdr.interlace && rpng->process) | |
1158 | return rpng_reverse_filter_adam7(data, &rpng->ihdr, rpng->process); | |
1159 | return rpng_reverse_filter_regular_iterate(data, &rpng->ihdr, rpng->process); | |
1160 | ||
1161 | error: | |
1162 | if (rpng->process) | |
1163 | { | |
1164 | if (rpng->process->inflate_buf) | |
1165 | free(rpng->process->inflate_buf); | |
1166 | if (rpng->process->stream) | |
1167 | rpng->process->stream_backend->stream_free(rpng->process->stream); | |
1168 | free(rpng->process); | |
1169 | rpng->process = NULL; | |
1170 | } | |
1171 | return IMAGE_PROCESS_ERROR; | |
1172 | } | |
1173 | ||
1174 | void rpng_free(rpng_t *rpng) | |
1175 | { | |
1176 | if (!rpng) | |
1177 | return; | |
1178 | ||
1179 | if (rpng->idat_buf.data) | |
1180 | free(rpng->idat_buf.data); | |
1181 | if (rpng->process) | |
1182 | { | |
1183 | if (rpng->process->inflate_buf) | |
1184 | free(rpng->process->inflate_buf); | |
1185 | if (rpng->process->stream) | |
1186 | { | |
1187 | if (rpng->process->stream_backend && rpng->process->stream_backend->stream_free) | |
1188 | rpng->process->stream_backend->stream_free(rpng->process->stream); | |
1189 | else | |
1190 | free(rpng->process->stream); | |
1191 | } | |
1192 | free(rpng->process); | |
1193 | } | |
1194 | ||
1195 | free(rpng); | |
1196 | } | |
1197 | ||
1198 | bool rpng_start(rpng_t *rpng) | |
1199 | { | |
1200 | if (!rpng) | |
1201 | return false; | |
1202 | ||
1203 | /* Check whether reading the header will overflow | |
1204 | * the data buffer */ | |
1205 | if (rpng->buff_end - rpng->buff_data < 8) | |
1206 | return false; | |
1207 | ||
1208 | if (string_is_not_equal_fast( | |
1209 | rpng->buff_data, png_magic, sizeof(png_magic))) | |
1210 | return false; | |
1211 | ||
1212 | rpng->buff_data += 8; | |
1213 | ||
1214 | return true; | |
1215 | } | |
1216 | ||
1217 | /** | |
1218 | * rpng_is_valid: | |
1219 | * | |
1220 | * Check if @rpng is a valid PNG image. | |
1221 | * Must contain an IHDR chunk, one or more IDAT | |
1222 | * chunks, and an IEND chunk. | |
1223 | * | |
1224 | * Leaf function. | |
1225 | * | |
1226 | * @return true if it's a valid PNG image, otherwise false. | |
1227 | **/ | |
1228 | bool rpng_is_valid(rpng_t *rpng) | |
1229 | { | |
1230 | return (rpng && ((rpng->flags & (RPNG_FLAG_HAS_IHDR | RPNG_FLAG_HAS_IDAT | | |
1231 | RPNG_FLAG_HAS_IEND)) > 0)); | |
1232 | } | |
1233 | ||
1234 | bool rpng_set_buf_ptr(rpng_t *rpng, void *data, size_t len) | |
1235 | { | |
1236 | if (!rpng || (len < 1)) | |
1237 | return false; | |
1238 | ||
1239 | rpng->buff_data = (uint8_t*)data; | |
1240 | rpng->buff_end = rpng->buff_data + (len - 1); | |
1241 | ||
1242 | return true; | |
1243 | } | |
1244 | ||
1245 | rpng_t *rpng_alloc(void) | |
1246 | { | |
1247 | rpng_t *rpng = (rpng_t*)calloc(1, sizeof(*rpng)); | |
1248 | if (!rpng) | |
1249 | return NULL; | |
1250 | return rpng; | |
1251 | } |