bool rpng_load_image_argb_process_init(struct rpng_t *rpng, uint32_t **data, unsigned *width, unsigned *height) { rpng->process.inflate_buf_size = 0; rpng->process.inflate_buf = NULL; png_pass_geom(&rpng->ihdr, rpng->ihdr.width, rpng->ihdr.height, NULL, NULL, &rpng->process.inflate_buf_size); if (rpng->ihdr.interlace == 1) /* To be sure. */ rpng->process.inflate_buf_size *= 2; rpng->process.stream = zlib_stream_new(); if (!rpng->process.stream) return false; if (!zlib_inflate_init(rpng->process.stream)) return false; rpng->process.inflate_buf = (uint8_t*)malloc(rpng->process.inflate_buf_size); if (!rpng->process.inflate_buf) return false; zlib_set_stream( rpng->process.stream, rpng->idat_buf.size, rpng->process.inflate_buf_size, rpng->idat_buf.data, rpng->process.inflate_buf); rpng->process.initialized = true; return true; }
static bool png_reverse_filter_adam7(uint32_t *data, const struct png_ihdr *ihdr, const uint8_t *inflate_buf, size_t inflate_buf_size, const uint32_t *palette) { unsigned pass; static const struct adam7_pass passes[] = { { 0, 0, 8, 8 }, { 4, 0, 8, 8 }, { 0, 4, 4, 8 }, { 2, 0, 4, 4 }, { 0, 2, 2, 4 }, { 1, 0, 2, 2 }, { 0, 1, 1, 2 }, }; for (pass = 0; pass < ARRAY_SIZE(passes); pass++) { if (ihdr->width <= passes[pass].x || ihdr->height <= passes[pass].y) // Empty pass continue; unsigned pass_width = (ihdr->width - passes[pass].x + passes[pass].stride_x - 1) / passes[pass].stride_x; unsigned pass_height = (ihdr->height - passes[pass].y + passes[pass].stride_y - 1) / passes[pass].stride_y; uint32_t *tmp_data = (uint32_t*)malloc(pass_width * pass_height * sizeof(uint32_t)); if (!tmp_data) return false; struct png_ihdr tmp_ihdr = *ihdr; tmp_ihdr.width = pass_width; tmp_ihdr.height = pass_height; size_t pass_size; png_pass_geom(&tmp_ihdr, pass_width, pass_height, NULL, NULL, &pass_size); if (pass_size > inflate_buf_size) { free(tmp_data); return false; } if (!png_reverse_filter(tmp_data, &tmp_ihdr, inflate_buf, pass_size, palette)) { free(tmp_data); return false; } inflate_buf += pass_size; inflate_buf_size -= pass_size; deinterlace_pass(data, ihdr, tmp_data, pass_width, pass_height, &passes[pass]); free(tmp_data); } return true; }
static struct rpng_process *rpng_process_init(rpng_t *rpng, unsigned *width, unsigned *height) { uint8_t *inflate_buf = NULL; struct rpng_process *process = (struct rpng_process*)calloc(1, sizeof(*process)); if (!process) return NULL; process->stream_backend = file_archive_get_default_file_backend(); png_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; process->stream = process->stream_backend->stream_new(); if (!process->stream) { free(process); return NULL; } if (!process->stream_backend->stream_decompress_init(process->stream)) { free(process); return NULL; } inflate_buf = (uint8_t*)malloc(process->inflate_buf_size); if (!inflate_buf) goto error; process->inflate_buf = inflate_buf; process->stream_backend->stream_set( process->stream, rpng->idat_buf.size, process->inflate_buf_size, rpng->idat_buf.data, process->inflate_buf); return process; error: if (process) { if (process->stream) process->stream_backend->stream_free(process->stream); free(process); } return NULL; }
bool rpng_nbio_load_image_argb_process(struct rpng_t *rpng, uint32_t **data, unsigned *width, unsigned *height) { if (!rpng) return false; rpng->process.inflate_buf_size = 0; rpng->process.inflate_buf = NULL; if (inflateInit(&rpng->process.stream) != Z_OK) return false; png_pass_geom(&rpng->ihdr, rpng->ihdr.width, rpng->ihdr.height, NULL, NULL, &rpng->process.inflate_buf_size); if (rpng->ihdr.interlace == 1) /* To be sure. */ rpng->process.inflate_buf_size *= 2; rpng->process.inflate_buf = (uint8_t*)malloc(rpng->process.inflate_buf_size); if (!rpng->process.inflate_buf) return false; rpng->process.stream.next_in = rpng->idat_buf.data; rpng->process.stream.avail_in = rpng->idat_buf.size; rpng->process.stream.avail_out = rpng->process.inflate_buf_size; rpng->process.stream.next_out = rpng->process.inflate_buf; if (inflate(&rpng->process.stream, Z_FINISH) != Z_STREAM_END) { inflateEnd(&rpng->process.stream); return false; } inflateEnd(&rpng->process.stream); *width = rpng->ihdr.width; *height = rpng->ihdr.height; #ifdef GEKKO /* we often use these in textures, make sure they're 32-byte aligned */ *data = (uint32_t*)memalign(32, rpng->ihdr.width * rpng->ihdr.height * sizeof(uint32_t)); #else *data = (uint32_t*)malloc(rpng->ihdr.width * rpng->ihdr.height * sizeof(uint32_t)); #endif if (!*data) return false; if (!png_reverse_filter_loop(rpng, data)) return false; return true; }
static int png_reverse_filter_init(const struct png_ihdr *ihdr, struct rpng_process_t *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 */ 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; pngp->data = (uint32_t*)malloc( pngp->pass.width * pngp->pass.height * sizeof(uint32_t)); if (!pngp->data) 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, pngp->pass.height, NULL, NULL, &pngp->pass.size); if (pngp->pass.size > zlib_stream_get_total_out(pngp->stream)) { free(pngp->data); return -1; } pngp->adam7_pass_initialized = true; return 0; } if (pngp->pass_initialized) return 0; png_pass_geom(ihdr, ihdr->width, ihdr->height, &pngp->bpp, &pngp->pitch, &pass_size); if (zlib_stream_get_total_out(pngp->stream) < pass_size) return -1; pngp->restore_buf_size = 0; pngp->data_restore_buf_size = 0; pngp->prev_scanline = (uint8_t*)calloc(1, pngp->pitch); pngp->decoded_scanline = (uint8_t*)calloc(1, pngp->pitch); if (!pngp->prev_scanline || !pngp->decoded_scanline) goto error; pngp->h = 0; pngp->pass_initialized = true; return 0; error: png_reverse_filter_deinit(pngp); return -1; }
bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, unsigned *height) { long pos; *data = NULL; *width = 0; *height = 0; bool ret = true; FILE *file = fopen(path, "rb"); if (!file) return false; fseek(file, 0, SEEK_END); long file_len = ftell(file); rewind(file); bool has_ihdr = false; bool has_idat = false; bool has_iend = false; bool has_plte = false; uint8_t *inflate_buf = NULL; size_t inflate_buf_size = 0; z_stream stream = {0}; struct idat_buffer idat_buf = {0}; struct png_ihdr ihdr = {0}; uint32_t palette[256] = {0}; char header[8]; if (fread(header, 1, sizeof(header), file) != sizeof(header)) GOTO_END_ERROR(); if (memcmp(header, png_magic, sizeof(png_magic)) != 0) GOTO_END_ERROR(); // feof() apparently isn't triggered after a seek (IEND). for (pos = ftell(file); pos < file_len && pos >= 0; pos = ftell(file)) { struct png_chunk chunk = {0}; if (!read_chunk_header(file, &chunk)) GOTO_END_ERROR(); switch (png_chunk_type(&chunk)) { case PNG_CHUNK_NOOP: default: if (fseek(file, chunk.size + sizeof(uint32_t), SEEK_CUR) < 0) GOTO_END_ERROR(); break; case PNG_CHUNK_ERROR: GOTO_END_ERROR(); case PNG_CHUNK_IHDR: if (has_ihdr || has_idat || has_iend) GOTO_END_ERROR(); if (!png_parse_ihdr(file, &chunk, &ihdr)) GOTO_END_ERROR(); has_ihdr = true; break; case PNG_CHUNK_PLTE: if (!has_ihdr || has_plte || has_iend || has_idat) GOTO_END_ERROR(); if (chunk.size % 3) GOTO_END_ERROR(); if (!png_read_plte(file, palette, chunk.size / 3)) GOTO_END_ERROR(); has_plte = true; break; case PNG_CHUNK_IDAT: if (!has_ihdr || has_iend || (ihdr.color_type == 3 && !has_plte)) GOTO_END_ERROR(); if (!png_append_idat(file, &chunk, &idat_buf)) GOTO_END_ERROR(); has_idat = true; break; case PNG_CHUNK_IEND: if (!has_ihdr || !has_idat) GOTO_END_ERROR(); if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0) GOTO_END_ERROR(); has_iend = true; break; } } if (!has_ihdr || !has_idat || !has_iend) GOTO_END_ERROR(); if (inflateInit(&stream) != Z_OK) GOTO_END_ERROR(); png_pass_geom(&ihdr, ihdr.width, ihdr.height, NULL, NULL, &inflate_buf_size); if (ihdr.interlace == 1) // To be sure. inflate_buf_size *= 2; inflate_buf = (uint8_t*)malloc(inflate_buf_size); if (!inflate_buf) GOTO_END_ERROR(); stream.next_in = idat_buf.data; stream.avail_in = idat_buf.size; stream.avail_out = inflate_buf_size; stream.next_out = inflate_buf; if (inflate(&stream, Z_FINISH) != Z_STREAM_END) { inflateEnd(&stream); GOTO_END_ERROR(); } inflateEnd(&stream); *width = ihdr.width; *height = ihdr.height; *data = (uint32_t*)malloc(ihdr.width * ihdr.height * sizeof(uint32_t)); if (!*data) GOTO_END_ERROR(); if (ihdr.interlace == 1) { if (!png_reverse_filter_adam7(*data, &ihdr, inflate_buf, stream.total_out, palette)) GOTO_END_ERROR(); } else if (!png_reverse_filter(*data, &ihdr, inflate_buf, stream.total_out, palette)) GOTO_END_ERROR(); end: if (file) fclose(file); if (!ret) free(*data); free(idat_buf.data); free(inflate_buf); return ret; }
static bool png_reverse_filter(uint32_t *data, const struct png_ihdr *ihdr, const uint8_t *inflate_buf, size_t inflate_buf_size, const uint32_t *palette) { unsigned i, h; bool ret = true; unsigned bpp; unsigned pitch; size_t pass_size; png_pass_geom(ihdr, ihdr->width, ihdr->height, &bpp, &pitch, &pass_size); if (inflate_buf_size < pass_size) return false; uint8_t *prev_scanline = (uint8_t*)calloc(1, pitch); uint8_t *decoded_scanline = (uint8_t*)calloc(1, pitch); if (!prev_scanline || !decoded_scanline) GOTO_END_ERROR(); for (h = 0; h < ihdr->height; h++, inflate_buf += pitch, data += ihdr->width) { unsigned filter = *inflate_buf++; switch (filter) { case 0: // None memcpy(decoded_scanline, inflate_buf, pitch); break; case 1: // Sub for (i = 0; i < bpp; i++) decoded_scanline[i] = inflate_buf[i]; for (i = bpp; i < pitch; i++) decoded_scanline[i] = decoded_scanline[i - bpp] + inflate_buf[i]; break; case 2: // Up for (i = 0; i < pitch; i++) decoded_scanline[i] = prev_scanline[i] + inflate_buf[i]; break; case 3: // Average for (i = 0; i < bpp; i++) { uint8_t avg = prev_scanline[i] >> 1; decoded_scanline[i] = avg + inflate_buf[i]; } for (i = bpp; i < pitch; i++) { uint8_t avg = (decoded_scanline[i - bpp] + prev_scanline[i]) >> 1; decoded_scanline[i] = avg + inflate_buf[i]; } break; case 4: // Paeth for (i = 0; i < bpp; i++) decoded_scanline[i] = paeth(0, prev_scanline[i], 0) + inflate_buf[i]; for (i = bpp; i < pitch; i++) decoded_scanline[i] = paeth(decoded_scanline[i - bpp], prev_scanline[i], prev_scanline[i - bpp]) + inflate_buf[i]; break; default: GOTO_END_ERROR(); } if (ihdr->color_type == 0) copy_line_bw(data, decoded_scanline, ihdr->width, ihdr->depth); else if (ihdr->color_type == 2) copy_line_rgb(data, decoded_scanline, ihdr->width, ihdr->depth); else if (ihdr->color_type == 3) copy_line_plt(data, decoded_scanline, ihdr->width, ihdr->depth, palette); else if (ihdr->color_type == 4) copy_line_gray_alpha(data, decoded_scanline, ihdr->width, ihdr->depth); else if (ihdr->color_type == 6) copy_line_rgba(data, decoded_scanline, ihdr->width, ihdr->depth); memcpy(prev_scanline, decoded_scanline, pitch); } end: free(decoded_scanline); free(prev_scanline); return ret; }