From 33c9f56df0ccb341285b1ec7db76683edd7fc147 Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Tue, 2 Aug 2022 15:55:26 +0200 Subject: [PATCH] (RPNG) PNG decoder - proper namespacing of functions, general cleanups --- libretro-common/formats/png/rpng.c | 230 ++++++++++++++--------------- 1 file changed, 110 insertions(+), 120 deletions(-) diff --git a/libretro-common/formats/png/rpng.c b/libretro-common/formats/png/rpng.c index 5c7eaacd15..43d3d4ccf4 100644 --- a/libretro-common/formats/png/rpng.c +++ b/libretro-common/formats/png/rpng.c @@ -126,7 +126,7 @@ struct rpng bool has_trns; }; -static const struct adam7_pass passes[] = { +static const struct adam7_pass rpng_passes[] = { { 0, 0, 8, 8 }, { 4, 0, 8, 8 }, { 0, 4, 4, 8 }, @@ -136,13 +136,13 @@ static const struct adam7_pass passes[] = { { 0, 1, 1, 2 }, }; -static INLINE uint32_t dword_be(const uint8_t *buf) +static INLINE uint32_t rpng_dword_be(const uint8_t *buf) { return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0); } #if defined(DEBUG) || defined(RPNG_TEST) -static bool png_process_ihdr(struct png_ihdr *ihdr) +static bool rpng_process_ihdr(struct png_ihdr *ihdr) { uint8_t ihdr_depth = ihdr->depth; @@ -190,7 +190,7 @@ static bool png_process_ihdr(struct png_ihdr *ihdr) return true; } #else -static bool png_process_ihdr(struct png_ihdr *ihdr) +static bool rpng_process_ihdr(struct png_ihdr *ihdr) { uint8_t ihdr_depth = ihdr->depth; @@ -220,10 +220,10 @@ static bool png_process_ihdr(struct png_ihdr *ihdr) } #endif -static void png_reverse_filter_copy_line_rgb(uint32_t *data, +static void rpng_reverse_filter_copy_line_rgb(uint32_t *data, const uint8_t *decoded, unsigned width, unsigned bpp) { - unsigned i; + int i; bpp /= 8; @@ -241,10 +241,10 @@ static void png_reverse_filter_copy_line_rgb(uint32_t *data, } } -static void png_reverse_filter_copy_line_rgba(uint32_t *data, +static void rpng_reverse_filter_copy_line_rgba(uint32_t *data, const uint8_t *decoded, unsigned width, unsigned bpp) { - unsigned i; + int i; bpp /= 8; @@ -263,10 +263,11 @@ static void png_reverse_filter_copy_line_rgba(uint32_t *data, } } -static void png_reverse_filter_copy_line_bw(uint32_t *data, +static void rpng_reverse_filter_copy_line_bw(uint32_t *data, const uint8_t *decoded, unsigned width, unsigned depth) { - unsigned i, bit; + int i; + unsigned bit; static const unsigned mul_table[] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; unsigned mul, mask; @@ -295,11 +296,11 @@ static void png_reverse_filter_copy_line_bw(uint32_t *data, } } -static void png_reverse_filter_copy_line_gray_alpha(uint32_t *data, +static void rpng_reverse_filter_copy_line_gray_alpha(uint32_t *data, const uint8_t *decoded, unsigned width, unsigned bpp) { - unsigned i; + int i; bpp /= 8; @@ -316,7 +317,7 @@ static void png_reverse_filter_copy_line_gray_alpha(uint32_t *data, } } -static void png_reverse_filter_copy_line_plt(uint32_t *data, +static void rpng_reverse_filter_copy_line_plt(uint32_t *data, const uint8_t *decoded, unsigned width, unsigned depth, const uint32_t *palette) { @@ -325,7 +326,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data, case 1: { unsigned w = width / 8; - unsigned i; + int i; for (i = 0; i < w; i++, decoded++) { @@ -363,7 +364,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data, case 2: { unsigned w = width / 4; - unsigned i; + int i; for (i = 0; i < w; i++, decoded++) { @@ -389,7 +390,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data, case 4: { unsigned w = width / 2; - unsigned i; + int i; for (i = 0; i < w; i++, decoded++) { @@ -404,7 +405,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data, case 8: { - unsigned i; + int i; for (i = 0; i < width; i++, decoded++, data++) *data = palette[*decoded]; @@ -413,7 +414,7 @@ static void png_reverse_filter_copy_line_plt(uint32_t *data, } } -static void png_pass_geom(const struct png_ihdr *ihdr, +static void rpng_pass_geom(const struct png_ihdr *ihdr, unsigned width, unsigned height, unsigned *bpp_out, unsigned *pitch_out, size_t *pass_size) { @@ -454,7 +455,7 @@ static void png_pass_geom(const struct png_ihdr *ihdr, *pitch_out = pitch; } -static void png_reverse_filter_adam7_deinterlace_pass(uint32_t *data, +static void rpng_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) @@ -473,7 +474,7 @@ static void png_reverse_filter_adam7_deinterlace_pass(uint32_t *data, } } -static void png_reverse_filter_deinit(struct rpng_process *pngp) +static void rpng_reverse_filter_deinit(struct rpng_process *pngp) { if (!pngp) return; @@ -488,33 +489,32 @@ static void png_reverse_filter_deinit(struct rpng_process *pngp) pngp->h = 0; } -static int png_reverse_filter_init(const struct png_ihdr *ihdr, +static int rpng_reverse_filter_init(const struct png_ihdr *ihdr, struct rpng_process *pngp) { size_t pass_size; if (!pngp->adam7_pass_initialized && ihdr->interlace) { - if (ihdr->width <= passes[pngp->pass_pos].x || - ihdr->height <= passes[pngp->pass_pos].y) /* Empty pass */ + if ( ihdr->width <= rpng_passes[pngp->pass_pos].x + || ihdr->height <= rpng_passes[pngp->pass_pos].y) /* Empty pass */ return 1; pngp->pass_width = (ihdr->width - - passes[pngp->pass_pos].x + passes[pngp->pass_pos].stride_x - 1) / passes[pngp->pass_pos].stride_x; - pngp->pass_height = (ihdr->height - passes[pngp->pass_pos].y + - passes[pngp->pass_pos].stride_y - 1) / passes[pngp->pass_pos].stride_y; + rpng_passes[pngp->pass_pos].x + rpng_passes[pngp->pass_pos].stride_x +- 1) / rpng_passes[pngp->pass_pos].stride_x; + pngp->pass_height = (ihdr->height - rpng_passes[pngp->pass_pos].y + + rpng_passes[pngp->pass_pos].stride_y - 1) / rpng_passes[pngp->pass_pos].stride_y; - pngp->data = (uint32_t*)malloc( - pngp->pass_width * pngp->pass_height * sizeof(uint32_t)); - - if (!pngp->data) + if (!(pngp->data = (uint32_t*)malloc( + pngp->pass_width * pngp->pass_height * sizeof(uint32_t)))) return -1; pngp->ihdr = *ihdr; pngp->ihdr.width = pngp->pass_width; pngp->ihdr.height = pngp->pass_height; - png_pass_geom(&pngp->ihdr, pngp->pass_width, + rpng_pass_geom(&pngp->ihdr, pngp->pass_width, pngp->pass_height, NULL, NULL, &pngp->pass_size); if (pngp->pass_size > pngp->total_out) @@ -532,7 +532,7 @@ static int png_reverse_filter_init(const struct png_ihdr *ihdr, if (pngp->pass_initialized) return 0; - png_pass_geom(ihdr, ihdr->width, ihdr->height, &pngp->bpp, &pngp->pitch, &pass_size); + rpng_pass_geom(ihdr, ihdr->width, ihdr->height, &pngp->bpp, &pngp->pitch, &pass_size); if (pngp->total_out < pass_size) return -1; @@ -551,11 +551,12 @@ static int png_reverse_filter_init(const struct png_ihdr *ihdr, return 0; error: - png_reverse_filter_deinit(pngp); + rpng_reverse_filter_deinit(pngp); return -1; } -static int png_reverse_filter_copy_line(uint32_t *data, const struct png_ihdr *ihdr, +static int rpng_reverse_filter_copy_line(uint32_t *data, + const struct png_ihdr *ihdr, struct rpng_process *pngp, unsigned filter) { unsigned i; @@ -603,21 +604,22 @@ static int png_reverse_filter_copy_line(uint32_t *data, const struct png_ihdr *i switch (ihdr->color_type) { case PNG_IHDR_COLOR_GRAY: - png_reverse_filter_copy_line_bw(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); + rpng_reverse_filter_copy_line_bw(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); break; case PNG_IHDR_COLOR_RGB: - png_reverse_filter_copy_line_rgb(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); + rpng_reverse_filter_copy_line_rgb(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); break; case PNG_IHDR_COLOR_PLT: - png_reverse_filter_copy_line_plt(data, pngp->decoded_scanline, ihdr->width, + rpng_reverse_filter_copy_line_plt( + data, pngp->decoded_scanline, ihdr->width, ihdr->depth, pngp->palette); break; case PNG_IHDR_COLOR_GRAY_ALPHA: - png_reverse_filter_copy_line_gray_alpha(data, pngp->decoded_scanline, ihdr->width, + rpng_reverse_filter_copy_line_gray_alpha(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); break; case PNG_IHDR_COLOR_RGBA: - png_reverse_filter_copy_line_rgba(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); + rpng_reverse_filter_copy_line_rgba(data, pngp->decoded_scanline, ihdr->width, ihdr->depth); break; } @@ -626,7 +628,8 @@ static int png_reverse_filter_copy_line(uint32_t *data, const struct png_ihdr *i return IMAGE_PROCESS_NEXT; } -static int png_reverse_filter_regular_iterate(uint32_t **data, const struct png_ihdr *ihdr, +static int rpng_reverse_filter_regular_iterate( + uint32_t **data, const struct png_ihdr *ihdr, struct rpng_process *pngp) { int ret = IMAGE_PROCESS_END; @@ -634,7 +637,7 @@ static int png_reverse_filter_regular_iterate(uint32_t **data, const struct png_ { unsigned filter = *pngp->inflate_buf++; pngp->restore_buf_size += 1; - ret = png_reverse_filter_copy_line(*data, + ret = rpng_reverse_filter_copy_line(*data, ihdr, pngp, filter); if (ret == IMAGE_PROCESS_END || ret == IMAGE_PROCESS_ERROR_END) goto end; @@ -652,7 +655,7 @@ static int png_reverse_filter_regular_iterate(uint32_t **data, const struct png_ return IMAGE_PROCESS_NEXT; end: - png_reverse_filter_deinit(pngp); + rpng_reverse_filter_deinit(pngp); pngp->inflate_buf -= pngp->restore_buf_size; *data -= pngp->data_restore_buf_size; @@ -660,28 +663,28 @@ end: return ret; } -static int png_reverse_filter_adam7_iterate(uint32_t **data_, +static int rpng_reverse_filter_adam7_iterate(uint32_t **data_, const struct png_ihdr *ihdr, struct rpng_process *pngp) { int ret = 0; - bool to_next = pngp->pass_pos < ARRAY_SIZE(passes); + bool to_next = pngp->pass_pos < ARRAY_SIZE(rpng_passes); uint32_t *data = *data_; if (!to_next) return IMAGE_PROCESS_END; - if ((ret = png_reverse_filter_init(ihdr, pngp)) == 1) + if ((ret = rpng_reverse_filter_init(ihdr, pngp)) == 1) return IMAGE_PROCESS_NEXT; else if (ret == -1) return IMAGE_PROCESS_ERROR_END; - if (png_reverse_filter_init(&pngp->ihdr, pngp) == -1) + if (rpng_reverse_filter_init(&pngp->ihdr, pngp) == -1) return IMAGE_PROCESS_ERROR; do { - ret = png_reverse_filter_regular_iterate(&pngp->data, + ret = rpng_reverse_filter_regular_iterate(&pngp->data, &pngp->ihdr, pngp); } while (ret == IMAGE_PROCESS_NEXT); @@ -693,8 +696,9 @@ static int png_reverse_filter_adam7_iterate(uint32_t **data_, pngp->total_out -= pngp->pass_size; - png_reverse_filter_adam7_deinterlace_pass(data, - ihdr, pngp->data, pngp->pass_width, pngp->pass_height, &passes[pngp->pass_pos]); + rpng_reverse_filter_adam7_deinterlace_pass(data, + ihdr, pngp->data, pngp->pass_width, pngp->pass_height, + &rpng_passes[pngp->pass_pos]); free(pngp->data); @@ -707,11 +711,11 @@ static int png_reverse_filter_adam7_iterate(uint32_t **data_, return IMAGE_PROCESS_NEXT; } -static int png_reverse_filter_adam7(uint32_t **data_, +static int rpng_reverse_filter_adam7(uint32_t **data_, const struct png_ihdr *ihdr, struct rpng_process *pngp) { - int ret = png_reverse_filter_adam7_iterate(data_, + int ret = rpng_reverse_filter_adam7_iterate(data_, ihdr, pngp); switch (ret) @@ -738,18 +742,8 @@ static int png_reverse_filter_adam7(uint32_t **data_, return ret; } -static int png_reverse_filter_iterate(rpng_t *rpng, uint32_t **data) -{ - if (!rpng) - return false; - - if (rpng->ihdr.interlace && rpng->process) - return png_reverse_filter_adam7(data, &rpng->ihdr, rpng->process); - - return png_reverse_filter_regular_iterate(data, &rpng->ihdr, rpng->process); -} - -static int rpng_load_image_argb_process_inflate_init(rpng_t *rpng, uint32_t **data) +static int rpng_load_image_argb_process_inflate_init( + rpng_t *rpng, uint32_t **data) { bool zstatus; enum trans_stream_error terror; @@ -793,7 +787,7 @@ end: process->palette = rpng->palette; if (rpng->ihdr.interlace != 1) - if (png_reverse_filter_init(&rpng->ihdr, process) == -1) + if (rpng_reverse_filter_init(&rpng->ihdr, process) == -1) goto false_end; process->inflate_initialized = true; @@ -805,33 +799,7 @@ false_end: return -1; } -static bool png_read_plte(uint8_t *buf, - uint32_t *buffer, unsigned entries) -{ - unsigned i; - - for (i = 0; i < entries; i++) - { - uint32_t r = buf[3 * i + 0]; - uint32_t g = buf[3 * i + 1]; - uint32_t b = buf[3 * i + 2]; - buffer[i] = (r << 16) | (g << 8) | (b << 0) | (0xffu << 24); - } - - return true; -} - -static bool png_read_trns(uint8_t *buf, uint32_t *palette, unsigned entries) -{ - unsigned i; - - for (i = 0; i < entries; i++, buf++, palette++) - *palette = (*palette & 0x00ffffff) | (unsigned)*buf << 24; - - return true; -} - -bool png_realloc_idat(struct idat_buffer *buf, uint32_t chunk_size) +static bool rpng_realloc_idat(struct idat_buffer *buf, uint32_t chunk_size) { uint8_t *new_buffer = (uint8_t*)realloc(buf->data, buf->size + chunk_size); @@ -884,7 +852,7 @@ static struct rpng_process *rpng_process_init(rpng_t *rpng) process->stream = NULL; process->stream_backend = trans_stream_get_zlib_inflate_backend(); - png_pass_geom(&rpng->ihdr, rpng->ihdr.width, + rpng_pass_geom(&rpng->ihdr, rpng->ihdr.width, rpng->ihdr.height, NULL, NULL, &process->inflate_buf_size); if (rpng->ihdr.interlace == 1) /* To be sure. */ process->inflate_buf_size *= 2; @@ -926,10 +894,18 @@ error: return NULL; } -static enum png_chunk_type read_chunk_header( +/** + * rpng_read_chunk_header: + * + * Leaf function. + * + * @return The PNG type of the memory chunk (i.e. IHDR, IDAT, IEND, + PLTE, and/or tRNS) + **/ +static enum png_chunk_type rpng_read_chunk_header( uint8_t *buf, uint32_t chunk_size) { - unsigned i; + int i; char type[4]; for (i = 0; i < 4; i++) @@ -1001,13 +977,13 @@ bool rpng_iterate_image(rpng_t *rpng) if (rpng->buff_end - buf < 8) return false; - chunk_size = dword_be(buf); + chunk_size = rpng_dword_be(buf); /* Check whether chunk will overflow the data buffer */ if (buf + 8 + chunk_size > rpng->buff_end) return false; - switch (read_chunk_header(buf, chunk_size)) + switch (rpng_read_chunk_header(buf, chunk_size)) { case PNG_CHUNK_NOOP: default: @@ -1025,8 +1001,8 @@ bool rpng_iterate_image(rpng_t *rpng) buf += 4 + 4; - rpng->ihdr.width = dword_be(buf + 0); - rpng->ihdr.height = dword_be(buf + 4); + rpng->ihdr.width = rpng_dword_be(buf + 0); + rpng->ihdr.height = rpng_dword_be(buf + 4); rpng->ihdr.depth = buf[8]; rpng->ihdr.color_type = buf[9]; rpng->ihdr.compression = buf[10]; @@ -1039,7 +1015,7 @@ bool rpng_iterate_image(rpng_t *rpng) || (uint64_t)rpng->ihdr.width*rpng->ihdr.height*sizeof(uint32_t) >= 0x80000000) return false; - if (!png_process_ihdr(&rpng->ihdr)) + if (!rpng_process_ihdr(&rpng->ihdr)) return false; if (rpng->ihdr.compression != 0) @@ -1055,8 +1031,14 @@ bool rpng_iterate_image(rpng_t *rpng) case PNG_CHUNK_PLTE: { + int i; unsigned entries = chunk_size / 3; + if (entries > 256) + return false; + if (chunk_size % 3) + return false; + if ( !rpng->has_ihdr || rpng->has_plte || rpng->has_iend @@ -1064,16 +1046,15 @@ bool rpng_iterate_image(rpng_t *rpng) || rpng->has_trns) return false; - if (chunk_size % 3) - return false; - - if (entries > 256) - return false; - buf += 8; - if (!png_read_plte(buf, rpng->palette, entries)) - return false; + for (i = 0; i < (int)entries; i++) + { + uint32_t r = buf[3 * i + 0]; + uint32_t g = buf[3 * i + 1]; + uint32_t b = buf[3 * i + 2]; + rpng->palette[i] = (r << 16) | (g << 8) | (b << 0) | (0xffu << 24); + } rpng->has_plte = true; } @@ -1085,14 +1066,17 @@ bool rpng_iterate_image(rpng_t *rpng) if (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT) { + int i; + uint32_t *palette; /* we should compare with the number of palette entries */ if (chunk_size > 256) return false; - buf += 8; + buf += 8; + palette = rpng->palette; - if (!png_read_trns(buf, rpng->palette, chunk_size)) - return false; + for (i = 0; i < chunk_size; i++, buf++, palette++) + *palette = (*palette & 0x00ffffff) | (unsigned)*buf << 24; } /* TODO: support colorkey in grayscale and truecolor images */ @@ -1103,7 +1087,7 @@ bool rpng_iterate_image(rpng_t *rpng) if (!(rpng->has_ihdr) || rpng->has_iend || (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT && !(rpng->has_plte))) return false; - if (!png_realloc_idat(&rpng->idat_buf, chunk_size)) + if (!rpng_realloc_idat(&rpng->idat_buf, chunk_size)) return false; buf += 8; @@ -1137,8 +1121,6 @@ int rpng_process_image(rpng_t *rpng, { uint32_t **data = (uint32_t**)_data; - (void)size; - if (!rpng->process) { struct rpng_process *process = rpng_process_init(rpng); @@ -1160,7 +1142,9 @@ int rpng_process_image(rpng_t *rpng, *width = rpng->ihdr.width; *height = rpng->ihdr.height; - return png_reverse_filter_iterate(rpng, data); + if (rpng->ihdr.interlace && rpng->process) + return rpng_reverse_filter_adam7(data, &rpng->ihdr, rpng->process); + return rpng_reverse_filter_regular_iterate(data, &rpng->ihdr, rpng->process); error: if (rpng->process) @@ -1218,14 +1202,20 @@ bool rpng_start(rpng_t *rpng) return true; } +/** + * rpng_is_valid: + * + * Check if @rpng is a valid PNG image. + * Must contain an IHDR chunk, one or more IDAT + * chunks, and an IEND chunk. + * + * Leaf function. + * + * @return true if it's a valid PNG image, otherwise false. + **/ bool rpng_is_valid(rpng_t *rpng) { - /* A valid PNG image must contain an IHDR chunk, - * one or more IDAT chunks, and an IEND chunk */ - if (rpng && rpng->has_ihdr && rpng->has_idat && rpng->has_iend) - return true; - - return false; + return (rpng && rpng->has_ihdr && rpng->has_idat && rpng->has_iend); } bool rpng_set_buf_ptr(rpng_t *rpng, void *data, size_t len)