git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / formats / png / rpng.c
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 }