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