diff --git a/ANNOUNCE b/ANNOUNCE index a9126a098..e4b555f11 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,5 +1,5 @@ -Libpng 1.6.0beta16 - March 4, 2012 +Libpng 1.6.0beta16 - March 6, 2012 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. @@ -258,7 +258,7 @@ Version 1.6.0beta15 [March 2, 2012] without the necessary color data. Removed whitespace from the end of lines in all source files and scripts. -Version 1.6.0beta16 [March 4, 2012] +Version 1.6.0beta16 [March 6, 2012] Relocated palette-index checking function from pngrutil.c to pngtrans.c Added palette-index checking while writing. Changed png_inflate() and calling routines to avoid overflow problems. @@ -275,6 +275,12 @@ Version 1.6.0beta16 [March 4, 2012] Added contrib/libtests/tarith.c to test internal arithmetic functions from png.c. This is a libpng maintainer program used to validate changes to the internal arithmetic functions. + Made read 'inflate' handling like write 'deflate' handling. The read + code now claims and releases png_ptr->zstream, like the write code. + The bug whereby the progressive reader failed to release the zstream + is now fixed, all initialization is delayed, and the code checks for + changed parameters on deflate rather than always calling + deflatedEnd/deflateInit. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 3364ab80a..a9b12bb30 100644 --- a/CHANGES +++ b/CHANGES @@ -4009,7 +4009,7 @@ Version 1.6.0beta15 [March 2, 2012] without the necessary color data. Removed whitespace from the end of lines in all source files and scripts. -Version 1.6.0beta16 [March 4, 2012] +Version 1.6.0beta16 [March 6, 2012] Relocated palette-index checking function from pngrutil.c to pngtrans.c Added palette-index checking while writing. Changed png_inflate() and calling routines to avoid overflow problems. @@ -4026,6 +4026,12 @@ Version 1.6.0beta16 [March 4, 2012] Added contrib/libtests/tarith.c to test internal arithmetic functions from png.c. This is a libpng maintainer program used to validate changes to the internal arithmetic functions. + Made read 'inflate' handling like write 'deflate' handling. The read + code now claims and releases png_ptr->zstream, like the write code. + The bug whereby the progressive reader failed to release the zstream + is now fixed, all initialization is delayed, and the code checks for + changed parameters on deflate rather than always calling + deflatedEnd/deflateInit. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/png.c b/png.c index 809b888a8..4d9d2ad90 100644 --- a/png.c +++ b/png.c @@ -283,7 +283,6 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, */ if (png_user_version_check(&create_struct, user_png_ver)) { - /* TODO: delay initializing the zlib structure until it really is * needed. */ @@ -309,6 +308,13 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, *png_ptr = create_struct; + /* png_ptr->zstream holds a back-pointer to the png_struct, so + * this can only be done now: + */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = png_ptr; + /* This is the successful return point */ return png_ptr; } @@ -762,13 +768,13 @@ png_get_copyright(png_const_structrp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.6.0beta16 - March 3, 2012" PNG_STRING_NEWLINE \ + "libpng version 1.6.0beta16 - March 6, 2012" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2012 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ PNG_STRING_NEWLINE; # else - return "libpng version 1.6.0beta16 - March 3, 2012\ + return "libpng version 1.6.0beta16 - March 6, 2012\ Copyright (c) 1998-2012 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; diff --git a/pngpread.c b/pngpread.c index d3a3ce2d3..c7de794ae 100644 --- a/pngpread.c +++ b/pngpread.c @@ -766,7 +766,7 @@ png_push_read_IDAT(png_structrp png_ptr) { png_ptr->process_mode = PNG_READ_CHUNK_MODE; - if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) png_error(png_ptr, "Not enough compressed data"); return; @@ -838,6 +838,7 @@ png_push_read_IDAT(png_structrp png_ptr) png_crc_finish(png_ptr, 0); png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; } } @@ -854,13 +855,14 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, * handle the uncompressed results. */ png_ptr->zstream.next_in = buffer; + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ png_ptr->zstream.avail_in = (uInt)buffer_length; /* Keep going until the decompressed data is all processed * or the stream marked as finished. */ while (png_ptr->zstream.avail_in > 0 && - !(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) { int ret; @@ -871,9 +873,9 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, */ if (!(png_ptr->zstream.avail_out > 0)) { - png_ptr->zstream.avail_out = - (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1; + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ + png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1); png_ptr->zstream.next_out = png_ptr->row_buf; } @@ -891,7 +893,8 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, if (ret != Z_OK && ret != Z_STREAM_END) { /* Terminate the decompression. */ - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; /* This may be a truncated stream (missing or * damaged end code). Treat that as a warning. @@ -919,7 +922,8 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, { /* Extra data. */ png_warning(png_ptr, "Extra compressed data in IDAT"); - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; /* Do no more processing; skip the unprocessed * input check below. @@ -934,7 +938,7 @@ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, /* And check for the end of the stream. */ if (ret == Z_STREAM_END) - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; } /* All the data should have been processed, if anything diff --git a/pngpriv.h b/pngpriv.h index f9451f0c4..c9d69bfab 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -510,36 +510,36 @@ typedef const png_uint_16p * png_const_uint_16pp; /* Flags for the png_ptr->flags rather than declaring a byte for each one */ #define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 -#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 -#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 -#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 -#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 -#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */ +#define PNG_FLAG_ZSTREAM_IN_USE 0x0004 /* Added to libpng-1.6.0 */ +#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */ + /* 0x0010 unused */ + /* 0x0020 unused */ #define PNG_FLAG_ROW_INIT 0x0040 #define PNG_FLAG_FILLER_AFTER 0x0080 #define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 #define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 #define PNG_FLAG_CRC_CRITICAL_USE 0x0400 #define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 -#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */ -#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */ -#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */ #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000 -#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 -#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 -#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 - /* 0x100000 unused */ - /* 0x200000 unused */ - /* 0x400000 unused */ -#define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000 /* Added to libpng-1.4.0 */ -#define PNG_FLAG_ZTXT_CUSTOM_STRATEGY 0x1000000 /* 5 lines added */ -#define PNG_FLAG_ZTXT_CUSTOM_LEVEL 0x2000000 /* to libpng-1.5.4 */ -#define PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL 0x4000000 -#define PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS 0x8000000 -#define PNG_FLAG_ZTXT_CUSTOM_METHOD 0x10000000 - /* 0x20000000 unused */ - /* 0x40000000 unused */ +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */ + /* 0x200000 unused */ + /* 0x400000 unused */ + /* 0x800000 unused */ + /* 0x1000000 unused */ + /* 0x2000000 unused */ + /* 0x4000000 unused */ + /* 0x8000000 unused */ + /* 0x10000000 unused */ + /* 0x20000000 unused */ + /* 0x40000000 unused */ #define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ PNG_FLAG_CRC_ANCILLARY_NOWARN) @@ -734,6 +734,9 @@ extern "C" { * All of these functions must be declared with PNG_INTERNAL_FUNCTION. */ +/* Zlib support */ +PNG_INTERNAL_FUNCTION(void,png_inflate_claim,(png_structrp png_ptr),PNG_EMPTY); + #if defined PNG_FLOATING_POINT_SUPPORTED &&\ !defined PNG_FIXED_POINT_MACRO_SUPPORTED PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, diff --git a/pngread.c b/pngread.c index fb9566ce7..59721fe2d 100644 --- a/pngread.c +++ b/pngread.c @@ -48,61 +48,14 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, if (png_ptr != NULL) { - int ok = 0; - - /* TODO: why does this happen here on read, but in png_write_IHDR on - * write? If it happened there then there would be no error handling case - * here and png_ptr could be a png_structrp. + /* TODO: delay this, it can be done in png_init_io (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. */ - png_ptr->zstream.zalloc = png_zalloc; - png_ptr->zstream.zfree = png_zfree; - png_ptr->zstream.opaque = png_ptr; - - switch (inflateInit(&png_ptr->zstream)) - { - case Z_OK: - ok = 1; - break; - - case Z_MEM_ERROR: - png_warning(png_ptr, "zlib memory error"); - break; - - case Z_STREAM_ERROR: - png_warning(png_ptr, "zlib stream error"); - break; - - case Z_VERSION_ERROR: - png_warning(png_ptr, "zlib version error"); - break; - - default: - png_warning(png_ptr, "Unknown zlib error"); - break; - } - - if (ok) - { - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = png_ptr->zbuf_size; - - /* TODO: delay this, it can be done in png_init_io (if the app doesn't - * do it itself) avoiding setting the default function if it is not - * required. - */ - png_set_read_fn(png_ptr, NULL, NULL); - - return png_ptr; - } - - /* Else something went wrong in the zlib initialization above; it would - * much simplify this code if the creation of the zlib stuff was to be - * delayed until it is needed. - */ - png_destroy_read_struct(&png_ptr, NULL, NULL); + png_set_read_fn(png_ptr, NULL, NULL); } - return NULL; + return png_ptr; } @@ -471,15 +424,17 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) png_error(png_ptr, "Invalid attempt to read row data"); png_ptr->zstream.next_out = png_ptr->row_buf; - png_ptr->zstream.avail_out = - (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + /* TODO: WARNING: BAD NEWS ALERT: this fails, terminally, if the row width is + * bigger than a uInt. + */ + png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); do { if (!(png_ptr->zstream.avail_in)) { - while (!png_ptr->idat_size) + while (png_ptr->idat_size == 0) { png_crc_finish(png_ptr, 0); @@ -487,7 +442,7 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) if (png_ptr->chunk_name != png_IDAT) png_error(png_ptr, "Not enough image data"); } - png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.avail_in = png_ptr->zbuf_size; png_ptr->zstream.next_in = png_ptr->zbuf; if (png_ptr->zbuf_size > png_ptr->idat_size) png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; @@ -495,7 +450,11 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) png_ptr->idat_size -= png_ptr->zstream.avail_in; } - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + /* Use NO_FLUSH, not SYNC_FLUSH, here because we keep reading data until + * we have a row to process (so leave it to zlib to decide when to flush + * the output.) + */ + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); if (ret == Z_STREAM_END) { @@ -503,7 +462,9 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) png_ptr->idat_size) png_benign_error(png_ptr, "Extra compressed data"); png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + /* Release the stream */ + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; break; } diff --git a/pngrutil.c b/pngrutil.c index ae3d148d6..2be25e106 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -277,6 +277,59 @@ png_crc_error(png_structrp png_ptr) return (0); } +/* png_inflate_claim: claim the zstream for some nefarious purpose that involves + * decompression. + */ +void /* PRIVATE */ +png_inflate_claim(png_structrp png_ptr) +{ + /* Implementation note: unlike 'png_deflate_claim' this internal function + * does not take the size of the data as an argument. Some efficiency could + * be gained by using this when it is known *if* the zlib stream itself does + * not record the number, however this is a chimera: the original writer of + * the PNG may have selected a lower window size, and we really must follow + * that because, for systems with with limited capabilities, we would + * otherwise reject the applications attempts to use a smaller window size. + * (zlib doesn't have an interface to say "this or lower"!) + */ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE)) + { + int ret; /* zlib return code */ + + /* Do this for safety now. */ + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED; + + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + ret = inflateReset(&png_ptr->zstream); + + else + { + ret = inflateInit(&png_ptr->zstream); + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_IN_USE; + + else + { + /* A problem; the flags are set ok, but we need a credible error + * message. + */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + + else + png_error(png_ptr, "zlib initialization error"); + } + } + + else + png_error(png_ptr, "zstream already in use (internal error)"); +} + #ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED /* png_inflate: returns one of the following error codes. If output is not NULL * the uncompressed data is placed there, up to output_size. output_size is @@ -308,15 +361,6 @@ png_inflate(png_structrp png_ptr, png_bytep data, png_uint_32 input_size, png_alloc_size_t avail_out = *output_size; png_uint_32 avail_in = input_size; - /* TODO: PROBLEM: png_ptr-zstream is not reset via inflateReset after reading - * the image, consequently the first compressed chunk (zTXt or iTXt) after - * the data gets a 'finished' stream and the first call to inflate returns - * zero decompressed bytes. This was handled silently before 1.6 by - * returning empty uncompressed data. This is a temporary work-round (it's - * harmless if the stream is already reset). - */ - inflateReset(&png_ptr->zstream); - /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it can't * even necessarily handle 65536 bytes) because the type uInt is "16 bits or * more". Consequently it is necessary to chunk the input to zlib. This @@ -423,6 +467,7 @@ png_inflate(png_structrp png_ptr, png_bytep data, png_uint_32 input_size, switch (ret) { case Z_STREAM_END: + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; return PNG_INFLATE_OK; case Z_BUF_ERROR: /* no progress in zlib */ @@ -479,6 +524,8 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, */ png_alloc_size_t limit = PNG_SIZE_MAX; + png_inflate_claim(png_ptr); + # ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED if (png_ptr->user_chunk_malloc_max > 0 && png_ptr->user_chunk_malloc_max < limit) @@ -532,6 +579,7 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)text; + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; return NULL; } @@ -540,10 +588,12 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, case PNG_INFLATE_TRUNCATED: png_free(png_ptr, text); + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; return "libpng inflate error"; default: /* PNG_INFLATE_ERROR */ png_free(png_ptr, text); + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; return (png_const_charp)png_ptr->zbuf; } } @@ -556,11 +606,13 @@ png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength, default: /* PNG_INFLATE_ERROR */ /* png_inflate puts the error message in zbuf. */ + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; return (png_const_charp)png_ptr->zbuf; } } /* out of memory returns. */ + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; return "insufficient memory"; } #endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */ @@ -3830,19 +3882,20 @@ png_read_finish_row(png_structrp png_ptr) } #endif /* PNG_READ_INTERLACING_SUPPORTED */ - if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + /* Here after at the end of the last row of the last pass. */ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) { - char extra; + Byte extra; int ret; - png_ptr->zstream.next_out = (Byte *)&extra; - png_ptr->zstream.avail_out = (uInt)1; + png_ptr->zstream.next_out = &extra; + png_ptr->zstream.avail_out = 1; for (;;) { if (!(png_ptr->zstream.avail_in)) { - while (!png_ptr->idat_size) + while (png_ptr->idat_size == 0) { png_crc_finish(png_ptr, 0); png_ptr->idat_size = png_read_chunk_header(png_ptr); @@ -3850,17 +3903,17 @@ png_read_finish_row(png_structrp png_ptr) png_error(png_ptr, "Not enough image data"); } - png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.avail_in = png_ptr->zbuf_size; png_ptr->zstream.next_in = png_ptr->zbuf; if (png_ptr->zbuf_size > png_ptr->idat_size) - png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_ptr->zstream.avail_in = png_ptr->idat_size; png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); png_ptr->idat_size -= png_ptr->zstream.avail_in; } - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); if (ret == Z_STREAM_END) { @@ -3868,8 +3921,7 @@ png_read_finish_row(png_structrp png_ptr) png_ptr->idat_size) png_warning(png_ptr, "Extra compressed data"); - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; break; } @@ -3880,11 +3932,8 @@ png_read_finish_row(png_structrp png_ptr) if (!(png_ptr->zstream.avail_out)) { png_warning(png_ptr, "Extra compressed data"); - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; break; } - } png_ptr->zstream.avail_out = 0; } @@ -3892,8 +3941,7 @@ png_read_finish_row(png_structrp png_ptr) if (png_ptr->idat_size || png_ptr->zstream.avail_in) png_warning(png_ptr, "Extra compression data"); - inflateReset(&png_ptr->zstream); - + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; png_ptr->mode |= PNG_AFTER_IDAT; } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ @@ -3921,7 +3969,7 @@ png_read_start_row(png_structrp png_ptr) png_size_t row_bytes; png_debug(1, "in png_read_start_row"); - png_ptr->zstream.avail_in = 0; + #ifdef PNG_READ_TRANSFORMS_SUPPORTED png_init_read_transformations(png_ptr); #endif @@ -4169,6 +4217,9 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) png_debug1(3, "irowbytes = %lu", (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); + /* Finally claim the zstream for the inflate of the IDAT data. */ + png_inflate_claim(png_ptr); + png_ptr->flags |= PNG_FLAG_ROW_INIT; } #endif /* PNG_READ_SUPPORTED */ diff --git a/pngstruct.h b/pngstruct.h index 467007ec2..87ea234b0 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -68,20 +68,8 @@ struct png_struct_def z_stream zstream; /* pointer to decompression structure (below) */ png_bytep zbuf; /* buffer for zlib */ uInt zbuf_size; /* size of zbuf (typically 65536) */ + #ifdef PNG_WRITE_SUPPORTED - -/* Added in 1.5.4: state to keep track of whether the zstream has been - * initialized and if so whether it is for IDAT or some other chunk. - */ -#define PNG_ZLIB_UNINITIALIZED 0 -#define PNG_ZLIB_FOR_IDAT 1 -#define PNG_ZLIB_FOR_TEXT 2 /* anything other than IDAT */ -#define PNG_ZLIB_USE_MASK 3 /* bottom two bits */ -#define PNG_ZLIB_IN_USE 4 /* a flag value */ - - png_uint_32 zlib_state; /* State of zlib initialization */ -/* End of material added at libpng 1.5.4 */ - int zlib_level; /* holds zlib compression level */ int zlib_method; /* holds zlib compression method */ int zlib_window_bits; /* holds zlib compression window bits */ @@ -89,8 +77,7 @@ struct png_struct_def int zlib_strategy; /* holds zlib compression strategy */ #endif /* Added at libpng 1.5.4 */ -#if defined(PNG_WRITE_COMPRESSED_TEXT_SUPPORTED) || \ - defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED) +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED int zlib_text_level; /* holds zlib compression level */ int zlib_text_method; /* holds zlib compression method */ int zlib_text_window_bits; /* holds zlib compression window bits */ @@ -98,6 +85,14 @@ struct png_struct_def int zlib_text_strategy; /* holds zlib compression strategy */ #endif /* End of material added at libpng 1.5.4 */ +/* Added at libpng 1.6.0 */ +#ifdef PNG_WRITE_SUPPORTED + int zlib_set_level; /* Actual values set into the zstream on write */ + int zlib_set_method; + int zlib_set_window_bits; + int zlib_set_mem_level; + int zlib_set_strategy; +#endif png_uint_32 width; /* width of image in pixels */ png_uint_32 height; /* height of image in pixels */ diff --git a/pngwrite.c b/pngwrite.c index 11ede9bf9..7fb857745 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -472,6 +472,23 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); #endif /* PNG_USER_MEM_SUPPORTED */ + /* Set the zlib control values to defaults; they can be overridden by the + * application after the struct has been created. + */ + png_ptr->zlib_strategy = Z_FILTERED; /* may be overridden if no filters */ + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + png_ptr->zlib_mem_level = 8; + png_ptr->zlib_window_bits = 15; + png_ptr->zlib_method = 8; + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED + png_ptr->zlib_text_strategy = Z_DEFAULT_STRATEGY; + png_ptr->zlib_text_level = Z_DEFAULT_COMPRESSION; + png_ptr->zlib_text_mem_level = 8; + png_ptr->zlib_text_window_bits = 15; + png_ptr->zlib_text_method = 8; +#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ + if (png_ptr != NULL) { /* TODO: delay this, it can be done in png_init_io() (if the app doesn't @@ -827,7 +844,7 @@ png_write_destroy(png_structrp png_ptr) png_debug(1, "in png_write_destroy"); /* Free any memory zlib uses */ - if (png_ptr->zlib_state != PNG_ZLIB_UNINITIALIZED) + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) deflateEnd(&png_ptr->zstream); /* Free our memory. png_free checks NULL for us. */ @@ -1266,7 +1283,6 @@ png_set_compression_level(png_structrp png_ptr, int level) if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; png_ptr->zlib_level = level; } @@ -1278,7 +1294,6 @@ png_set_compression_mem_level(png_structrp png_ptr, int mem_level) if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; png_ptr->zlib_mem_level = mem_level; } @@ -1290,6 +1305,8 @@ png_set_compression_strategy(png_structrp png_ptr, int strategy) if (png_ptr == NULL) return; + /* The flag setting here prevents the libpng dynamic selection of strategy. + */ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; png_ptr->zlib_strategy = strategy; } @@ -1303,22 +1320,24 @@ png_set_compression_window_bits(png_structrp png_ptr, int window_bits) if (png_ptr == NULL) return; + /* Prior to 1.6.0 this would warn but then set the window_bits value, this + * meant that negative window bits values could be selected which would cause + * libpng to write a non-standard PNG file with raw deflate or gzip + * compressed IDAT or ancilliary chunks. Such files can be read and there is + * no warning on read, so this seems like a very bad idea. + */ if (window_bits > 15) + { png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } else if (window_bits < 8) + { png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } -#ifndef WBITS_8_OK - /* Avoid libpng bug with 256-byte windows */ - if (window_bits == 8) - { - png_warning(png_ptr, "Compression window is being reset to 512"); - window_bits = 9; - } - -#endif - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; png_ptr->zlib_window_bits = window_bits; } @@ -1330,10 +1349,12 @@ png_set_compression_method(png_structrp png_ptr, int method) if (png_ptr == NULL) return; + /* This would produce an invalid PNG file if it worked, but it doesn't and + * deflate will fault it, so it is harmless to just warn here. + */ if (method != 8) png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; png_ptr->zlib_method = method; } @@ -1347,7 +1368,6 @@ png_set_text_compression_level(png_structrp png_ptr, int level) if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_LEVEL; png_ptr->zlib_text_level = level; } @@ -1359,7 +1379,6 @@ png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL; png_ptr->zlib_text_mem_level = mem_level; } @@ -1371,7 +1390,6 @@ png_set_text_compression_strategy(png_structrp png_ptr, int strategy) if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_STRATEGY; png_ptr->zlib_text_strategy = strategy; } @@ -1385,21 +1403,17 @@ png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) return; if (window_bits > 15) + { png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } else if (window_bits < 8) + { png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } -#ifndef WBITS_8_OK - /* Avoid libpng bug with 256-byte windows */ - if (window_bits == 8) - { - png_warning(png_ptr, "Text compression window is being reset to 512"); - window_bits = 9; - } - -#endif - png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS; png_ptr->zlib_text_window_bits = window_bits; } @@ -1414,7 +1428,6 @@ png_set_text_compression_method(png_structrp png_ptr, int method) if (method != 8) png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_METHOD; png_ptr->zlib_text_method = method; } #endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ diff --git a/pngwutil.c b/pngwutil.c index b05929e65..b5b21b6ed 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -214,87 +214,159 @@ png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string, length); } -/* Initialize the compressor for the appropriate type of compression. */ -static void -png_zlib_claim(png_structrp png_ptr, png_uint_32 state) +/* 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.) + */ +static png_alloc_size_t +png_image_size(png_structrp png_ptr) { - if (!(png_ptr->zlib_state & PNG_ZLIB_IN_USE)) + /* 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 already initialized for 'state' do not re-init. */ - if (png_ptr->zlib_state != state) + if (png_ptr->interlaced) { - int ret = Z_OK; - png_const_charp who = "-"; + /* 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 cbBase; + int pass; - /* If actually initialized for another state do a deflateEnd. */ - if (png_ptr->zlib_state != PNG_ZLIB_UNINITIALIZED) + for (cbBase=0, pass=0; pass<=6; ++pass) { - ret = deflateEnd(&png_ptr->zstream); - who = "end"; - png_ptr->zlib_state = PNG_ZLIB_UNINITIALIZED; + png_uint_32 pw = PNG_PASS_COLS(w, pass); + + if (pw > 0) + cbBase += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass); } - /* zlib itself detects an incomplete state on deflateEnd */ - if (ret == Z_OK) switch (state) - { -# ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED - case PNG_ZLIB_FOR_TEXT: - ret = deflateInit2(&png_ptr->zstream, - png_ptr->zlib_text_level, png_ptr->zlib_text_method, - png_ptr->zlib_text_window_bits, - png_ptr->zlib_text_mem_level, png_ptr->zlib_text_strategy); - who = "text"; - break; -# endif - - case PNG_ZLIB_FOR_IDAT: - ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, - png_ptr->zlib_method, png_ptr->zlib_window_bits, - png_ptr->zlib_mem_level, png_ptr->zlib_strategy); - who = "IDAT"; - break; - - default: - png_error(png_ptr, "invalid zlib state"); - } - - if (ret == Z_OK) - png_ptr->zlib_state = state; - - else /* an error in deflateEnd or deflateInit2 */ - { - size_t pos = 0; - char msg[64]; - - pos = png_safecat(msg, sizeof msg, pos, - "zlib failed to initialize compressor ("); - pos = png_safecat(msg, sizeof msg, pos, who); - - switch (ret) - { - case Z_VERSION_ERROR: - pos = png_safecat(msg, sizeof msg, pos, ") version error"); - break; - - case Z_STREAM_ERROR: - pos = png_safecat(msg, sizeof msg, pos, ") stream error"); - break; - - case Z_MEM_ERROR: - pos = png_safecat(msg, sizeof msg, pos, ") memory error"); - break; - - default: - pos = png_safecat(msg, sizeof msg, pos, ") unknown error"); - break; - } - - png_error(png_ptr, msg); - } + return cbBase; } - /* Here on success, claim the zstream: */ - png_ptr->zlib_state |= PNG_ZLIB_IN_USE; + else + return (png_ptr->rowbytes+1) * h; + } + + else + return 0xffffffffU; +} + +/* Initialize the compressor for the appropriate type of compression. */ +static void +png_deflate_claim(png_structrp png_ptr, int for_IDAT, + png_alloc_size_t data_size) +{ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE)) + { + int level = png_ptr->zlib_level; + int method = png_ptr->zlib_method; + int windowBits = png_ptr->zlib_window_bits; + int memLevel = png_ptr->zlib_mem_level; + int strategy; /* set below */ + int ret; /* zlib return code */ + + /* Do this for safety now. */ + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED; + + if (for_IDAT) + { + if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) + strategy = png_ptr->zlib_strategy; + + else if (png_ptr->do_filter != PNG_FILTER_NONE) + strategy = Z_FILTERED; + + else + strategy = Z_DEFAULT_STRATEGY; + } + + else + { +# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + level = png_ptr->zlib_text_level; + method = png_ptr->zlib_text_method; + windowBits = png_ptr->zlib_text_window_bits; + memLevel = png_ptr->zlib_text_mem_level; + strategy = png_ptr->zlib_text_strategy; +# else + /* If customization is not supported the values all come from the + * IDAT values except for the strategy, which is fixed to the + * default. (This is the pre-1.6.0 behavior too, although it was + * implemented in a very different way.) + */ + strategy = Z_DEFAULT_STRATEGY; +# endif + } + + /* Adjust 'windowBits' down if larger than 'data_size'; to stop this + * happening just pass 32768 as the data_size parameter. + */ + if (data_size <= 256) + windowBits = 8; + + else if (data_size <= 16384) /* Else 15 bits required */ for (;;) + { + png_uint_32 test = 1U << (windowBits-1); + if (data_size > test) + break; + --windowBits; + } + + /* Check against the previous initialized values, if any. */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) && + (png_ptr->zlib_set_level != level || + png_ptr->zlib_set_method != method || + png_ptr->zlib_set_window_bits != windowBits || + png_ptr->zlib_set_mem_level != memLevel || + png_ptr->zlib_set_strategy != strategy)) + { + if (deflateEnd(&png_ptr->zstream) != Z_OK) + png_warning(png_ptr, "deflateEnd failed (ignored)"); + + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* Now initialize if required, setting the new parameters, otherwise just + * to a simple reset to the previous parameters. + */ + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + ret = deflateReset(&png_ptr->zstream); + + else + { + ret = deflateInit2(&png_ptr->zstream, level, method, windowBits, + memLevel, strategy); + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* The return code is from either deflateReset or deflateInit2; they have + * pretty much the same set of error codes. + */ + if (ret == Z_OK) + { + /* No problem, so the stream is now 'in use'. */ + png_ptr->flags |= PNG_FLAG_ZSTREAM_IN_USE; + } + + else + { + /* A problem; the flags are set ok, but we need a credible error + * message. + */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + + else + png_error(png_ptr, "zlib initialization error"); + } } else @@ -305,52 +377,10 @@ png_zlib_claim(png_structrp png_ptr, png_uint_32 state) * error but will not fail. */ static void -png_zlib_release(png_structrp png_ptr) +png_deflate_release(png_structrp png_ptr) { - if (png_ptr->zlib_state & PNG_ZLIB_IN_USE) - { - int ret = deflateReset(&png_ptr->zstream); - - png_ptr->zlib_state &= ~PNG_ZLIB_IN_USE; - - if (ret != Z_OK) - { - png_const_charp err; - PNG_WARNING_PARAMETERS(p) - - switch (ret) - { - case Z_VERSION_ERROR: - err = "version"; - break; - - case Z_STREAM_ERROR: - err = "stream"; - break; - - case Z_MEM_ERROR: - err = "memory"; - break; - - default: - err = "unknown"; - break; - } - - png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d, ret); - png_warning_parameter(p, 2, err); - - if (png_ptr->zstream.msg) - err = png_ptr->zstream.msg; - else - err = "[no zlib message]"; - - png_warning_parameter(p, 3, err); - - png_formatted_warning(png_ptr, p, - "zlib failed to reset compressor: @1(@2): @3"); - } - } + if (png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE) + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE; else png_warning(png_ptr, "zstream not in use (internal error)"); @@ -416,7 +446,7 @@ png_text_compress(png_structrp png_ptr, * data, or if the input string is incredibly large (although this * wouldn't cause a failure, just a slowdown due to swapping). */ - png_zlib_claim(png_ptr, PNG_ZLIB_FOR_TEXT); + png_deflate_claim(png_ptr, 0/*!for IDAT*/, text_len); /* Set up the compression buffers */ /* TODO: the following cast hides a ****CERTAIN**** overflow problem. */ @@ -663,7 +693,7 @@ png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp) (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out)); /* Reset zlib for another zTXt/iTXt or image data */ - png_zlib_release(png_ptr); + png_deflate_release(png_ptr); } #endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ @@ -819,11 +849,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, /* Write the chunk */ png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); - /* Initialize zlib with PNG info */ - png_ptr->zstream.zalloc = png_zalloc; - png_ptr->zstream.zfree = png_zfree; - png_ptr->zstream.opaque = (voidpf)png_ptr; - if (!(png_ptr->do_filter)) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || @@ -834,55 +859,6 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, png_ptr->do_filter = PNG_ALL_FILTERS; } - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) - { - if (png_ptr->do_filter != PNG_FILTER_NONE) - png_ptr->zlib_strategy = Z_FILTERED; - - else - png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; - } - - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) - png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; - - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) - png_ptr->zlib_mem_level = 8; - - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) - png_ptr->zlib_window_bits = 15; - - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) - png_ptr->zlib_method = 8; - -#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED - if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_STRATEGY)) - png_ptr->zlib_text_strategy = Z_DEFAULT_STRATEGY; - - if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_LEVEL)) - png_ptr->zlib_text_level = png_ptr->zlib_level; - - if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL)) - png_ptr->zlib_text_mem_level = png_ptr->zlib_mem_level; - - if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS)) - png_ptr->zlib_text_window_bits = png_ptr->zlib_window_bits; - - if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_METHOD)) - png_ptr->zlib_text_method = png_ptr->zlib_method; -#else - png_ptr->zlib_text_strategy = Z_DEFAULT_STRATEGY; - png_ptr->zlib_text_level = png_ptr->zlib_level; - png_ptr->zlib_text_mem_level = png_ptr->zlib_mem_level; - png_ptr->zlib_text_window_bits = png_ptr->zlib_window_bits; - png_ptr->zlib_text_method = png_ptr->zlib_method; -#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ -#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ - - /* Record that the compressor has not yet been initialized. */ - png_ptr->zlib_state = PNG_ZLIB_UNINITIALIZED; - png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */ } @@ -2103,8 +2079,8 @@ png_write_start_row(png_structrp png_ptr) png_ptr->usr_width = png_ptr->width; } - png_zlib_claim(png_ptr, PNG_ZLIB_FOR_IDAT); - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_deflate_claim(png_ptr, 1/*for IDAT*/, png_image_size(png_ptr)); + png_ptr->zstream.avail_out = png_ptr->zbuf_size; png_ptr->zstream.next_out = png_ptr->zbuf; } @@ -2225,8 +2201,7 @@ png_write_finish_row(png_structrp png_ptr) png_ptr->zstream.avail_out); } - png_zlib_release(png_ptr); - png_ptr->zstream.data_type = Z_BINARY; + png_deflate_release(png_ptr); } #ifdef PNG_WRITE_INTERLACING_SUPPORTED