diff options
Diffstat (limited to 'lisiblepng')
| -rw-r--r-- | lisiblepng/meson.build | 7 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng.c | 290 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng/assert.h (renamed from lisiblepng/src/assert.h) | 4 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng/bitstream.c (renamed from lisiblepng/src/bitstream.c) | 0 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng/bitstream.h (renamed from lisiblepng/src/bitstream.h) | 0 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng/deflate.c (renamed from lisiblepng/src/deflate.c) | 87 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng/deflate.h (renamed from lisiblepng/src/deflate.h) | 6 | ||||
| -rw-r--r-- | lisiblepng/src/lisiblepng/log.h | 23 | ||||
| -rw-r--r-- | lisiblepng/src/log.h | 9 |
9 files changed, 353 insertions, 73 deletions
diff --git a/lisiblepng/meson.build b/lisiblepng/meson.build index 224011a..22510fe 100644 --- a/lisiblepng/meson.build +++ b/lisiblepng/meson.build @@ -1,3 +1,6 @@ +cc = meson.get_compiler('c') +m_dep = cc.find_library('m', required: true) + lisiblepng_incdir = include_directories('src/') -lisiblepng_lib = library('lisiblepng', 'src/lisiblepng.c', 'src/deflate.c', 'src/bitstream.c') -lisiblepng_dep = declare_dependency(include_directories: lisiblepng_incdir, link_with: [lisiblepng_lib]) +lisiblepng_lib = library('lisiblepng', 'src/lisiblepng.c', 'src/lisiblepng/deflate.c', 'src/lisiblepng/bitstream.c', dependencies: [m_dep]) +lisiblepng_dep = declare_dependency(include_directories: lisiblepng_incdir, link_with: [lisiblepng_lib], dependencies: [m_dep]) diff --git a/lisiblepng/src/lisiblepng.c b/lisiblepng/src/lisiblepng.c index 5ccd300..7a3818b 100644 --- a/lisiblepng/src/lisiblepng.c +++ b/lisiblepng/src/lisiblepng.c @@ -1,7 +1,7 @@ #include "lisiblepng.h" -#include "assert.h" -#include "deflate.h" -#include "log.h" +#include "lisiblepng/assert.h" +#include "lisiblepng/deflate.h" +#include "lisiblepng/log.h" #include <errno.h> #include <stdbool.h> #include <stdint.h> @@ -61,8 +61,39 @@ const uint32_t CRC32_TABLE[256] = { 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; +typedef enum { + ColourType_Greyscale = 0, + ColourType_Truecolour = 2, + ColourType_IndexedColour = 3, + ColourType_GreyscaleWithAlpha = 4, + ColourType_TruecolourWithAlpha = 6, + ColourType_Unknown +} ColourType; + +uint8_t ColourType_sample_count(const ColourType colour_type) { + switch (colour_type) { + case ColourType_Greyscale: + return 1; + case ColourType_Truecolour: + return 3; + case ColourType_IndexedColour: + return 1; + case ColourType_GreyscaleWithAlpha: + return 2; + case ColourType_TruecolourWithAlpha: + return 4; + default: + LPNG_LOG_ERR0("Unknown colour type"); + abort(); + } +} + struct Png { - const uint8_t *data; + char *data; + size_t width; + size_t height; + ColourType colour_type; + uint8_t bits_per_sample; }; typedef struct { @@ -103,7 +134,7 @@ long ParsingContext_cursor_position(DeflateDecompressor *ctx) { bool ParsingContext_skip_bytes(DeflateDecompressor *ctx, size_t byte_count) { ASSERT(ctx != NULL); if (fseek(ctx->stream, byte_count, SEEK_CUR) != 0) { - LOGN("Couldn't skip bytes: %s", strerror(errno)); + LPNG_LOG_ERR("Couldn't skip bytes: %s", strerror(errno)); return false; } @@ -115,7 +146,7 @@ bool ParsingContext_parse_bytes(DeflateDecompressor *ctx, size_t byte_count, ASSERT(ctx != NULL); ASSERT(output_buffer != NULL); if (fread(output_buffer, 1, byte_count, ctx->stream) < byte_count) { - LOG0("Couldn't parse bytes, EOF reached"); + LPNG_LOG_ERR0("Couldn't parse bytes, EOF reached"); return false; } @@ -180,16 +211,17 @@ typedef struct { void ImageHeader_print_image_header(const ImageHeader *image_header) { ASSERT(image_header != NULL); - LOGN("Image header:\n" - "- dimensions: %dx%d\n" - "- bit depth: %d\n" - "- colour type: %d\n" - "- compression method: %d\n" - "- filter method: %d\n" - "- interlace method: %d", - image_header->width, image_header->height, image_header->bit_depth, - image_header->colour_type, image_header->compression_method, - image_header->filter_method, image_header->interlace_method); + LPNG_LOG_DBG("Image header:\n" + "- dimensions: %dx%d\n" + "- bit depth: %d\n" + "- colour type: %d\n" + "- compression method: %d\n" + "- filter method: %d\n" + "- interlace method: %d", + image_header->width, image_header->height, + image_header->bit_depth, image_header->colour_type, + image_header->compression_method, image_header->filter_method, + image_header->interlace_method); } bool ParsingContext_validate_crc_if_required(DeflateDecompressor *ctx) { @@ -198,7 +230,7 @@ bool ParsingContext_validate_crc_if_required(DeflateDecompressor *ctx) { uint32_t crc; PARSE_FIELD(uint32_t, crc); if (computed_crc != crc) { - LOG0("Invalid CRC checksum"); + LPNG_LOG_ERR0("Invalid CRC checksum"); return false; } #else @@ -208,6 +240,11 @@ bool ParsingContext_validate_crc_if_required(DeflateDecompressor *ctx) { return true; } +bool is_valid_bit_depth(uint8_t bit_depth) { + return bit_depth == 1 || bit_depth == 2 || bit_depth == 4 || bit_depth == 8 || + bit_depth == 16; +} + bool parse_IHDR_chunk(DeflateDecompressor *ctx, ImageHeader *image_header) { ASSERT(ctx != NULL); ASSERT(image_header != NULL); @@ -220,7 +257,7 @@ bool parse_IHDR_chunk(DeflateDecompressor *ctx, ImageHeader *image_header) { uint32_t type; PARSE_FIELD(uint32_t, type); if (type != IHDR_CHUNK_TYPE) { - LOG0("Expected IHDR chunk"); + LPNG_LOG_ERR0("Expected IHDR chunk"); return false; } @@ -228,6 +265,10 @@ bool parse_IHDR_chunk(DeflateDecompressor *ctx, ImageHeader *image_header) { PARSE_FIELD(uint32_t, image_header->width); PARSE_FIELD(uint32_t, image_header->height); PARSE_FIELD(uint8_t, image_header->bit_depth); + if (!is_valid_bit_depth(image_header->bit_depth)) { + LPNG_LOG_ERR("Invalid bitdepth: %d", image_header->bit_depth); + return false; + } PARSE_FIELD(uint8_t, image_header->colour_type); PARSE_FIELD(uint8_t, image_header->compression_method); PARSE_FIELD(uint8_t, image_header->filter_method); @@ -265,6 +306,119 @@ uint32_t uint32_t_to_le(uint32_t value) { (value_bytes[2] << 8) + value_bytes[3]; } +uint8_t none_reconstruction_function(uint8_t recon_a, uint8_t recon_b, + uint8_t recon_c, uint8_t x) { + (void)recon_a; + (void)recon_b; + (void)recon_c; + return x; +} +uint8_t sub_reconstruction_function(uint8_t recon_a, uint8_t recon_b, + uint8_t recon_c, uint8_t x) { + (void)recon_b; + (void)recon_c; + return x + recon_a; +} +uint8_t up_reconstruction_function(uint8_t recon_a, uint8_t recon_b, + uint8_t recon_c, uint8_t x) { + (void)recon_a; + (void)recon_c; + return x + recon_b; +} +uint8_t average_reconstruction_function(uint8_t recon_a, uint8_t recon_b, + uint8_t recon_c, uint8_t x) { + (void)recon_c; + uint16_t sum = (recon_a + recon_b) / 2u; + return x + sum; +} +uint8_t paeth_predictor(uint8_t a, uint8_t b, uint8_t c) { + int16_t p = a + b - c; + int16_t pa = abs(p - a); + int16_t pb = abs(p - b); + int16_t pc = abs(p - c); + uint8_t pr; + if (pa <= pb && pa <= pc) { + pr = a; + } else if (pb <= pc) { + pr = b; + } else { + pr = c; + } + + return pr; +} +uint8_t paeth_reconstruction_function(uint8_t recon_a, uint8_t recon_b, + uint8_t recon_c, uint8_t x) { + return x + paeth_predictor(recon_a, recon_b, recon_c); +} +typedef uint8_t (*ReconstructionFn)(uint8_t, uint8_t, uint8_t, uint8_t); +static const ReconstructionFn reconstruction_functions[] = { + none_reconstruction_function, sub_reconstruction_function, + up_reconstruction_function, average_reconstruction_function, + paeth_reconstruction_function}; + +void apply_reconstruction_functions_to_scanline( + char *output_scanline, const char *input_scanline, + const char *previous_output_scanline, size_t filter_type, + size_t scanline_size, size_t bytes_per_pixel) { + ASSERT(output_scanline != NULL); + ASSERT(input_scanline != NULL); + ASSERT(filter_type < + sizeof(reconstruction_functions) / sizeof(ReconstructionFn)); + + for (size_t i = 0; i < scanline_size / bytes_per_pixel; i++) { + for (size_t p = 0; p < bytes_per_pixel; p++) { + uint8_t a = 0; + if (i > 0) { + a = output_scanline[i * bytes_per_pixel - bytes_per_pixel + p]; + } + + uint8_t b = 0; + uint8_t c = 0; + if (previous_output_scanline != NULL) { + b = previous_output_scanline[i * bytes_per_pixel + p]; + if (i > 0) { + c = previous_output_scanline[i * bytes_per_pixel - bytes_per_pixel + + p]; + } + } + + uint8_t x = input_scanline[i * bytes_per_pixel + p]; + output_scanline[i * bytes_per_pixel + p] = + reconstruction_functions[filter_type](a, b, c, x); + } + } +} + +void apply_reconstruction_functions(Png *image, + const char *decompressed_data_buffer) { + ASSERT(image != NULL); + ASSERT(decompressed_data_buffer != NULL); + size_t sample_count = ColourType_sample_count(image->colour_type); + size_t bits_per_pixel = (image->bits_per_sample * sample_count); + size_t bytes_per_pixel = bits_per_pixel / 8; + if (image->bits_per_sample < 8) { + bytes_per_pixel = 1; + } + size_t scanline_size = image->width * bits_per_pixel / 8; + + for (size_t scanline = 0; scanline < image->height; scanline++) { + size_t output_scanline_start = scanline_size * scanline; + size_t input_scanline_start = (scanline_size + 1) * scanline; + uint8_t filter_type = decompressed_data_buffer[input_scanline_start]; + const char *previous_output_scanline = NULL; + if (scanline > 0) { + size_t previous_output_scanline_start = scanline_size * (scanline - 1); + previous_output_scanline = &image->data[previous_output_scanline_start]; + } + + apply_reconstruction_functions_to_scanline( + &image->data[output_scanline_start], + &decompressed_data_buffer[input_scanline_start + 1], + previous_output_scanline, filter_type, scanline_size, bytes_per_pixel); + } +} + Png *lis_Png_parse(FILE *stream) { Png *png = malloc(sizeof(Png)); DeflateDecompressor ctx; @@ -273,12 +427,12 @@ Png *lis_Png_parse(FILE *stream) { uint8_t parsed_png_signature[PNG_SIGNATURE_LENGTH]; if (!ParsingContext_parse_bytes(&ctx, PNG_SIGNATURE_LENGTH, parsed_png_signature)) { - LOG0("Couldn't parse signature"); + LPNG_LOG_ERR0("Couldn't parse signature"); goto err; } if (!matches_png_signature(parsed_png_signature)) { - LOG0("Invalid signature"); + LPNG_LOG_ERR0("Invalid signature"); goto err; } @@ -289,40 +443,64 @@ Png *lis_Png_parse(FILE *stream) { ImageHeader_print_image_header(&header); ImageData image_data = {0}; + size_t parsed_data_chunk_count = 0; bool end_reached = false; while (!end_reached) { uint32_t length; if (!ParsingContext_parse_uint32_t(&ctx, &length)) { - LOG0("Couldn't parse chunk length"); + LPNG_LOG_ERR0("Couldn't parse chunk length"); goto cleanup_data; } ParsingContext_crc_reset(&ctx); uint32_t type; if (!ParsingContext_parse_uint32_t(&ctx, &type)) { - LOG0("Couldn't parse chunk type"); + LPNG_LOG_ERR0("Couldn't parse chunk type"); goto cleanup_data; } +#ifdef LPNG_DEBUG_LOG uint32_t type_le = uint32_t_to_le(type); - LOGN("Parsing %.4s chunk", (char *)&type_le); + LPNG_LOG_DBG("Parsing %.4s chunk", (char *)&type_le); +#endif + switch (type) { case IDAT_CHUNK_TYPE: - parse_IDAT_chunk(&ctx, length, &image_data); + if (!parse_IDAT_chunk(&ctx, length, &image_data)) { + LPNG_LOG_ERR0("Couldn't parse IDAT chunk"); + goto cleanup_data; + } + parsed_data_chunk_count++; break; case IEND_CHUNK_TYPE: end_reached = true; ParsingContext_skip_bytes(&ctx, sizeof(uint32_t)); break; default: - LOG0("Unknown chunk type, skipping chunk..."); + LPNG_LOG_DBG0("Unknown chunk type, skipping chunk..."); ParsingContext_skip_bytes(&ctx, length + sizeof(uint32_t)); break; } } - LOGN("Data length: %zul", image_data.length); - zlib_decompress(image_data.data, image_data.length); + if (parsed_data_chunk_count == 0) { + LPNG_LOG_ERR0("No IDAT chunk found, at least one is required"); + goto cleanup_data; + } + + LPNG_LOG_DBG("Data length: %zu", image_data.length); + png->width = header.width; + png->height = header.height; + png->colour_type = header.colour_type; + png->bits_per_sample = header.bit_depth; + size_t output_length; + char *output_buffer = + zlib_decompress(image_data.data, image_data.length, &output_length); + png->data = output_buffer; + + apply_reconstruction_functions(png, output_buffer); + + free(image_data.data); return png; @@ -334,4 +512,60 @@ err: #undef PARSE_FIELD void lis_Png_destroy(Png *png) { free(png); } -void lis_Png_dump_ppm(const Png *png) { ASSERT(png != NULL); } +void lis_Png_dump_ppm(const Png *png) { + ASSERT(png != NULL); + printf("P3\n"); + printf("%zu %zu\n", png->width, png->height); + printf("%d\n", (1 << png->bits_per_sample) - 1); + size_t sample_count = ColourType_sample_count(png->colour_type); + size_t bits_per_pixel = png->bits_per_sample * sample_count; + size_t bytes_per_pixel = bits_per_pixel / 8; + if (png->bits_per_sample < 8) { + bytes_per_pixel = 1; + } + for (size_t pixel_index = 0; pixel_index < png->height * png->width; + pixel_index++) { + + if (png->colour_type == ColourType_Greyscale) { + if (bits_per_pixel == 16) { + uint8_t hi = png->data[pixel_index * bytes_per_pixel]; + uint8_t lo = png->data[pixel_index * bytes_per_pixel + 1]; + uint16_t grey = ((hi & 0xFFu) << 8) | (lo & 0xFFu); + printf("%u %u %u\n", grey, grey, grey); + } else { + size_t absolute_bit_offset = pixel_index * bits_per_pixel; + size_t byte_offset = absolute_bit_offset / 8; + size_t relative_bit_offset = absolute_bit_offset % 8; + unsigned char grey = + (png->data[byte_offset] >> + (7 - relative_bit_offset - (bits_per_pixel - 1))) & + ((1 << bits_per_pixel) - 1); + printf("%u %u %u\n", grey, grey, grey); + } + } else if (png->colour_type == ColourType_Truecolour) { + if (png->bits_per_sample == 16) { + uint8_t hi_r = png->data[pixel_index * bytes_per_pixel]; + uint8_t lo_r = png->data[pixel_index * bytes_per_pixel + 1]; + uint16_t r = ((hi_r & 0xFFu) << 8) | (lo_r & 0xFFu); + + uint8_t hi_g = png->data[pixel_index * bytes_per_pixel + 2]; + uint8_t lo_g = png->data[pixel_index * bytes_per_pixel + 3]; + uint16_t g = ((hi_g & 0xFFu) << 8) | (lo_g & 0xFFu); + + uint8_t hi_b = png->data[pixel_index * bytes_per_pixel + 4]; + uint8_t lo_b = png->data[pixel_index * bytes_per_pixel + 5]; + uint16_t b = ((hi_b & 0xFFu) << 8) | (lo_b & 0xFFu); + + printf("%u %u %u\n", r, g, b); + } else { + uint8_t r = png->data[pixel_index * bytes_per_pixel]; + uint8_t g = png->data[pixel_index * bytes_per_pixel + 1]; + uint8_t b = png->data[pixel_index * bytes_per_pixel + 2]; + printf("%u %u %u\n", r, g, b); + } + } else { + LPNG_LOG_ERR0("Unsupported colour type"); + exit(1); + } + } +} diff --git a/lisiblepng/src/assert.h b/lisiblepng/src/lisiblepng/assert.h index 7d41b57..1de9c36 100644 --- a/lisiblepng/src/assert.h +++ b/lisiblepng/src/lisiblepng/assert.h @@ -6,8 +6,8 @@ #define ASSERT(predicate) \ do { \ if (!(predicate)) { \ - LOGN("Assertion failed in %s:%d:\n\t%s", __FILE__, __LINE__, \ - #predicate); \ + LPNG_LOG_ERR("Assertion failed in %s:%d:\n\t%s", __FILE__, __LINE__, \ + #predicate); \ exit(1); \ } \ } while (0) diff --git a/lisiblepng/src/bitstream.c b/lisiblepng/src/lisiblepng/bitstream.c index e7ddb79..e7ddb79 100644 --- a/lisiblepng/src/bitstream.c +++ b/lisiblepng/src/lisiblepng/bitstream.c diff --git a/lisiblepng/src/bitstream.h b/lisiblepng/src/lisiblepng/bitstream.h index 72d8295..72d8295 100644 --- a/lisiblepng/src/bitstream.h +++ b/lisiblepng/src/lisiblepng/bitstream.h diff --git a/lisiblepng/src/deflate.c b/lisiblepng/src/lisiblepng/deflate.c index d894a35..50f90e3 100644 --- a/lisiblepng/src/deflate.c +++ b/lisiblepng/src/lisiblepng/deflate.c @@ -14,7 +14,7 @@ #define CODE_LENGTH_ALPHABET_MAX_SYMBOL_COUNT 19 #define MAX_HUFFMAN_CODE_LENGTH 15 -#define MAX_LENGTH_CODES 286 +#define MAX_LENGTH_CODES 288 #define MAX_DISTANCE_CODES 32 #define FIXED_LENGTH_LITERAL_CODE_COUNT 288 @@ -52,7 +52,7 @@ void OutputBuffer_init(OutputBuffer *output_buffer) { ASSERT(output_buffer != NULL); output_buffer->buffer = calloc(DEFLATE_OUTPUT_BUFFER_INITIAL_CAPACITY, 1); if (!output_buffer->buffer) { - LOG0("Couldn't allocate deflate output buffer (out of memory?)"); + LPNG_LOG_ERR0("Couldn't allocate deflate output buffer (out of memory?)"); abort(); } output_buffer->cap = DEFLATE_OUTPUT_BUFFER_INITIAL_CAPACITY; @@ -64,7 +64,7 @@ void OutputBuffer_expand(OutputBuffer *output_buffer) { size_t new_cap = output_buffer->cap * 2; output_buffer->buffer = realloc(output_buffer->buffer, new_cap); if (!output_buffer->buffer) { - LOG0("Couldn't reallocate deflate output buffer (out of memory?)"); + LPNG_LOG_ERR0("Couldn't reallocate deflate output buffer (out of memory?)"); abort(); } @@ -192,7 +192,7 @@ bool deflate_decompress_(Bitstream *bitstream, symbol = huffman_table_decode(length_literal_table->symbols, length_literal_table->counts, bitstream); if (symbol < 0) { - LOG0("Unknown symbol decoded"); + LPNG_LOG_ERR0("Unknown symbol decoded"); return false; } if (symbol < 256) { @@ -228,14 +228,15 @@ bool deflate_decompress(Bitstream *bitstream, OutputBuffer *output) { uint8_t b_final = 0; while (!b_final) { - LOG0("Parse deflate block"); + LPNG_LOG_DBG0("Parse deflate block"); b_final = Bitstream_next_bits(bitstream, BFINAL_LENGTH_BITS); const uint8_t b_type = Bitstream_next_bits(bitstream, BTYPE_LENGTH_BITS); if (b_type == DeflateBlockType_NoCompression) { - LOG0("Uncompressed deflate blocks aren't supported"); + LPNG_LOG_ERR0("Uncompressed deflate blocks aren't supported"); abort(); } else { if (b_type == DeflateBlockType_FixedHuffman) { + LPNG_LOG_ERR0("Static huffman table"); static bool are_tables_blank = true; static LengthLiteralTable length_literal_table = {0}; static DistanceTable distance_table = {0}; @@ -243,6 +244,7 @@ bool deflate_decompress(Bitstream *bitstream, OutputBuffer *output) { uint16_t lenlit_codelengths[FIXED_LENGTH_LITERAL_CODE_COUNT]; if (are_tables_blank) { + LPNG_LOG_ERR0("Computing static huffman table"); for (int symbol = 0; symbol < 144; symbol++) { lenlit_codelengths[symbol] = 8; } @@ -282,11 +284,11 @@ bool deflate_decompress(Bitstream *bitstream, OutputBuffer *output) { const uint16_t hlit = Bitstream_next_bits(bitstream, 5) + 257; const uint8_t hdist = Bitstream_next_bits(bitstream, 5) + 1; const uint8_t hclen = Bitstream_next_bits(bitstream, 4) + 4; - LOGN("Dynamic table:\n" - "hlit: %d\n" - "hdist: %d\n" - "hclen: %d", - hlit, hdist, hclen); + LPNG_LOG_DBG("Dynamic table:\n" + "hlit: %d\n" + "hdist: %d\n" + "hclen: %d", + hlit, hdist, hclen); CodeLengthTable codelength_table = {0}; build_codelength_table(&codelength_table, bitstream, hclen); @@ -297,7 +299,7 @@ bool deflate_decompress(Bitstream *bitstream, OutputBuffer *output) { int symbol = huffman_table_decode(codelength_table.symbols, codelength_table.counts, bitstream); if (symbol < 0) { - LOG0("Unknown symbol decoded using huffman table"); + LPNG_LOG_ERR0("Unknown symbol decoded using huffman table"); return false; } else if (symbol < 16) { lenlit_dist_codelengths[index++] = symbol; @@ -328,6 +330,7 @@ bool deflate_decompress(Bitstream *bitstream, OutputBuffer *output) { build_huffman_table_from_codelengths( distance_table.symbols, distance_table.counts, lenlit_dist_codelengths + hlit, hdist); + deflate_decompress_(bitstream, &length_literal_table, &distance_table, output); } @@ -337,7 +340,8 @@ bool deflate_decompress(Bitstream *bitstream, OutputBuffer *output) { } char *zlib_decompress(const uint8_t *compressed_data_buffer, - const size_t compressed_data_length) { + const size_t compressed_data_length, + size_t *output_length) { ASSERT(compressed_data_buffer != NULL); Bitstream bitstream; @@ -349,46 +353,67 @@ char *zlib_decompress(const uint8_t *compressed_data_buffer, uint16_t fdict = Bitstream_next_bits(&bitstream, FDICT_LENGTH_BITS); uint16_t flevel = Bitstream_next_bits(&bitstream, FLEVEL_LENGTH_BITS); - LOGN("zlib informations:\n" - "- CM (compression method): %d\n" - "- CINFO (compression info): %d\n" - "- FCHECK (check bits for CMF and FLG): %d\n" - "- FDICT (preset dictionary): %d\n" - "- FLEVEL (compression level): %d", - cm, cinfo, fcheck, fdict, flevel); +#ifdef LPNG_DEBUG_LOG + LPNG_LOG_DBG("zlib informations:\n" + "- CM (compression method): %d\n" + "- CINFO (compression info): %d\n" + "- FCHECK (check bits for CMF and FLG): %d\n" + "- FDICT (preset dictionary): %d\n" + "- FLEVEL (compression level): %d", + cm, cinfo, fcheck, fdict, flevel); +#else + (void)cm; + (void)cinfo; + (void)fcheck; +#endif // LPNG_DEBUG_LOG + uint16_t cmf = bitstream.data[0]; uint16_t flg = bitstream.data[1]; if ((cmf * 256 + flg) % 31 != 0) { - LOG0("fcheck validation failed"); + LPNG_LOG_ERR0("fcheck validation failed"); return false; } + if (flevel > 9) { + LPNG_LOG_ERR0("Invalid compression level"); + return NULL; + } + if (fdict != 0) { - LOG0("preset dictionnaries are unsupported"); + LPNG_LOG_ERR0("preset dictionnaries are unsupported"); return NULL; } OutputBuffer output; OutputBuffer_init(&output); if (!deflate_decompress(&bitstream, &output)) { - LOG0("deflate decompression failed"); + LPNG_LOG_ERR0("deflate decompression failed"); return NULL; } - int adler32 = bitstream.data[bitstream.current_byte_index + 4] + - (bitstream.data[bitstream.current_byte_index + 3] << 8) + - (bitstream.data[bitstream.current_byte_index + 2] << 16) + - (bitstream.data[bitstream.current_byte_index + 1] << 24); - LOGN("Adler32 checksum: %d", adler32); + size_t adler32_offset = bitstream.current_byte_index; + if (bitstream.current_bit_offset % 8 != 0) { + adler32_offset++; + } + uint32_t adler32 = bitstream.data[adler32_offset + 3] + + (bitstream.data[adler32_offset + 2] << 8) + + (bitstream.data[adler32_offset + 1] << 16) + + (bitstream.data[adler32_offset] << 24); + LPNG_LOG_DBG("Adler32 checksum: %u", adler32); uint32_t a = 1; uint32_t b = 0; for (size_t i = 0; i < output.len; i++) { - a = (a + (unsigned char)output.buffer[i]) % 65521; + a = (a + (uint8_t)output.buffer[i]) % 65521; b = (b + a) % 65521; } - int computed_adler32 = (b << 16) | a; - LOGN("Computed Adler32 checksum: %d", computed_adler32); + uint32_t computed_adler32 = (b << 16) | a; + LPNG_LOG_DBG("Computed Adler32 checksum: %u", computed_adler32); + if (adler32 != computed_adler32) { + LPNG_LOG_ERR0("Invalid checksum"); + exit(1); + } + *output_length = output.len; return output.buffer; } diff --git a/lisiblepng/src/deflate.h b/lisiblepng/src/lisiblepng/deflate.h index 77100c6..88a3711 100644 --- a/lisiblepng/src/deflate.h +++ b/lisiblepng/src/lisiblepng/deflate.h @@ -7,9 +7,13 @@ /// Decompresses zlib compressed data as defined by RFC 1950 /// +/// @param compressed_data_buffer The compressed data +/// @param compressed_data_length The length in bytes of the compressed data +/// @param output_length The pointer to write the output length to /// @return A pointer to the decompressed data, the caller is responsible to /// deallocate it using free(). In case of error, NULL is returned. char *zlib_decompress(const uint8_t *compressed_data_buffer, - const size_t compressed_data_length); + const size_t compressed_data_length, + size_t *output_length); #endif // LISIBLE_PNG_DEFLATE_H diff --git a/lisiblepng/src/lisiblepng/log.h b/lisiblepng/src/lisiblepng/log.h new file mode 100644 index 0000000..720acd1 --- /dev/null +++ b/lisiblepng/src/lisiblepng/log.h @@ -0,0 +1,23 @@ +#ifndef LISIBLE_PNG_LOG_H +#define LISIBLE_PNG_LOG_H + +#include <stdio.h> + +#define LPNG_LOG_PREFIX "LPNG: " +#define LPNG_DEBUG_LOG_PREFIX "%s:%d " LPNG_LOG_PREFIX +#define LPNG_LOG_ERR0(fmt) fprintf(stderr, LPNG_LOG_PREFIX fmt "\n") +#define LPNG_LOG_ERR(fmt, ...) \ + fprintf(stderr, LPNG_LOG_PREFIX fmt "\n", __VA_ARGS__) + +#ifdef LPNG_DEBUG_LOG +#define LPNG_LOG_DBG0(fmt) \ + fprintf(stderr, LPNG_DEBUG_LOG_PREFIX fmt "\n", __FILE__, __LINE__) +#define LPNG_LOG_DBG(fmt, ...) \ + fprintf(stderr, LPNG_DEBUG_LOG_PREFIX fmt "\n", __FILE__, __LINE__, \ + __VA_ARGS__) +#else +#define LPNG_LOG_DBG0(fmt) +#define LPNG_LOG_DBG(fmt, ...) +#endif // LPNG_DEBUG_LOG + +#endif // LISIBLE_PNG_LOG_H diff --git a/lisiblepng/src/log.h b/lisiblepng/src/log.h deleted file mode 100644 index fba6820..0000000 --- a/lisiblepng/src/log.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef LISIBLE_PNG_LOG_H -#define LISIBLE_PNG_LOG_H - -#include <stdio.h> - -#define LOG0(fmt) fprintf(stderr, fmt "\n") -#define LOGN(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__) - -#endif // LISIBLE_PNG_LOG_H |
