[libpng16] 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.
diff --git a/ANNOUNCE b/ANNOUNCE
index a9126a0..e4b555f 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 @@
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 @@
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 3364ab8..a9b12bb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4009,7 +4009,7 @@
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 @@
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 809b888..4d9d2ad 100644
--- a/png.c
+++ b/png.c
@@ -283,7 +283,6 @@
*/
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_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 @@
#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 d3a3ce2..c7de794 100644
--- a/pngpread.c
+++ b/pngpread.c
@@ -766,7 +766,7 @@
{
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_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 @@
* 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 @@
*/
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 @@
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 @@
{
/* 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 @@
/* 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 f9451f0..c9d69bf 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -510,36 +510,36 @@
/* 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 @@
* 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 fb9566c..59721fe 100644
--- a/pngread.c
+++ b/pngread.c
@@ -48,61 +48,14 @@
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_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 @@
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_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_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 ae3d148..2be25e1 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -277,6 +277,59 @@
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_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 @@
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_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_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 @@
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 @@
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 @@
}
#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_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_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 @@
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 @@
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_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 @@
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 467007e..87ea234 100644
--- a/pngstruct.h
+++ b/pngstruct.h
@@ -68,20 +68,8 @@
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 @@
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 @@
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 11ede9b..7fb8577 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -472,6 +472,23 @@
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_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 @@
if (png_ptr == NULL)
return;
- png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
png_ptr->zlib_level = level;
}
@@ -1278,7 +1294,6 @@
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 @@
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 @@
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 @@
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 @@
if (png_ptr == NULL)
return;
- png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_LEVEL;
png_ptr->zlib_text_level = level;
}
@@ -1359,7 +1379,6 @@
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 @@
if (png_ptr == NULL)
return;
- png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_STRATEGY;
png_ptr->zlib_text_strategy = strategy;
}
@@ -1385,21 +1403,17 @@
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 @@
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 b05929e..b5b21b6 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -214,87 +214,159 @@
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 @@
* 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 @@
* 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_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 @@
/* 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_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_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_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