static bool png_parse_ihdr(FILE *file, struct png_chunk *chunk, struct png_ihdr *ihdr) { unsigned i; bool ret = true; if (!png_read_chunk(file, chunk)) return false; if (chunk->size != 13) GOTO_END_ERROR(); ihdr->width = dword_be(chunk->data + 0); ihdr->height = dword_be(chunk->data + 4); ihdr->depth = chunk->data[8]; ihdr->color_type = chunk->data[9]; ihdr->compression = chunk->data[10]; ihdr->filter = chunk->data[11]; ihdr->interlace = chunk->data[12]; if (ihdr->width == 0 || ihdr->height == 0) GOTO_END_ERROR(); if (ihdr->color_type == 2 || ihdr->color_type == 4 || ihdr->color_type == 6) { if (ihdr->depth != 8 && ihdr->depth != 16) GOTO_END_ERROR(); } else if (ihdr->color_type == 0) { static const unsigned valid_bpp[] = { 1, 2, 4, 8, 16 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else if (ihdr->color_type == 3) { static const unsigned valid_bpp[] = { 1, 2, 4, 8 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else GOTO_END_ERROR(); #ifdef RPNG_TEST fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n", ihdr->width, ihdr->height, ihdr->depth, ihdr->color_type == 3 ? "yes" : "no", ihdr->color_type & 2 ? "yes" : "no", ihdr->color_type & 4 ? "yes" : "no", ihdr->interlace == 1 ? "yes" : "no"); #endif if (ihdr->compression != 0) GOTO_END_ERROR(); //if (ihdr->interlace != 0) // No Adam7 supported. // GOTO_END_ERROR(); end: png_free_chunk(chunk); return ret; }
static bool rpng_load_image_argb_iterate(FILE **fd, rpng_t *rpng) { struct png_chunk chunk = {0}; FILE *file = *fd; if (!read_chunk_header_fio(fd, &chunk)) return false; switch (png_chunk_type(&chunk)) { case PNG_CHUNK_NOOP: default: if (fseek(file, chunk.size + sizeof(uint32_t), SEEK_CUR) < 0) return false; break; case PNG_CHUNK_ERROR: return false; case PNG_CHUNK_IHDR: if (rpng_is_valid(rpng)) return false; if (!png_parse_ihdr_fio(fd, &chunk, &rpng->ihdr)) { png_free_chunk(&chunk); return false; } if (!png_process_ihdr(&rpng->ihdr)) { png_free_chunk(&chunk); return false; } png_free_chunk(&chunk); rpng->has_ihdr = true; break; case PNG_CHUNK_PLTE: { uint8_t buf[256 * 3]; unsigned entries = chunk.size / 3; if (!rpng->has_ihdr || rpng->has_plte || rpng->has_iend || rpng->has_idat) return false; if (chunk.size % 3) return false; if (entries > 256) return false; if (fread(rpng->palette, 3, entries, *fd) != entries) return false; if (!png_read_plte(&buf[0], rpng->palette, entries)) return false; if (fseek(*fd, sizeof(uint32_t), SEEK_CUR) < 0) return false; rpng->has_plte = true; } break; case PNG_CHUNK_IDAT: if (!rpng->has_ihdr || rpng->has_iend || (rpng->ihdr.color_type == PNG_IHDR_COLOR_PLT && !rpng->has_plte)) return false; if (!png_realloc_idat(&chunk, &rpng->idat_buf)) return false; if (fread(rpng->idat_buf.data + rpng->idat_buf.size, 1, chunk.size, file) != chunk.size) return false; if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0) return false; rpng->idat_buf.size += chunk.size; rpng->has_idat = true; break; case PNG_CHUNK_IEND: if (!rpng->has_ihdr || !rpng->has_idat) return false; if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0) return false; rpng->has_iend = true; break; } return true; }