diff --git a/ANNOUNCE b/ANNOUNCE index de4f88b08..c7f51fbb7 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,5 +1,5 @@ -Libpng 1.6.3beta05 - May 7, 2013 +Libpng 1.6.3beta05 - May 8, 2013 This is not intended to be a public release. It will be replaced within a few weeks by a public version or by another test version. @@ -45,7 +45,7 @@ Version 1.6.3beta04 [May 6, 2013] Calculate our own zlib windowBits when decoding rather than trusting the CMF bytes in the PNG datastream. -Version 1.6.3beta05 [May 7, 2013] +Version 1.6.3beta05 [May 8, 2013] Choose to use windowBits==15 or the zlib header setting via the benign-errors setting. If benign errors are allowed, then ignore the windowBits setting in the zlib header. @@ -53,6 +53,11 @@ Version 1.6.3beta05 [May 7, 2013] undid the improvement in beta04. Check for EOF in contrib/pngminus/pnm2png.c (Paul Stewart). Ignore "#" delimited comments in input file to pnm2png.c. + Added an option to force maximum window size for inflating. For inflate, + reverted previous fixes. + Added fixitxt and pngdeflate to the built programs and removed warnings + from the source code and timepng that are revealed as a result. Fixed + fixitxt when the chunk length is more than 65535 (untested, no test case). Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 72c987411..ff162a319 100644 --- a/CHANGES +++ b/CHANGES @@ -4528,7 +4528,7 @@ Version 1.6.3beta04 [May 6, 2013] Calculate our own zlib windowBits when decoding rather than trusting the CMF bytes in the PNG datastream. -Version 1.6.3beta05 [May 7, 2013] +Version 1.6.3beta05 [May 8, 2013] Choose to use windowBits==15 or the zlib header setting via the benign-errors setting. If benign errors are allowed, then ignore the windowBits setting in the zlib header. @@ -4536,6 +4536,11 @@ Version 1.6.3beta05 [May 7, 2013] undid the improvement in beta04. Check for EOF in contrib/pngminus/pnm2png.c (Paul Stewart). Ignore "#" delimited comments in input file to pnm2png.c. + Added an option to force maximum window size for inflating. For inflate, + reverted previous fixes. + Added fixitxt and pngdeflate to the built programs and removed warnings + from the source code and timepng that are revealed as a result. Fixed + fixitxt when the chunk length is more than 65535 (untested, no test case). Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/Makefile.am b/Makefile.am index a2cd2c10b..351d37f60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,9 @@ ACLOCAL_AMFLAGS = -I scripts # test programs - run on make check, make distcheck check_PROGRAMS= pngtest pngunknown pngstest pngvalid +# Utilities - installed +bin_PROGRAMS= pngdeflate fixitxt + pngtest_SOURCES = pngtest.c pngtest_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la @@ -21,6 +24,11 @@ pngstest_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la pngunknown_SOURCES = contrib/libtests/pngunknown.c pngunknown_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la +pngdeflate_SOURCES = contrib/tools/pngdeflate.c +pngdeflate_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la + +fixitxt_SOURCES = contrib/tools/fixitxt.c + # Generally these are single line shell scripts to run a test with a particular # set of parameters: TESTS =\ @@ -187,7 +195,8 @@ $(srcdir)/scripts/pnglibconf.h.prebuilt: # an installed one (this can happen immediately after on a clean system if # 'make test' is the first thing the user does.) contrib/libtests/pngstest.o contrib/libtests/pngvalid.o pngtest.o: pnglibconf.h -contrib/libtests/pngunknown.o: pnglibconf.h +contrib/libtests/pngunknown.o contrib/libtests/timepng.o: pnglibconf.h +contrib/libtests/pngdeflate.o contrib/libtests/fixitxt.o: pnglibconf.h # We must use -DPNG_NO_USE_READ_MACROS here even when the library may actually # be built with PNG_USE_READ_MACROS; this prevents the read macros from diff --git a/contrib/tools/fixitxt.c b/contrib/tools/fixitxt.c index d717b3e6a..c119d53ea 100644 --- a/contrib/tools/fixitxt.c +++ b/contrib/tools/fixitxt.c @@ -24,18 +24,20 @@ * * gcc -O -o fixitxt fixitxt.c -lz */ +#include #define MAX_LENGTH 500000 -#define GETBREAK c=getchar(); if (c == EOF) break; -#include +#define GETBREAK ((unsigned char)(inchar=getchar())); if (inchar == EOF) break -main() +int +main(void) { unsigned int i; unsigned char buf[MAX_LENGTH]; unsigned long crc; - unsigned int c; + unsigned char c; + int inchar; /* Skip 8-byte signature */ for (i=8; i; i--) @@ -44,17 +46,16 @@ main() putchar(c); } -if (c != EOF) +if (inchar != EOF) for (;;) { /* Read the length */ - unsigned long length; - c=GETBREAK; buf[0] = c; - c=GETBREAK; buf[1] = c; - c=GETBREAK; buf[2] = c; - c=GETBREAK; buf[3] = c; + unsigned long length; /* must be 32 bits! */ + c=GETBREAK; buf[0] = c; length = c; length <<= 8; + c=GETBREAK; buf[1] = c; length += c; length <<= 8; + c=GETBREAK; buf[2] = c; length += c; length <<= 8; + c=GETBREAK; buf[3] = c; length += c; - length=((((unsigned long) buf[0]<<8 + buf[1]<<16) + buf[2] << 8) + buf[3]); /* Read the chunkname */ c=GETBREAK; buf[4] = c; c=GETBREAK; buf[5] = c; @@ -125,7 +126,7 @@ for (;;) putchar(c); } - if (c == EOF) + if (inchar == EOF) { break; } @@ -135,10 +136,12 @@ for (;;) break; } - if (c == EOF) + if (inchar == EOF) break; if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68) break; } + + return 0; } diff --git a/contrib/tools/pngdeflate.c b/contrib/tools/pngdeflate.c new file mode 100644 index 000000000..087cab79e --- /dev/null +++ b/contrib/tools/pngdeflate.c @@ -0,0 +1,1147 @@ +/* pngdeflate.c + * + * Copyright (c) 2013 John Cunningham Bowler + * + * Last changed in libpng 1.6.3 [(PENDING RELEASE)] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Tool to check and fix the deflate 'too far back' problem, see the usage + * message for more information. + */ +#include +#include +#ifdef PNG_READ_SUPPORTED +#include +#include + +#include +#include + +static int idat_error = 0; +static int verbose = 0; +static int errors = 0; +static int warnings = 0; +#ifdef PNG_MAXIMUM_INFLATE_WINDOW + static int set_option = 0; +#endif +static const char *name = "stdin"; +static uLong crc_IDAT_head; /* CRC32 of "IDAT" */ +static uLong crc_IEND; +static z_stream z_idat; + +/* Control structure for the temporary file */ +typedef struct +{ + size_t image_size; + off_t file_size; + fpos_t header_pos; + fpos_t crc_pos; + uLong crc_tail; /* CRC of bytes after header */ + png_uint_32 len_tail; /* Count thereof */ + png_byte header[2]; + + /* Image info */ + png_uint_32 width; + png_uint_32 height; + png_byte bit_depth; + png_byte color_type; + png_byte compression_method; + png_byte filter_method; + png_byte interlace_method; +} IDAT_info; + +static png_uint_32 +mult(png_uint_32 n, png_uint_32 m) +{ + if ((n + (m-1)) / m > 0xffffffff/m) + { + fprintf(stderr, "%s: overflow (%lu, %u)\n", name, (unsigned long)n, m); + exit(2); + } + + return n * m; +} + +static size_t +image_size(const IDAT_info *info) +{ + unsigned int pd = info->bit_depth; + size_t cb; + + switch (info->color_type) + { + case 0: case 3: + break; + + case 2: /* rgb */ + pd *= 3; + break; + + case 4: /* ga */ + pd *= 2; + break; + + case 6: /* rgba */ + pd *= 4; + break; + + default: + fprintf(stderr, "%s: invalid color type (%d)\n", name, + info->color_type); + exit(2); + } + + switch (info->interlace_method) + { + case PNG_INTERLACE_ADAM7: + /* Interlacing makes the image larger because of the replication of + * both the filter byte and the padding to a byte boundary. + */ + { + int pass; + + for (cb=0, pass=0; pass<=6; ++pass) + { + png_uint_32 pw = PNG_PASS_COLS(info->width, pass); + + if (pw > 0) + cb += mult(((mult(pd, pw)+7) >> 3)+1, + PNG_PASS_ROWS(info->height, pass)); + } + } + break; + + case PNG_INTERLACE_NONE: + cb = mult(info->height, 1+((mult(info->width, pd) + 7) >> 3)); + break; + + default: + fprintf(stderr, "%s: invalid interlace type %d\n", name, + info->interlace_method); + exit(2); + } + + return cb; +} + +static int +image_windowBits(const IDAT_info *info) +{ + size_t cb = image_size(info); + + if (cb > 16384) return 15; + if (cb > 8192) return 14; + if (cb > 4096) return 13; + if (cb > 2048) return 12; + if (cb > 1024) return 11; + if (cb > 512) return 10; + if (cb > 256) return 9; + return 8; +} + +static void +error_handler(png_structp png_ptr, png_const_charp message) +{ + if (strcmp(message, "IDAT: invalid distance too far back") == 0) + idat_error = 1; + + else if (errors || verbose) + fprintf(stderr, "%s: %s\n", name, message); + + png_longjmp(png_ptr, 1); +} + +static void +warning_handler(png_structp png_ptr, png_const_charp message) +{ + if (warnings || verbose) + fprintf(stderr, "%s: %s\n", name, message); + + (void)png_ptr; +} + +static int +read_png(FILE *fp) +{ + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0, + error_handler, warning_handler); + png_infop info_ptr = NULL; + png_bytep row = NULL, display = NULL; + + if (png_ptr == NULL) + return 0; + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (row != NULL) free(row); + if (display != NULL) free(display); + return 0; + } + +# ifdef PNG_MAXIMUM_INFLATE_WINDOW + png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, set_option != 0); +# endif + + png_init_io(png_ptr, fp); + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + png_error(png_ptr, "OOM allocating info structure"); + + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); + + png_read_info(png_ptr, info_ptr); + + /* Limit the decompression buffer size to 1 - this ensures that overlong + * length codes are always detected. + */ + png_set_compression_buffer_size(png_ptr, 1); + + { + png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + row = malloc(rowbytes); + display = malloc(rowbytes); + + if (row == NULL || display == NULL) + png_error(png_ptr, "OOM allocating row buffers"); + + { + png_uint_32 height = png_get_image_height(png_ptr, info_ptr); + int passes = png_set_interlace_handling(png_ptr); + int pass; + + png_start_read_image(png_ptr); + + for (pass = 0; pass < passes; ++pass) + { + png_uint_32 y = height; + + /* NOTE: this trashes the row each time; interlace handling won't + * work, but this avoids memory thrashing for speed testing. + */ + while (y-- > 0) + png_read_row(png_ptr, row, display); + } + } + } + + /* Make sure to read to the end of the file: */ + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + free(row); + free(display); + return 1; +} + +/* Chunk tags (copied from pngpriv.h) */ +#define PNG_32b(b,s) ((png_uint_32)(b) << (s)) +#define PNG_CHUNK(b1,b2,b3,b4) \ + (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0)) + +#define png_IHDR PNG_CHUNK( 73, 72, 68, 82) +#define png_IDAT PNG_CHUNK( 73, 68, 65, 84) +#define png_IEND PNG_CHUNK( 73, 69, 78, 68) +#define png_PLTE PNG_CHUNK( 80, 76, 84, 69) +#define png_bKGD PNG_CHUNK( 98, 75, 71, 68) +#define png_cHRM PNG_CHUNK( 99, 72, 82, 77) +#define png_gAMA PNG_CHUNK(103, 65, 77, 65) +#define png_hIST PNG_CHUNK(104, 73, 83, 84) +#define png_iCCP PNG_CHUNK(105, 67, 67, 80) +#define png_iTXt PNG_CHUNK(105, 84, 88, 116) +#define png_oFFs PNG_CHUNK(111, 70, 70, 115) +#define png_pCAL PNG_CHUNK(112, 67, 65, 76) +#define png_sCAL PNG_CHUNK(115, 67, 65, 76) +#define png_pHYs PNG_CHUNK(112, 72, 89, 115) +#define png_sBIT PNG_CHUNK(115, 66, 73, 84) +#define png_sPLT PNG_CHUNK(115, 80, 76, 84) +#define png_sRGB PNG_CHUNK(115, 82, 71, 66) +#define png_sTER PNG_CHUNK(115, 84, 69, 82) +#define png_tEXt PNG_CHUNK(116, 69, 88, 116) +#define png_tIME PNG_CHUNK(116, 73, 77, 69) +#define png_tRNS PNG_CHUNK(116, 82, 78, 83) +#define png_zTXt PNG_CHUNK(122, 84, 88, 116) + +static void +rx(FILE *fp, png_bytep buf, off_t cb) +{ + if (fread(buf,cb,1,fp) != 1) { + fprintf(stderr, "%s: failed to read %lu bytes\n", name, + (unsigned long)cb); + exit(2); + } +} + +static png_uint_32 +r32(FILE *fp) +{ + png_byte buf[4]; + rx(fp, buf, 4); + return ((((((png_uint_32)buf[0] << 8)+buf[1]) << 8)+buf[2]) << 8) + buf[3]; +} + +static void +wx(FILE *fp, png_const_bytep buf, off_t cb) +{ + if (fwrite(buf,cb,1,fp) != 1) { + fprintf(stderr, "%s: failed to write %lu bytes\n", name, + (unsigned long)cb); + exit(3); + } +} + +static void +w32(FILE *fp, png_uint_32 val) +{ + png_byte buf[4]; + buf[0] = (png_byte)(val >> 24); + buf[1] = (png_byte)(val >> 16); + buf[2] = (png_byte)(val >> 8); + buf[3] = (png_byte)(val); + wx(fp, buf, 4); +} + +static void +wcrc(FILE *fp, uLong crc) +{ + /* Safe cast because a CRC is 32 bits */ + w32(fp, (png_uint_32)crc); +} + +static void +copy(FILE *fp, FILE *fpIn, off_t cb) +{ + png_byte buffer[1024]; + + while (cb >= 1024) + { + rx(fpIn, buffer, 1024); + wx(fp, buffer, 1024); + cb -= 1024; + } + + if (cb > 0) + { + rx(fpIn, buffer, cb); + wx(fp, buffer, cb); + } +} + +static void +skip_bytes(FILE *fpIn, png_uint_32 cb) +{ + png_byte buffer[1024]; + + while (cb >= 1024) + { + rx(fpIn, buffer, 1024); + cb -= 1024; + } + + if (cb > 0) + rx(fpIn, buffer, cb); +} + +static void +safe_getpos(FILE *fp, fpos_t *pos) +{ + if (fgetpos(fp, pos)) + { + perror("tmpfile"); + fprintf(stderr, "%s: tmpfile fgetpos failed\n", name); + exit(3); + } +} + +static void +safe_setpos(FILE *fp, fpos_t *pos) +{ + if (fflush(fp)) + { + perror("tmpfile"); + fprintf(stderr, "%s: tmpfile fflush failed\n", name); + exit(3); + } + + if (fsetpos(fp, pos)) + { + perror("tmpfile"); + fprintf(stderr, "%s: tmpfile fsetpos failed\n", name); + exit(3); + } +} + +static void +idat_update(FILE *fp, IDAT_info *info) +{ + uLong crc; + + safe_setpos(fp, &info->header_pos); + wx(fp, info->header, 2); + + crc = crc32(crc_IDAT_head, info->header, 2); + crc = crc32_combine(crc, info->crc_tail, info->len_tail); + + safe_setpos(fp, &info->crc_pos); + wcrc(fp, crc); +} + +static void +set_bits(const char *file, FILE *fp, IDAT_info *info, int bits) +{ + int byte1 = (info->header[0] & 0xf) + ((bits-8) << 4); + int byte2 = info->header[1] & 0xe0; + + /* The checksum calculation: */ + byte2 += 0x1f - ((byte1 << 8) + byte2) % 0x1f; + + info->header[0] = (png_byte)byte1; + info->header[1] = (png_byte)byte2; + + if (verbose) + fprintf(stderr, "%s: trying windowBits %d (Z_CMF = 0x%x)\n", file, bits, + byte1); + + idat_update(fp, info); +} + +static void +ptagchar(png_uint_32 ch) +{ + ch &= 0xff; + if (isprint(ch)) + putc(ch, stderr); + + else + fprintf(stderr, "[%02x]", ch); +} + +static void +ptag(png_uint_32 tag) +{ + if (tag != 0) + { + ptag(tag >> 8); + ptagchar(tag); + } +} + +static int +fix_one(FILE *fp, FILE *fpIn, IDAT_info *info, png_uint_32 max_IDAT, int strip) +{ + int state = 0; + /* 0: at beginning, before first IDAT + * 1: read first CMF header byte + * 2: read second byte, in first IDAT + * 3: after first IDAT + * +4: saw deflate stream end. + */ + int truncated_idat = 0; /* Count of spurious IDAT bytes */ + uLong crc_idat = 0; /* Running CRC of current IDAT */ + png_uint_32 len_IDAT = 0; /* Length of current IDAT */ + fpos_t pos_IDAT_length; /* fpos_t of length field in current IDAT */ + + /* The signature: */ + { + png_byte buf[8]; + rx(fpIn, buf, 8); + wx(fp, buf, 8); + } + + info->file_size = 45; /* signature + IHDR + IEND */ + + for (;;) /* Chunk for loop */ + { + png_uint_32 len = r32(fpIn); + png_uint_32 tag = r32(fpIn); + + if (tag == png_IHDR) + { + /* Need width, height, color type, bit depth and interlace for the + * file. + */ + info->width = r32(fpIn); + info->height = r32(fpIn); + rx(fpIn, &info->bit_depth, 1); + rx(fpIn, &info->color_type, 1); + rx(fpIn, &info->compression_method, 1); + rx(fpIn, &info->filter_method, 1); + rx(fpIn, &info->interlace_method, 1); + + /* And write the information. */ + w32(fp, len); + w32(fp, tag); + w32(fp, info->width); + w32(fp, info->height); + wx(fp, &info->bit_depth, 1); + wx(fp, &info->color_type, 1); + wx(fp, &info->compression_method, 1); + wx(fp, &info->filter_method, 1); + wx(fp, &info->interlace_method, 1); + + /* Copy the CRC */ + copy(fp, fpIn, 4); + } + + else if (tag == png_IEND) + { + /* Ok, write an IEND chunk and finish. */ + w32(fp, 0); + w32(fp, png_IEND); + wcrc(fp, crc_IEND); + break; + } + + else if (tag == png_IDAT && len > 0) + { + /* Write the chunk header now if it hasn't been written yet */ + if (len_IDAT == 0) + { + /* The length is set at the end: */ + safe_getpos(fp, &pos_IDAT_length); + w32(fp, max_IDAT); /* length, not yet written */ + w32(fp, png_IDAT); + + if (state == 0) /* Start of first IDAT */ + { + safe_getpos(fp, &info->header_pos); + /* This will become info->crc_tail: */ + crc_idat = crc32(0L, Z_NULL, 0); + } + + else + crc_idat = crc_IDAT_head; + } + + /* Do the zlib 2-byte header, it gets written out but not added + * to the CRC (yet): + */ + while (len > 0 && state < 2) + { + rx(fpIn, info->header + state, 1); + wx(fp, info->header + state, 1); + ++state; + ++len_IDAT; + --len; + + if (state == 2) + { + /* The zlib stream is used to validate the compressed IDAT + * data in the most relaxed way possible. + */ + png_byte bdummy; + int ret; + + z_idat.next_in = info->header; + z_idat.avail_in = 2; + z_idat.next_out = &bdummy; /* Else Z_STREAM_ERROR! */ + z_idat.avail_out = 0; + + ret = inflate(&z_idat, Z_NO_FLUSH); + if (ret != Z_OK || z_idat.avail_in != 0) + { + fprintf(stderr, + "%s: unexpected/invalid inflate result %d \"%s\"\n", + name, ret, z_idat.msg); + return 1; + } + } + } /* while in zlib header */ + + /* Process further bytes in the IDAT chunk */ + while (len > 0 && state < 4) + { + png_byte b; + + rx(fpIn, &b, 1); + --len; + + /* Do this 1 byte at a time to maximize the chance of + * detecting errors (in particular zlib can skip the + * 'too-far-back' error if the output buffer is bigger than + * the window size.) + */ + z_idat.next_in = &b; + z_idat.avail_in = 1; + + do + { + int ret; + png_byte bout; + + z_idat.next_out = &bout; + z_idat.avail_out = 1; + + ret = inflate(&z_idat, Z_SYNC_FLUSH); + + if (z_idat.avail_out == 0) + ++info->image_size; + + switch (ret) + { + case Z_OK: + /* Just keep going */ + break; + + case Z_BUF_ERROR: + if (z_idat.avail_in > 0) + { + fprintf(stderr, + "%s: unexpected buffer error \"%s\"\n", + name, z_idat.msg); + return 1; + } + goto end_loop; + + case Z_STREAM_END: + /* End of stream */ + state |= 4; + goto end_loop; + + default: + fprintf(stderr, "%s: bad zlib stream %d, \"%s\"\n", + name, ret, z_idat.msg); + return 1; + } + } while (z_idat.avail_in > 0 || z_idat.avail_out == 0); + + /* The byte need not be consumed, if, for example, there is a + * spurious byte after the end of the zlib data. + */ + end_loop: + if (z_idat.avail_in == 0) + { + /* Write it and update the length information and running + * CRC. + */ + wx(fp, &b, 1); + crc_idat = crc32(crc_idat, &b, 1); + ++len_IDAT; + } + + else + ++truncated_idat; + + if (len_IDAT >= max_IDAT || state >= 4) + { + /* Either the IDAT chunk is full or we've seen the end of + * the deflate stream, or both. Flush the chunk and handle + * the details of the first chunk. + */ + fpos_t save; + + if ((state & 3) < 3) /* First IDAT */ + { + safe_getpos(fp, &info->crc_pos); + info->crc_tail = crc_idat; + info->len_tail = len_IDAT-2; + } + + /* This is not the correct value for the first IDAT! */ + wcrc(fp, crc_idat); + state |= 3; + + /* Update the length if it is not max_IDAT: */ + if (len_IDAT != max_IDAT) + { + safe_getpos(fp, &save); + safe_setpos(fp, &pos_IDAT_length); + w32(fp, len_IDAT); + safe_setpos(fp, &save); + } + + /* Add this IDAT to the file size: */ + info->file_size += 12 + len_IDAT; + } + } /* while len > 0 && state < 4 */ + + /* The above loop only exits on 0 bytes left or end of stream, if + * the stream ended with bytes left discard them: + */ + if (len > 0) + { + truncated_idat += len; + /* Skip those bytes and the CRC */ + skip_bytes(fpIn, len+4); + } + + else + skip_bytes(fpIn, 4); /* The CRC */ + } /* IDAT and len > 0 */ + + else + { + int skip = 0; + + if (tag == png_IDAT) + skip = 1; + + else if (state == 0) + { + /* Chunk before IDAT */ + if (!skip) switch (strip) + { + case 0: /* Don't strip */ + break; + + case 1: /* Keep gAMA, sRGB */ + if (tag == png_gAMA || tag == png_sRGB) + break; + /* Fall trhough */ + + default: /* Keep only IHDR, PLTE */ + if (tag == png_IHDR || tag == png_PLTE) + break; + + skip = 1; + break; + } + } + + else if (state >= 4) + { + /* Keep nothing after IDAT if stripping: */ + skip = strip; + } + + else + { + /* This is either an unterminated deflate stream or a spurious + * non-IDAT chunk in the list of IDAT chunks. Both are fatal + * errors. + */ + fprintf(stderr, "%s: tag '", name); + ptag(tag); + fprintf(stderr, "' after unterminated IDAT\n"); + break; + } + + /* Skip or send? */ + if (skip) + { + if (tag != png_IDAT && (tag & 0x20000000) == 0) + { + fprintf(stderr, "%s: unknown critical chunk '", name); + ptag(tag); + fprintf(stderr, "'\n"); + return 1; + } + + /* Skip this tag */ + if (fseek(fpIn, len+4, SEEK_CUR)) + { + perror(name); + fprintf(stderr, "%s: seek failed\n", name); + return 1; + } + } + + else /* Keep this tag */ + { + w32(fp, len); + w32(fp, tag); + copy(fp, fpIn, len+4); + info->file_size += 12+len; + } + } /* Not IDAT or len == 0 */ + } /* Chunk for loop */ + + /* Break out of the loop on error or end */ + if (state >= 4) + { + if (truncated_idat) + fprintf(stderr, "%s: removed %d bytes from end of IDAT\n", name, + truncated_idat); + + return 0; /* success */ + } + + /* This is somewhat generic but it works: */ + fprintf(stderr, "%s: unterminated/truncated PNG (%d)\n", name, state); + + return 1; +} + +static FILE *fpIn; + +static int +fix_file(FILE *fp, const char *file, png_uint_32 max_IDAT, int inplace, + int strip, int optimize, const char *output) +{ + IDAT_info info; + int imageBits, oldBits, bestBits, lowBits, newBits, ok_read; + + memset(&info, 0, sizeof info); + + name = file; + idat_error = 0; + + /* fpIn is closed by the caller if necessary */ + fpIn = fopen(file, "rb"); + if (fpIn == NULL) + { + perror(file); + fprintf(stderr, "%s: open failed\n", file); + return 1; + } + + /* With no arguments just check this file */ + if (optimize == 0 && strip == 0 && output == NULL) + return !read_png(fpIn); + + /* Otherwise, maybe, fix it */ + if (fix_one(fp, fpIn, &info, max_IDAT, strip)) + return 1; + + /* oldBits may be invalid, imageBits is always OK, newBits always records the + * actual window bits of the temporary file (fp). + */ + bestBits = imageBits = image_windowBits(&info); + newBits = oldBits = 8+(info.header[0] >> 4); + ok_read = 0; /* records a successful read */ + + /* Find the optimal (lowest) newBits */ + if (optimize) + for (lowBits=8; lowBits < bestBits;) + { + /* This will always set 'newBits' to a value lower than 'bestBits' because + * 'lowBits' is less than 'bestBits': + */ + newBits = (bestBits + lowBits) >> 1; + + set_bits(file, fp, &info, newBits); + + rewind(fp); + idat_error = 0; + + if (!read_png(fp)) + { + /* If idat_error is *not* set this is some other problem */ + if (!idat_error) + return 1; + + /* This is the hypothetical case where the IDAT has too much data *and* + * the window size is wrong. In fact this should never happen because + * of the way libpng handles a deflate stream that produces extra data. + */ + if (newBits >= imageBits) + { + fprintf(stderr, "%s: imageBits(%d) too low (%d)\n", file, imageBits, + newBits); + return 1; + } + + if (lowBits <= newBits) + lowBits = newBits+1; + } + + else + { + bestBits = newBits; + ok_read = 1; + } + } + + else if (bestBits > oldBits) + { + /* See if the original value is ok */ + rewind(fp); + idat_error = 0; + + if (read_png(fp)) + { + ok_read = 1; + bestBits = oldBits; + } + + else if (!idat_error) + return 1; + + /* Otherwise there is an IDAT error and no optimization is being done, so + * just use imageBits (which is already set in bestBits). + */ + } + + if (newBits != bestBits) + { + /* Update the header to the required value */ + newBits = bestBits; + + set_bits(file, fp, &info, newBits); + } + + if (!ok_read) + { + /* bestBits has not been checked */ + idat_error = 0; + rewind(fp); + ok_read = read_png(fp); + + if (idat_error) + { + /* This should never happen */ + fprintf(stderr, "%s: imageBits(%d) too low [%d]\n", file, imageBits, + newBits); + return 1; + } + + /* This means that the PNG has some other error */ + if (!ok_read) + return 1; + } + + /* Have a valid newBits */ + if (optimize) + printf("%2d %2d %2d %s %s %d %s\n", newBits, oldBits, imageBits, + newBits < imageBits ? "<" : "=", + newBits < oldBits ? "reduce " : + (newBits > oldBits ? "INCREASE" : "ok "), + newBits - oldBits, name); + +# ifdef PNG_MAXIMUM_INFLATE_WINDOW + /* Because setting libpng to use the maximum window bits breaks the + * read_png test above. + */ + if (set_option) + return 0; +# endif + + if (output != NULL || (inplace && (bestBits != oldBits || strip))) + { + FILE *fpOut; + + if (output != NULL) + fpOut = fopen(output, "wb"); + + else + { + fpOut = freopen(file, "wb", fpIn); + fpIn = NULL; + } + + if (fpOut == NULL) + { + perror(output); + fprintf(stderr, "%s: %s: open failed\n", file, output); + exit(3); + } + + rewind(fp); + copy(fpOut, fp, info.file_size); + + if (fflush(fpOut) || ferror(fpOut) || fclose(fpOut)) + { + perror(output != NULL ? output : file); + fprintf(stderr, "%s: %s: close failed\n", file, output); + if (output != NULL) + remove(output); + exit(3); + } + } + + return 0; +} + +static void +usage(const char *prog, int rc) +{ + fprintf(stderr, + "Usage: %s {[options] png-file}\n", prog); + fprintf(stderr, + " Tests, optimizes and fixes the zlib header in PNG files.\n" + " Optionally, when fixing, strips ancilliary chunks from the file.\n"); + fprintf(stderr, + "\nOptions:\n" +# ifdef PNG_MAXIMUM_INFLATE_WINDOW + " --test: Test the PNG_MAXIMUM_INFLATE_WINDOW option.\n" +# endif + " --optimize (-o): Find the smallest deflate window size for the file.\n" + " Also outputs a summary for each file.\n" + " --strip (-s): Remove chunks except for IHDR, PLTE, IEND, gAMA, sRGB.\n" + " If given twice remove gAMA and sRGB as well.\n" + " --errors (-e): Output errors from libpng (except too-far-back).\n"); + fprintf(stderr, + " --warnings (-w): Output warnings from libpng.\n" + " --verbose (-v): Output more verbose messages.\n" + " --max=: Output IDAT chunks sized . If not given the\n" + " the IDAT chunks will be the maximum size permitted\n" + " (2^31-1 bytes.)\n" + " --out=: Save the result for the next PNG to .\n" + " --inplace (-i): Modify the file in place.\n"); + fprintf(stderr, + "\nExit codes:\n" + " 0: Success, all files pass the test, all output written ok.\n" + " 1: At least one file had a read error, all files checked.\n" + " 2: A file had an unrecoverable error (integer overflow, bad format),\n" + " the program exited immediately, without processing further files.\n" + " 3: An IO or out of memory error, or a file could not be opened.h\n"); + fprintf(stderr, + "\nDescription:\n" + " %s checks each PNG file on the command line for errors.\n" + " By default it is silent and just exits with an error code (as above)\n" + " if any error is detected. With --optimize, --strip or --out,\n" + " however, the zlib \"invalid distance too far back\" error is fixed\n" + " and the program exits with a 0 success code unless some other error\n" + " is encountered.\n" + "\n", prog); + fprintf(stderr, + " Use --errors to display the other errors, use --optimize to test\n" + " different values for the deflate \"window bits\" parameter and find\n" + " the smallest that works.\n" + "\n" + " Notice that some PNG files with the zlib header problem can still be\n" + " read by libpng. This program will still detect the error.\n" + "\n"); + fprintf(stderr, + " The output produced with --optimize is as follows:\n" + "\n" + " opt-bits curr-bits image-bits opt-flag opt-type change file\n" + "\n" + " opt-bits: The minimum window bits (8-15) that works, if the file\n" + " is written this is the value that will be stored.\n" + " curr-bits: The value currently stored in the file.\n"); + fprintf(stderr, + " image-bits: The window bits value corresponding to the size of the\n" + " uncompressed PNG image data. When --optimize is not\n" + " given but --strip is this value will be used if lower\n" + " than the current value.\n" + " opt-flag: < if the optimized bit value is less than that implied by\n" + " the PNG image size (opt-bits < image-bits)\n" + " = if optimization is not possible (opt-bits = image-bits)\n" + " opt-type: reduce if opts-bits < curr-bits\n"); + fprintf(stderr, + " ok if opt-bits = curr-bits (no change required)\n" + " INCREASE if opt-bits > curr-bits (the file has the bug)\n" + " change: opt-bits - curr-bits, so negative if optimization is\n" + " possible, 0 if no change is required, positive if the\n" + " bug is present.\n" + " file: The file name.\n"); + + exit(rc); +} + +int +main(int argc, const char **argv) +{ + int err, strip = 0, optimize = 0, inplace = 0, done = 0; + png_uint_32 max_IDAT = 0x7fffffff; + FILE *fp; + const char *outfile = NULL; + const char *prog = *argv; + static const png_byte idat_bytes[4] = { 73, 68, 65, 84 }; + static const png_byte iend_bytes[4] = { 73, 69, 78, 68 }; + + /* Initialize this first, could be stored as a constant: */ + crc_IEND = crc_IDAT_head = crc32(0L, Z_NULL, 0); + crc_IDAT_head = crc32(crc_IDAT_head, idat_bytes, 4); + crc_IEND = crc32(crc_IEND, iend_bytes, 4); + + z_idat.next_in = Z_NULL; + z_idat.avail_in = 0; + z_idat.zalloc = Z_NULL; + z_idat.zfree = Z_NULL; + z_idat.opaque = Z_NULL; + + err = inflateInit(&z_idat); + if (err != Z_OK) + { + fprintf(stderr, "inflateInit failed %d \"%s\"\n", err, z_idat.msg); + inflateEnd(&z_idat); + return 3; + } + + fp = tmpfile(); + if (fp == NULL) + { + perror("tmpfile"); + fprintf(stderr, "could not open a temporary file\n"); + return 3; + } + + err = 0; + while (--argc > 0) + { + ++argv; + + if (strcmp(*argv, "--inplace") == 0 || strcmp(*argv, "-i") == 0) + ++inplace; + + else if (strncmp(*argv, "--max=", 6) == 0) + max_IDAT = (png_uint_32)atol(6+*argv); + + else if (strcmp(*argv, "--optimize") == 0 || strcmp(*argv, "-o") == 0) + ++optimize; + + else if (strncmp(*argv, "--out=", 6) == 0) + outfile = 6+*argv; + + else if (strcmp(*argv, "--strip") == 0 || strcmp(*argv, "-s") == 0) + ++strip; + + else if (strcmp(*argv, "--errors") == 0 || strcmp(*argv, "-e") == 0) + ++errors; + + else if (strcmp(*argv, "--warnings") == 0 || strcmp(*argv, "-w") == 0) + ++warnings; + + else if (strcmp(*argv, "--verbose") == 0 || strcmp(*argv, "-v") == 0) + ++verbose; + +# ifdef PNG_MAXIMUM_INFLATE_WINDOW + else if (strcmp(*argv, "--test") == 0) + ++set_option; +# endif + + else if ((*argv)[0] == '-') + usage(prog, 3); + + else + { + int ret; + + err += + fix_file(fp, *argv, max_IDAT, inplace, strip, optimize, outfile); + + if (fpIn != NULL) + { + fclose(fpIn); + fpIn = NULL; + } + + z_idat.next_in = z_idat.next_out = Z_NULL; + z_idat.avail_in = z_idat.avail_out = 0; + ret = inflateReset(&z_idat); + if (ret != Z_OK) + { + fprintf(stderr, "inflateReset failed %d \"%s\"\n", ret, z_idat.msg); + inflateEnd(&z_idat); + return 3; + } + + rewind(fp); + outfile = NULL; + ++done; + } + } + + inflateEnd(&z_idat); + + if (!done) + usage(prog, 0); + + return err != 0; +} + +#else /* No read support */ + +int +main(void) +{ + fprintf(stderr, "pngdeflate does not work without read support\n"); + return 77; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/png.h b/png.h index 076c9a25a..9f05ebd11 100644 --- a/png.h +++ b/png.h @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.3beta05 - May 6, 2013 + * libpng version 1.6.3beta05 - May 8, 2013 * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) @@ -11,7 +11,7 @@ * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.6.3beta05 - May 6, 2013: Glenn + * libpng versions 0.97, January 1998, through 1.6.3beta05 - May 8, 2013: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: @@ -207,7 +207,7 @@ * * This code is released under the libpng license. * - * libpng versions 1.2.6, August 15, 2004, through 1.6.3beta05, May 6, 2013, are + * libpng versions 1.2.6, August 15, 2004, through 1.6.3beta05, May 8, 2013, are * Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson, and are * distributed according to the same disclaimer and license as libpng-1.2.5 * with the following individual added to the list of Contributing Authors: @@ -319,7 +319,7 @@ * Y2K compliance in libpng: * ========================= * - * May 6, 2013 + * May 8, 2013 * * Since the PNG Development group is an ad-hoc body, we can't make * an official declaration. @@ -387,7 +387,7 @@ /* Version information for png.h - this should match the version in png.c */ #define PNG_LIBPNG_VER_STRING "1.6.3beta05" #define PNG_HEADER_VERSION_STRING \ - " libpng version 1.6.3beta05 - May 6, 2013\n" + " libpng version 1.6.3beta05 - May 8, 2013\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -3268,7 +3268,8 @@ PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, #ifdef PNG_ARM_NEON_API_SUPPORTED # define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ #endif -#define PNG_OPTION_NEXT 2 /* Next option - numbers must be even */ +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_OPTION_NEXT 4 /* Next option - numbers must be even */ /* Return values: NOTE: there are four values and 'off' is *not* zero */ #define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ diff --git a/pngrutil.c b/pngrutil.c index 79898ea53..4e84b0ebd 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -332,7 +332,7 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) * chunk apparently owns the stream. Prior to release it does a png_error. */ static int -png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) +png_inflate_claim(png_structrp png_ptr, png_uint_32 owner) { if (png_ptr->zowner != 0) { @@ -367,6 +367,21 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) */ { int ret; /* zlib return code */ +# if ZLIB_VERNUM >= 0x1240 + +# ifdef PNG_SET_OPTION_SUPPORTED + int window_bits; + + if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) == + PNG_OPTION_ON) + window_bits = 15; + + else + window_bits = 0; +# else +# define window_bits 0 +# endif +# endif /* Set this for safety, just in case the previous owner left pointers to * memory allocations. @@ -379,7 +394,6 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) { # if ZLIB_VERNUM < 0x1240 - PNG_UNUSED(window_bits) ret = inflateReset(&png_ptr->zstream); # else ret = inflateReset2(&png_ptr->zstream, window_bits); @@ -406,6 +420,10 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) return ret; } + +# ifdef window_bits +# undef window_bits +# endif } #ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED @@ -578,14 +596,8 @@ png_decompress_chunk(png_structrp png_ptr, if (limit < *newlength) *newlength = limit; - /* Now try to claim the stream; the 'warn' setting causes zlib to be told - * to use the maximum window size during inflate; this hides errors in the - * deflate header window bits value which is used if '0' is passed. In - * fact this only has an effect with zlib versions 1.2.4 and later - see - * the comments in png_inflate_claim above. - */ - ret = png_inflate_claim(png_ptr, png_ptr->chunk_name, - png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0); + /* Now try to claim the stream. */ + ret = png_inflate_claim(png_ptr, png_ptr->chunk_name); if (ret == Z_OK) { @@ -1355,8 +1367,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { read_length -= keyword_length+2; - if (png_inflate_claim(png_ptr, png_iCCP, - png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0) == Z_OK) + if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK) { Byte profile_header[132]; Byte local_buffer[PNG_INFLATE_BUF_SIZE]; @@ -4163,77 +4174,6 @@ png_read_finish_row(png_structrp png_ptr) } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -#ifdef PNG_READ_OPTIMIZE_WINDOWBITS_SUPPORTED -#if ZLIB_VERNUM > 0x1280 -/* This is the code to to select a windowBits value to match the smallest - * possible sliding window needed to contain the entire uncompressed image. - */ -static unsigned int -required_window_bits(png_alloc_size_t data_size) -{ - unsigned int windowBits = 15; - if (data_size <= 16384) /* else windowBits must be 15 */ - { - unsigned int half_z_window_size = 1U << (windowBits-1); /* 16384 */ - - do - { - half_z_window_size >>= 1; - --windowBits; - } - while (windowBits > 8 && data_size <= half_z_window_size); - } - return windowBits; -} -/* This is used below to find the size of an image to pass to png_deflate_claim, - * so it only needs to be accurate if the size is less than 16384 bytes (the - * point at which a lower LZ window size can be used.) - * - * To do: merge this with png_image_size() in pngwutil.c and put the result - * in png.c as a PNG_INTERNAL_FUNCTION. - */ -static png_alloc_size_t -png_read_image_size(png_structrp png_ptr) -{ - /* Only return sizes up to the maximum of a png_uint_32, do this by limiting - * the width and height used to 15 bits. - */ - png_uint_32 h = png_ptr->height; - - if (png_ptr->rowbytes < 32768 && h < 32768) - { - if (png_ptr->interlaced) - { - /* Interlacing makes the image larger because of the replication of - * both the filter byte and the padding to a byte boundary. - */ - png_uint_32 w = png_ptr->width; - unsigned int pd = png_ptr->pixel_depth; - png_alloc_size_t cb_base; - int pass; - - for (cb_base=0, pass=0; pass<=6; ++pass) - { - png_uint_32 pw = PNG_PASS_COLS(w, pass); - - if (pw > 0) - cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass); - } - - return cb_base; - } - - else - return (png_ptr->rowbytes+1) * h; - } - - else - return 0xffffffffU; -} - -#endif /* ZLIB_VERNUM */ -#endif /* PNG_READ_OPTIMIZE_WINDOWBITS_SUPPORTED */ - void /* PRIVATE */ png_read_start_row(png_structrp png_ptr) { @@ -4518,37 +4458,13 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) png_free(png_ptr, buffer); } -#ifdef PNG_READ_OPTIMIZE_WINDOWBITS_SUPPORTED - /* To do in libpng17: get windowBits from the CMF bytes and select the - * smaller of that and the required_window_bits. Requires a one-byte - * lookahead into the first IDAT chunk data, and requires actually - * injecting the revised CMF bytes into the datastream before reading. + /* Finally claim the zstream for the inflate of the IDAT data, use the bits + * value from the stream (note that this will result in a fatal error if the + * IDAT stream has a bogus deflate header window_bits value, but this should + * not be happening any longer!) */ - { -#if ZLIB_VERNUM < 0x1290 - unsigned int windowBits; -#endif /* ZLIB_VERNUM */ - - if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) - { -#if ZLIB_VERNUM < 0x1290 - windowBits=15; -#else - /* Compute required windowBits from the image size, pixel size, and - * interlacing setting. - */ - windowBits=required_window_bits(png_read_image_size(png_ptr)); -#endif /* ZLIB_VERNUM */ - } - - else -#endif - windowBits=0; /* Use the setting from the zlib CMF bytes */ - - /* Finally claim the zstream for the inflate of the IDAT data */ - if (png_inflate_claim(png_ptr, png_IDAT, windowBits) != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg); - } + if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); png_ptr->flags |= PNG_FLAG_ROW_INIT; } diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index e096c3ec4..2d1744eab 100755 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -399,7 +399,7 @@ option SAFE_LIMITS enables USER_LIMITS disabled # to libpng 1.6; the new interfaces in 1.6 will take several years to become # popular. -option READ enables READ_INTERLACING +option READ enables READ_INTERLACING SET_OPTION # Disabling READ_16BIT does not disable reading 16-bit PNG files, but it # forces them to be chopped down to 8-bit, and disables any 16-bit @@ -826,9 +826,6 @@ option GET_PALETTE_MAX enables READ_GET_PALETTE_MAX WRITE_GET_PALETTE_MAX option READ_GET_PALETTE_MAX requires READ_CHECK_FOR_INVALID_INDEX disabled option WRITE_GET_PALETTE_MAX requires WRITE_CHECK_FOR_INVALID_INDEX disabled -# added at libpng-1.6.3 -option READ_OPTIMIZE_WINDOWBITS requires READ - # Simplified API options (added at libpng-1.6.0) # Read: option SIMPLIFIED_READ, diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index 3d64e2763..6f90f4ce8 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -2,7 +2,7 @@ /* pnglibconf.h - library build configuration */ -/* Libpng version 1.6.3beta05 - May 6, 2013 */ +/* Libpng version 1.6.3beta05 - May 8, 2013 */ /* Copyright (c) 1998-2013 Glenn Randers-Pehrson */ @@ -105,10 +105,9 @@ #define PNG_SETJMP_SUPPORTED #define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED #define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED -/*#undef PNG_SET_OPTION_SUPPORTED*/ +#define PNG_SET_OPTION_SUPPORTED #define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED #define PNG_SET_USER_LIMITS_SUPPORTED -#define PNG_READ_OPTIMIZE_WINDOWBITS_SUPPORTED #define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED #define PNG_SIMPLIFIED_READ_BGR_SUPPORTED #define PNG_SIMPLIFIED_READ_SUPPORTED