[libpng16] Deflate/inflate was reworked to move common zlib calls into single

functions [rw]util.c.  A new shared keyword check routine was also added
and the 'zbuf' is no longer allocated on progressive read.  It is now
possible to call png_inflate() incrementally.
diff --git a/ANNOUNCE b/ANNOUNCE
index 4a0fdde..f91b5e4 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,5 +1,5 @@
 
-Libpng 1.6.0beta17 - March 8, 2012
+Libpng 1.6.0beta17 - March 9, 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.
@@ -286,9 +286,13 @@
     If the call to deflateInit2() is wrong a png_warning will be issued
     (in fact this is harmless, but the PNG data produced may be sub-optimal).
 
-Version 1.6.0beta17 [March 8, 2012]
+Version 1.6.0beta17 [March 9, 2012]
   Fixed PNG_LIBPNG_BUILD_BASE_TYPE definition. 
-  Reject iCCP chunk after the first, even if the first one is invalid.
+  Reject all iCCP chunks after the first, even if the first one is invalid.
+  Deflate/inflate was reworked to move common zlib calls into single
+    functions [rw]util.c.  A new shared keyword check routine was also added
+    and the 'zbuf' is no longer allocated on progressive read.  It is now
+    possible to call png_inflate() incrementally.
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net
 (subscription required; visit
diff --git a/CHANGES b/CHANGES
index 2e118e5..080a0d7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4037,9 +4037,13 @@
     If the call to deflateInit2() is wrong a png_warning will be issued
     (in fact this is harmless, but the PNG data produced may be sub-optimal).
 
-Version 1.6.0beta17 [March 8, 2012]
+Version 1.6.0beta17 [March 9, 2012]
   Fixed PNG_LIBPNG_BUILD_BASE_TYPE definition. 
-  Reject iCCP chunk after the first, even if the first one is invalid.
+  Reject all iCCP chunks after the first, even if the first one is invalid.
+  Deflate/inflate was reworked to move common zlib calls into single
+    functions [rw]util.c.  A new shared keyword check routine was also added
+    and the 'zbuf' is no longer allocated on progressive read.  It is now
+    possible to call png_inflate() incrementally.
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net
 (subscription required; visit
diff --git a/png.c b/png.c
index 3f1b7f7..cf8c29c 100644
--- a/png.c
+++ b/png.c
@@ -283,41 +283,29 @@
           */
          if (png_user_version_check(&create_struct, user_png_ver))
          {
-            /* TODO: delay initializing the zlib structure until it really is
-             * needed.
-             */
-            /* Initialize zbuf - compression/decompression buffer */
-            create_struct.zbuf_size = PNG_ZBUF_SIZE;
-            create_struct.zbuf = png_voidcast(png_bytep,
-               png_malloc_warn(&create_struct, create_struct.zbuf_size));
+            png_structrp png_ptr = png_voidcast(png_structrp,
+               png_malloc_warn(&create_struct, sizeof *png_ptr));
 
-            /* Finally allocate the png_struct itself. */
-            if (create_struct.zbuf != NULL)
+            if (png_ptr != NULL)
             {
-               png_structrp png_ptr = png_voidcast(png_structrp,
-                  png_malloc_warn(&create_struct, sizeof *png_ptr));
+               /* png_ptr->zstream holds a back-pointer to the png_struct, so
+                * this can only be done now:
+                */
+               create_struct.zstream.zalloc = png_zalloc;
+               create_struct.zstream.zfree = png_zfree;
+               create_struct.zstream.opaque = png_ptr;
 
-               if (png_ptr != NULL)
-               {
-#                 ifdef PNG_SETJMP_SUPPORTED
-                     /* Eliminate the local error handling: */
-                     create_struct.jmp_buf_ptr = NULL;
-                     create_struct.jmp_buf_size = 0;
-                     create_struct.longjmp_fn = 0;
-#                 endif
+#              ifdef PNG_SETJMP_SUPPORTED
+                  /* Eliminate the local error handling: */
+                  create_struct.jmp_buf_ptr = NULL;
+                  create_struct.jmp_buf_size = 0;
+                  create_struct.longjmp_fn = 0;
+#              endif
 
-                  *png_ptr = create_struct;
+               *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;
-               }
+               /* This is the successful return point */
+               return png_ptr;
             }
          }
       }
@@ -325,15 +313,6 @@
    /* A longjmp because of a bug in the application storage allocator or a
     * simple failure to allocate the png_struct.
     */
-   if (create_struct.zbuf != NULL)
-   {
-      png_bytep zbuf = create_struct.zbuf;
-
-      /* Ensure we don't keep on returning to this point: */
-      create_struct.zbuf = NULL;
-      png_free(&create_struct, zbuf);
-   }
-
    return NULL;
 }
 
@@ -768,13 +747,13 @@
 #else
 #  ifdef __STDC__
    return PNG_STRING_NEWLINE \
-     "libpng version 1.6.0beta17 - March 6, 2012" PNG_STRING_NEWLINE \
+     "libpng version 1.6.0beta17 - March 9, 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.0beta17 - March 6, 2012\
+      return "libpng version 1.6.0beta17 - March 9, 2012\
       Copyright (c) 1998-2012 Glenn Randers-Pehrson\
       Copyright (c) 1996-1997 Andreas Dilger\
       Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
@@ -867,6 +846,7 @@
    if (png_ptr == NULL)
       return Z_STREAM_ERROR;
 
+   /* WARNING: this resets the window bits to the maximum! */
    return (inflateReset(&png_ptr->zstream));
 }
 #endif /* PNG_READ_SUPPORTED */
@@ -882,6 +862,76 @@
 
 
 #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Ensure that png_ptr->zstream.msg holds some appropriate error message string.
+ * If it doesn't 'ret' is used to set it to something appropriate, even in cases
+ * like Z_OK or Z_STREAM_END where the error code is apparently a success code.
+ */
+void /* PRIVATE */
+png_zstream_error(png_structrp png_ptr, int ret)
+{
+   /* Translate 'ret' into an appropriate error string, priority is given to the
+    * one in zstream if set.  This always returns a string, even in cases like
+    * Z_OK or Z_STREAM_END where the error code is a success code.
+    */
+   if (png_ptr->zstream.msg == NULL) switch (ret)
+   {
+      default:
+      case Z_OK:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");
+         break;
+
+      case Z_STREAM_END:
+         /* Normal exit */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
+         break;
+
+      case Z_NEED_DICT:
+         /* This means the deflate stream did not have a dictionary; this
+          * indicates a bogus PNG.
+          */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");
+         break;
+
+      case Z_ERRNO:
+         /* gz APIs only: should not happen */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");
+         break;
+
+      case Z_STREAM_ERROR:
+         /* internal libpng error */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");
+         break;
+
+      case Z_DATA_ERROR:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");
+         break;
+
+      case Z_MEM_ERROR:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");
+         break;
+
+      case Z_BUF_ERROR:
+         /* End of input or output; not a problem if the caller is doing
+          * incremental read or write.
+          */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");
+         break;
+
+      case Z_VERSION_ERROR:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");
+         break;
+
+      case PNG_UNEXPECTED_ZLIB_RETURN:
+         /* Compile errors here mean that zlib now uses the value co-opted in
+          * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above
+          * and change pngpriv.h.  Note that this message is "... return",
+          * whereas the default/Z_OK one is "... return code".
+          */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");
+         break;
+   }
+}
+
 /* png_convert_size: a PNGAPI but no longer in png.h, so deleted
  * at libpng 1.5.5!
  */
diff --git a/png.h b/png.h
index 748582a..c75b3b6 100644
--- a/png.h
+++ b/png.h
@@ -1,7 +1,7 @@
 
 /* png.h - header file for PNG reference library
  *
- * libpng version 1.6.0beta17 - March 6, 2012
+ * libpng version 1.6.0beta17 - March 9, 2012
  * Copyright (c) 1998-2012 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.0beta17 - March 6, 2012: Glenn
+ *   libpng versions 0.97, January 1998, through 1.6.0beta17 - March 9, 2012: Glenn
  *   See also "Contributing Authors", below.
  *
  * Note about libpng version numbers:
@@ -198,7 +198,7 @@
  *
  * This code is released under the libpng license.
  *
- * libpng versions 1.2.6, August 15, 2004, through 1.6.0beta17, March 6, 2012, are
+ * libpng versions 1.2.6, August 15, 2004, through 1.6.0beta17, March 9, 2012, are
  * Copyright (c) 2004, 2006-2012 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:
@@ -310,7 +310,7 @@
  * Y2K compliance in libpng:
  * =========================
  *
- *    March 6, 2012
+ *    March 9, 2012
  *
  *    Since the PNG Development group is an ad-hoc body, we can't make
  *    an official declaration.
@@ -376,7 +376,7 @@
 /* Version information for png.h - this should match the version in png.c */
 #define PNG_LIBPNG_VER_STRING "1.6.0beta17"
 #define PNG_HEADER_VERSION_STRING \
-     " libpng version 1.6.0beta17 - March 6, 2012\n"
+     " libpng version 1.6.0beta17 - March 9, 2012\n"
 
 #define PNG_LIBPNG_VER_SONUM   16
 #define PNG_LIBPNG_VER_DLLNUM  16
@@ -1047,7 +1047,7 @@
 
 #ifdef PNG_READ_SUPPORTED
 /* Reset the compression stream */
-PNG_EXPORT(10, int, png_reset_zstream, (png_structrp png_ptr));
+PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED);
 #endif
 
 /* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
diff --git a/pngget.c b/pngget.c
index e2c9175..0fedb44 100644
--- a/pngget.c
+++ b/pngget.c
@@ -1070,7 +1070,24 @@
 png_size_t PNGAPI
 png_get_compression_buffer_size(png_const_structrp png_ptr)
 {
-   return (png_ptr ? png_ptr->zbuf_size : 0);
+   if (png_ptr == NULL)
+      return 0;
+
+#  ifdef PNG_WRITE_SUPPORTED
+      if (png_ptr->mode & PNG_IS_READ_STRUCT)
+#  endif
+   {
+#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+         return png_ptr->IDAT_read_size;
+#     else
+         return PNG_IDAT_READ_SIZE;
+#     endif
+   }
+
+#  ifdef PNG_WRITE_SUPPORTED
+      else
+         return png_ptr->zbuffer_size;
+#  endif
 }
 
 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
diff --git a/pngpread.c b/pngpread.c
index c7de794..aed706f 100644
--- a/pngpread.c
+++ b/pngpread.c
@@ -838,7 +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;
+      png_ptr->zowner = 0;
    }
 }
 
@@ -894,7 +894,7 @@
       {
          /* Terminate the decompression. */
          png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-         png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
+         png_ptr->zowner = 0;
 
          /* This may be a truncated stream (missing or
           * damaged end code).  Treat that as a warning.
@@ -923,7 +923,7 @@
             /* Extra data. */
             png_warning(png_ptr, "Extra compressed data in IDAT");
             png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-            png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
+            png_ptr->zowner = 0;
 
             /* Do no more processing; skip the unprocessed
              * input check below.
diff --git a/pngpriv.h b/pngpriv.h
index af5dae0..313f0fe 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -467,6 +467,7 @@
 #define PNG_HAVE_PNG_SIGNATURE    0x1000
 #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
 #define PNG_HAVE_iCCP             0x4000
+#define PNG_IS_READ_STRUCT        0x8000 /* Else is a write struct */
 
 /* Flags for the transformations the PNG library does on the image data */
 #define PNG_BGR                 0x0001
@@ -512,9 +513,9 @@
 /* Flags for the png_ptr->flags rather than declaring a byte for each one */
 #define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001
 #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 */
+                                  /*      0x0004    unused */
 #define PNG_FLAG_ZSTREAM_ENDED            0x0008 /* Added to libpng-1.6.0 */
-#define PNG_FLAG_ZSTREAM_CMF_FIXUP        0x0010 /* Added to libpng-1.6.0 */
+                                  /*      0x0010    unused */
                                   /*      0x0020    unused */
 #define PNG_FLAG_ROW_INIT                 0x0040
 #define PNG_FLAG_FILLER_AFTER             0x0080
@@ -551,18 +552,6 @@
 #define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \
                                      PNG_FLAG_CRC_CRITICAL_MASK)
 
-/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
- * can handle at once.  This type need be no larger than 16 bits (so maximum of
- * 65535), this define allows us to discover how big it is, but limited by the
- * maximuum for png_size_t.  The value can be overriden in a library build
- * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
- * lower value (e.g. 255 works).  A lower value may help memory usage (slightly)
- * and may even improve performance on some systems (and degrade it on others.)
- */
-#ifndef ZLIB_IO_MAX
-#  define ZLIB_IO_MAX ((uInt)-1)
-#endif
-
 /* Save typing and make code easier to understand */
 
 #define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
@@ -736,7 +725,18 @@
  */
 
 /* Zlib support */
-PNG_INTERNAL_FUNCTION(void,png_inflate_claim,(png_structrp png_ptr),PNG_EMPTY);
+#define PNG_UNEXPECTED_ZLIB_RETURN (-7)
+PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
+   PNG_EMPTY);
+   /* Used by the zlib handling functions to ensure that z_stream::msg is always
+    * set before they return.
+    */
+
+#ifdef PNG_WRITE_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
+   png_compression_bufferp *list),PNG_EMPTY);
+   /* Free the buffer list used by the compressed write code. */
+#endif
 
 #if defined PNG_FLOATING_POINT_SUPPORTED &&\
    !defined PNG_FIXED_POINT_MACRO_SUPPORTED
@@ -851,15 +851,16 @@
 /* Write the IHDR chunk, and update the png_struct with the necessary
  * information.
  */
-PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, png_uint_32 width,
-   png_uint_32 height, int bit_depth, int color_type, int compression_method,
-   int filter_method, int interlace_method),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
+   png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
+   int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
 
 PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
    png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
 
-PNG_INTERNAL_FUNCTION(void,png_write_IDAT,(png_structrp png_ptr, png_bytep data,
-   png_size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
+   png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
+   PNG_EMPTY);
 
 PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
 
@@ -921,15 +922,9 @@
 #endif
 
 /* Chunks that have keywords */
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
-    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(png_size_t,png_check_keyword,(png_structrp png_ptr,
-    png_const_charp key, png_charpp new_key),PNG_EMPTY);
-#endif
-
 #ifdef PNG_WRITE_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, png_const_charp key,
-    png_const_charp text, png_size_t text_len),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
+   png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_zTXt_SUPPORTED
@@ -1054,8 +1049,24 @@
 PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
     png_row_infop row_info),PNG_EMPTY);
 
-/* Finish a row while reading, dealing with interlacing passes, etc. */
-PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),PNG_EMPTY);
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
+   png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
+   /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer
+    * is NULL the function checks, instead, for the end of the stream.  In this
+    * case a benign error will be issued if the stream end is not found or if
+    * extra data has to be consumed.
+    */
+PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
+   PNG_EMPTY);
+   /* This cleans up when the IDAT LZ stream does not end when the last image
+    * byte is read; there is still some pending input.
+    */
+
+PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
+   PNG_EMPTY);
+   /* Finish a row while reading, dealing with interlacing passes, etc. */
+#endif
 
 /* Initialize the row buffers, etc. */
 PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
diff --git a/pngread.c b/pngread.c
index 59721fe..68304b8 100644
--- a/pngread.c
+++ b/pngread.c
@@ -48,6 +48,14 @@
 
    if (png_ptr != NULL)
    {
+      png_ptr->mode = PNG_IS_READ_STRUCT;
+
+      /* Adding in libpng-1.6.0; this can be used to detect a read structure if
+       * required (it will be zero in a write structure.)
+       */
+#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+         png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE;
+#     endif
       /* 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.
@@ -268,8 +276,6 @@
 void PNGAPI
 png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
 {
-   int ret;
-
    png_row_info row_info;
 
    if (png_ptr == NULL)
@@ -423,56 +429,8 @@
    if (!(png_ptr->mode & PNG_HAVE_IDAT))
       png_error(png_ptr, "Invalid attempt to read row data");
 
-   png_ptr->zstream.next_out = png_ptr->row_buf;
-   /* 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 == 0)
-         {
-            png_crc_finish(png_ptr, 0);
-
-            png_ptr->idat_size = png_read_chunk_header(png_ptr);
-            if (png_ptr->chunk_name != png_IDAT)
-               png_error(png_ptr, "Not enough image data");
-         }
-         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_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
-         png_ptr->idat_size -= png_ptr->zstream.avail_in;
-      }
-
-      /* 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)
-      {
-         if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in ||
-            png_ptr->idat_size)
-            png_benign_error(png_ptr, "Extra compressed data");
-         png_ptr->mode |= PNG_AFTER_IDAT;
-         /* Release the stream */
-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-         png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-         break;
-      }
-
-      if (ret != Z_OK)
-         png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
-             "Decompression error");
-
-   } while (png_ptr->zstream.avail_out);
+   /* Fill the row with IDAT data: */
+   png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
 
    if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
    {
@@ -702,7 +660,10 @@
    if (png_ptr == NULL)
       return;
 
-   png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */
+   /* If png_read_end is called in the middle of reading the rows there may
+    * still be pending IDAT data and an owned zstream.  Deal with this here.
+    */
+   png_read_finish_IDAT(png_ptr);
 
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
    /* Report invalid palette index; added at libng-1.5.10 */
@@ -851,10 +812,9 @@
    png_destroy_gamma_table(png_ptr);
 #endif
 
-   png_free(png_ptr, png_ptr->zbuf);
    png_free(png_ptr, png_ptr->big_row_buf);
    png_free(png_ptr, png_ptr->big_prev_row);
-   png_free(png_ptr, png_ptr->chunkdata);
+   png_free(png_ptr, png_ptr->read_buffer);
 
 #ifdef PNG_READ_QUANTIZE_SUPPORTED
    png_free(png_ptr, png_ptr->palette_lookup);
diff --git a/pngrutil.c b/pngrutil.c
index fd89c89..2befd4a 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -203,17 +203,20 @@
 int /* PRIVATE */
 png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
 {
-   png_uint_32 i;
-   uInt istop = png_ptr->zbuf_size;
-
-   for (i = skip; i > istop; i -= istop)
+   /* The size of the local buffer for inflate is a good guess as to a
+    * reasonable size to use for buffering reads from the application.
+    */
+   while (skip > 0)
    {
-      png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
-   }
+      png_uint_32 len;
+      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
 
-   if (i)
-   {
-      png_crc_read(png_ptr, png_ptr->zbuf, i);
+      len = sizeof tmpbuf;
+      if (len > skip)
+         len = skip;
+      skip -= len;
+
+      png_crc_read(png_ptr, tmpbuf, len);
    }
 
    if (png_crc_error(png_ptr))
@@ -277,12 +280,72 @@
       return (0);
 }
 
-/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
- * decompression.
+/* Manage the read buffer; this simply reallocates the buffer if it is not small
+ * enough (or if it is not allocated).  The routine returns a pointer to the
+ * buffer, if an error occurs and 'warn' is set the routine returns NULL, else
+ * it will call png_error (via png_malloc) on failure.  (warn == 2 means
+ * 'silent').
  */
-void /* PRIVATE */
-png_inflate_claim(png_structrp png_ptr)
+static png_bytep
+png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
 {
+   png_bytep buffer = png_ptr->read_buffer;
+
+   if (buffer != NULL && new_size > png_ptr->read_buffer_size)
+   {
+      png_ptr->read_buffer = NULL;
+      png_ptr->read_buffer = NULL;
+      png_ptr->read_buffer_size = 0;
+      png_free(png_ptr, buffer);
+      buffer = NULL;
+   }
+
+   if (buffer == NULL)
+   {
+      buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));
+
+      if (buffer != NULL)
+      {
+         png_ptr->read_buffer = buffer;
+         png_ptr->read_buffer_size = new_size;
+      }
+
+      else if (warn < 2) /* else silent */
+      {
+         (warn ? png_chunk_warning : png_chunk_error)(png_ptr,
+            "insufficient memory to read chunk");
+      }
+   }
+
+   return buffer;
+}
+
+/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
+ * decompression.  Returns Z_OK on success, else a zlib error code.  It checks
+ * the owner but, in final release builds, just issues a warning if some other
+ * 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)
+{
+   if (png_ptr->zowner != 0)
+   {
+      char msg[64];
+
+      PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner);
+      /* So the message that results is "<chunk> using zstream"; this is an
+       * internal error, but is very useful for debugging.  i18n requirements
+       * are minimal.
+       */
+      (void)png_safecat(msg, sizeof msg, 4, " using zstream");
+#     if PNG_LIBPNG_BUILD_BASE_TYPE == PNG_LIBPNG_BUILD_STABLE
+         png_chunk_warning(png_ptr, msg);
+         png_ptr->zowner = 0;
+#     else
+         png_chunk_error(png_ptr, msg);
+#     endif
+   }
+
    /* 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
@@ -291,227 +354,193 @@
     * 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"!)
+    *
+    * inflateReset2 was added to zlib 1.2.4; before this the window could not be
+    * reset, therefore it is necessary to always allocate the maximum window
+    * size with earlier zlibs just in case later compressed chunks need it.
     */
-   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;
+      /* Set this for safety, just in case the previous owner left pointers to
+       * memory allocations.
+       */
+      png_ptr->zstream.next_in = NULL;
+      png_ptr->zstream.avail_in = 0;
+      png_ptr->zstream.next_out = NULL;
+      png_ptr->zstream.avail_out = 0;
 
       if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
-         ret = inflateReset(&png_ptr->zstream);
+      {
+#        if ZLIB_VERNUM < 0x1240
+            ret = inflateReset(&png_ptr->zstream);
+#        else
+            ret = inflateReset2(&png_ptr->zstream, 0/*use stream windowBits*/);
+#        endif
+      }
 
       else
       {
-         ret = inflateInit(&png_ptr->zstream);
+#        if ZLIB_VERNUM < 0x1240
+            ret = inflateInit(&png_ptr->zstream);
+#        else
+            ret = inflateInit2(&png_ptr->zstream, 0/*use stream windowBits*/);
+#        endif
 
          if (ret == Z_OK)
             png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
       }
 
       if (ret == Z_OK)
-         png_ptr->flags |= PNG_FLAG_ZSTREAM_IN_USE;
+         png_ptr->zowner = owner;
 
       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);
+         png_zstream_error(png_ptr, ret);
 
-         else
-            png_error(png_ptr, "zlib initialization error");
-      }
+      return ret;
+   }
+}
+
+/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to
+ * allow the caller to do multiple calls if required.  If the 'finish' flag is
+ * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must
+ * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and
+ * Z_OK or Z_STREAM_END will be returned on success.
+ *
+ * The input and output sizes are updated to the actual amounts of data consumed
+ * or written, not the amount available (as in a z_stream).  The data pointers
+ * are not changed, so the next input is (data+input_size) and the next
+ * available output is (output+output_size).
+ */
+static int
+png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish,
+    /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr,
+    /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr)
+{
+   if (png_ptr->zowner == owner) /* Else not claimed */
+   {
+      int ret;
+      png_alloc_size_t avail_out = *output_size_ptr;
+      png_uint_32 avail_in = *input_size_ptr;
+
+      /* 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 code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the
+       * maximum value that can be stored in a uInt.)  It is possible to set
+       * ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have
+       * a performance advantage, because it reduces the amount of data accessed
+       * at each step and that may give the OS more time to page it in.
+       */
+      png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
+      /* avail_in and avail_out are set below from 'size' */
+      png_ptr->zstream.avail_in = 0;
+      png_ptr->zstream.avail_out = 0;
+
+      /* Read directly into the output if it is available (this is set to
+       * a local buffer below if output is NULL).
+       */
+      if (output != NULL)
+         png_ptr->zstream.next_out = output;
+
+      do
+      {
+         uInt avail;
+         Byte local_buffer[PNG_INFLATE_BUF_SIZE];
+
+         /* zlib INPUT BUFFER */
+         /* The setting of 'avail_in' used to be outside the loop; by setting it
+          * inside it is possible to chunk the input to zlib and simply rely on
+          * zlib to advance the 'next_in' pointer.  This allows arbitrary
+          * amounts of data to be passed through zlib at the unavoidable cost of
+          * requiring a window save (png_memcpy of up to 32768 output bytes)
+          * every ZLIB_IO_MAX input bytes.
+          */
+         avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
+
+         avail = ZLIB_IO_MAX;
+
+         if (avail_in < avail)
+            avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
+
+         avail_in -= avail;
+         png_ptr->zstream.avail_in = avail;
+
+         /* zlib OUTPUT BUFFER */
+         avail_out += png_ptr->zstream.avail_out; /* not written last time */
+
+         avail = ZLIB_IO_MAX; /* maximum zlib can process */
+
+         if (output == NULL)
+         {
+            /* Reset the output buffer each time round if output is NULL and
+             * make available the full buffer, up to 'remaining_space'
+             */
+            png_ptr->zstream.next_out = local_buffer;
+            if (sizeof local_buffer < avail)
+               avail = sizeof local_buffer;
+         }
+
+         if (avail_out < avail)
+            avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
+
+         png_ptr->zstream.avail_out = avail;
+         avail_out -= avail;
+
+         /* zlib inflate call */
+         /* In fact 'avail_out' may be 0 at this point, that happens at the end
+          * of the read when the final LZ end code was not passed at the end of
+          * the previous chunk of input data.  Tell zlib if we have reached the
+          * end of the output buffer.
+          */
+         ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH :
+            (finish ? Z_FINISH : Z_SYNC_FLUSH));
+      } while (ret == Z_OK);
+
+      /* For safety kill the local buffer pointer now */
+      if (output == NULL)
+         png_ptr->zstream.next_out = NULL;
+
+      /* Claw back the 'size' and 'remaining_space' byte counts. */
+      avail_in += png_ptr->zstream.avail_in;
+      avail_out += png_ptr->zstream.avail_out;
+
+      /* Update the input and output sizes; the updated values are the amount
+       * consumed or written, effectively the inverse of what zlib uses.
+       */
+      if (avail_out > 0)
+         *output_size_ptr -= avail_out;
+
+      if (avail_in > 0)
+         *input_size_ptr -= avail_in;
+
+      /* Ensure png_ptr->zstream.msg is set (even in the success case!) */
+      png_zstream_error(png_ptr, ret);
+      return ret;
    }
 
    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
- * adjusted to the actual amount of uncompressed data.   If
- * PNG_INFLATE_TRUNCATED is returned partial output will have been produced; no
- * error or warning is produced in this case.
- */
-#define PNG_INFLATE_OK        1
-#define PNG_INFLATE_TRUNCATED 2 /* output_size will be unchanged */
-#define PNG_INFLATE_ERROR     3 /* a chunk warning has been output */
-
-static void
-stash_error(png_structrp png_ptr, png_const_charp message)
-{
-   size_t len = strlen(message);
-
-   if (len >= png_ptr->zbuf_size)
-      len = png_ptr->zbuf_size-1;
-
-   memcpy(png_ptr->zbuf, message, len);
-   png_ptr->zbuf[len] = 0;
-}
-
-static int
-png_inflate(png_structrp png_ptr, png_bytep data, png_uint_32 input_size,
-    png_bytep output, png_alloc_size_t *output_size)
-{
-   int ret;
-   png_alloc_size_t avail_out = *output_size;
-   png_uint_32 avail_in = input_size;
-
-   /* 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
-    * code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the maximum value
-    * that can be stored in a uInt.)  It is possible to set ZLIB_IO_MAX to a
-    * lower value in pngpriv.h and this may sometimes have a performance
-    * advantage, because it forces access of the input data to be separated from
-    * at least some of the use by some period of time.
-    */
-   png_ptr->zstream.next_in = data;
-   /* avail_in and avail_out are set below from 'size' */
-   png_ptr->zstream.avail_in = 0;
-   png_ptr->zstream.avail_out = 0;
-
-   /* Read directly into the output if it is available (this is set to
-    * png_ptr->zbuf below if output is NULL).
-    */
-   if (output != NULL)
-      png_ptr->zstream.next_out = output;
-
-   do
    {
-      uInt avail;
-
-      /* zlib INPUT BUFFER */
-      /* The setting of 'avail_in' used to be outside the loop; by setting it
-       * inside it is possible to chunk the input to zlib and simply rely on
-       * zlib to advance the 'next_in' pointer.  This allows arbitrary amounts o
-       * data to be passed through zlib at the unavoidable cost of requiring a
-       * window save (png_memcpy of up to 32768 output bytes) every ZLIB_IO_MAX
-       * input bytes.
+      /* This is a bad internal error.  The recovery assigns to the zstream msg
+       * pointer, which is not owned by the caller, but this is safe; it's only
+       * used on errors!
        */
-      avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
-
-      avail = ZLIB_IO_MAX;
-
-      if (avail_in < avail)
-         avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
-
-      avail_in -= avail;
-      png_ptr->zstream.avail_in = avail;
-
-      /* zlib OUTPUT BUFFER */
-      avail_out += png_ptr->zstream.avail_out; /* not written last time */
-
-      avail = ZLIB_IO_MAX; /* maximum zlib can process */
-
-      if (output == NULL)
-      {
-         /* Reset the output buffer each time round if output is NULL and make
-          * available the full buffer, up to 'remaining_space'
-          */
-         png_ptr->zstream.next_out = png_ptr->zbuf;
-         if (png_ptr->zbuf_size < avail)
-            avail = png_ptr->zbuf_size;
-      }
-
-      if (avail_out < avail)
-         avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
-
-      png_ptr->zstream.avail_out = avail;
-      avail_out -= avail;
-
-      /* zlib inflate call */
-      /* In fact 'avail_out' may be 0 at this point, that happens at the end of
-       * the read when the final LZ end code was not passed at the end of the
-       * previous chunk of input data.  Tell zlib if we have reached the end of
-       * the output buffer.
-       *
-       * TODO: this prevents multiple calls to inflate, but may help inflate
-       * avoid overhead.  Make using Z_FINISH an option for the caller.
-       */
-      ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH : Z_FINISH);
-   } while (ret == Z_OK);
-
-   /* Claw back the 'size' and 'remaining_space' byte counts. */
-   avail_in += png_ptr->zstream.avail_in;
-   avail_out += png_ptr->zstream.avail_out;
-
-   /* Always reset the zstream, it must be left in inflateInit state.
-    *
-    * TODO: don't do this on Z_BUF_ERROR and no remaining space, then the
-    * caller can use png_inflate incrementally.  Also change to using the
-    * same approach as in the write code - init and reset only on demand.
-    */
-   png_ptr->zstream.next_in = NULL;
-   png_ptr->zstream.avail_in = 0;
-   png_ptr->zstream.next_out = NULL;
-   png_ptr->zstream.avail_out = 0;
-   inflateReset(&png_ptr->zstream);
-
-   /* Update the output size if the output was not filled.
-    *
-    * TODO: do this with 'size' as well, not currently a parameter.
-    */
-   if (avail_out > 0)
-      *output_size -= avail_out;
-
-   /* Error messages are stashed in png_ptr->zbuf, to avoid any errors in the
-    * errors do this first:
-    */
-   png_ptr->zbuf[0] = 0;
-
-   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 */
-         if (avail_out > 0) /* input truncated */
-         {
-            stash_error(png_ptr, "LZ data truncated");
-            return PNG_INFLATE_ERROR;
-         }
-
-         else if (avail_in > 0) /* output truncated */
-         {
-            /* TODO: currently restarting a partial read isn't supporte because
-             * of the inflateReset above, change this to do the Reset/Init on
-             * demand.
-             */
-            stash_error(png_ptr, "uncompressed data too large");
-            return PNG_INFLATE_TRUNCATED;
-         }
-
-      default:
-         /* Everything else is an error, expect 'Z_DATA_ERROR' and
-          * zstream.msg to be non-NULL.  In any case just copy something
-          * into zbuf and return the error code.
-          */
-         if (png_ptr->zstream.msg != NULL)
-            stash_error(png_ptr, png_ptr->zstream.msg);
-
-         else
-            stash_error(png_ptr, "LZ data damaged");
-
-         return PNG_INFLATE_ERROR;
+      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
+      return Z_STREAM_ERROR;
    }
 }
 
+#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
 /*
- * Decompress trailing data in a chunk.  The assumption is that chunkdata
+ * Decompress trailing data in a chunk.  The assumption is that read_buffer
  * points at an allocated area holding the contents of a chunk with a
  * trailing compressed part.  What we get back is an allocated area
  * holding the original prefix part and an uncompressed version of the
  * trailing part (the malloc area passed in is freed).
  */
-static png_const_charp
-png_decompress_chunk(png_structrp png_ptr, png_uint_32 chunklength,
-   png_uint_32 prefix_size,
+static int
+png_decompress_chunk(png_structrp png_ptr,
+   png_uint_32 chunklength, png_uint_32 prefix_size,
    png_alloc_size_t *newlength /* must be initialized to the maximum! */,
    int terminate /*add a '\0' to the end of the uncompressed data*/)
 {
@@ -524,8 +553,6 @@
     */
    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)
@@ -537,17 +564,31 @@
 
    if (limit >= prefix_size + (terminate != 0))
    {
+      int ret;
+      
       limit -= prefix_size + (terminate != 0);
 
       if (limit < *newlength)
          *newlength = limit;
 
-      switch (png_inflate(png_ptr,
-         (png_bytep)png_ptr->chunkdata + prefix_size,
-         chunklength - prefix_size, NULL /* output */, newlength))
+      /* Now try to claim the stream */
+      ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);
+
+      if (ret == Z_OK)
       {
-         case PNG_INFLATE_OK:
-            /* Success (maybe) - really uncompress the chunk. */
+         png_uint_32 lzsize = chunklength - prefix_size;
+
+         ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
+            /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
+            /* output: */ NULL, newlength);
+          
+         if (ret == Z_STREAM_END)
+         {
+#if 1
+            if (inflateReset(&png_ptr->zstream) == Z_OK)
+#else
+            if (inflateReset2(&png_ptr->zstream, 0/*from stream*/) == Z_OK)
+#endif
             {
                /* Because of the limit checks above we know that the new,
                 * expanded, size will fit in a size_t (let alone an
@@ -555,65 +596,101 @@
                 * extra OOM message.
                 */
                png_alloc_size_t new_size = *newlength;
-               png_bytep text = png_voidcast(png_bytep, png_malloc_base(
-                  png_ptr, prefix_size + new_size + (terminate != 0)));
+               png_alloc_size_t buffer_size = prefix_size + new_size +
+                  (terminate != 0);
+               png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr,
+                  buffer_size));
 
-               if (text == NULL)
-                  break; /* out of memory */
-
-               /* This should now work, although it is possible a logic
-                * error might result.
-                */
-               switch (png_inflate(png_ptr, (png_bytep)png_ptr->chunkdata +
-                  prefix_size, chunklength - prefix_size, text + prefix_size,
-                  newlength))
+               if (text != NULL)
                {
-                  case PNG_INFLATE_OK:
+                  ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
+                     png_ptr->read_buffer + prefix_size, &lzsize,
+                     text + prefix_size, newlength);
+
+                  if (ret == Z_STREAM_END)
+                  {
                      if (new_size == *newlength)
                      {
                         if (terminate)
                            text[prefix_size + *newlength] = 0;
 
                         if (prefix_size > 0)
-                           png_memcpy(text, png_ptr->chunkdata, prefix_size);
+                           png_memcpy(text, png_ptr->read_buffer, prefix_size);
 
-                        png_free(png_ptr, png_ptr->chunkdata);
-                        png_ptr->chunkdata = (png_charp)text;
-                        png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-                        return NULL;
+                        {
+                           png_bytep old_ptr = png_ptr->read_buffer;
+
+                           png_ptr->read_buffer = text;
+                           png_ptr->read_buffer_size = buffer_size;
+                           text = old_ptr; /* freed below */
+                        }
                      }
 
-                     /* This is an unexpected internal error. */
-                     /* FALL THROUGH */
+                     else
+                     {
+                        /* The size changed on the second read, there can be no
+                         * guarantee that anything is correct at this point.
+                         * The 'msg' pointer has been set to "unexpected end of
+                         * LZ stream", which is fine, but return an error code
+                         * that the caller won't accept.
+                         */
+                        ret = PNG_UNEXPECTED_ZLIB_RETURN;
+                     }
+                  }
 
-                  case PNG_INFLATE_TRUNCATED:
-                     png_free(png_ptr, text);
-                     png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-                     return "libpng inflate error";
+                  else if (ret == Z_OK)
+                     ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */
 
-                  default: /* PNG_INFLATE_ERROR */
-                     png_free(png_ptr, text);
-                     png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-                     return (png_const_charp)png_ptr->zbuf;
+                  /* Free the text pointer (this is the old read_buffer on
+                   * success)
+                   */
+                  png_free(png_ptr, text);
+
+                  /* This really is very benign, but it's still an error because
+                   * the extra space may otherwise be used as a Trojan Horse.
+                   */
+                  if (ret == Z_STREAM_END &&
+                     chunklength - prefix_size != lzsize)
+                     png_chunk_benign_error(png_ptr, "extra compressed data");
+               }
+
+               else
+               {
+                  /* Out of memory allocating the buffer */
+                  ret = Z_MEM_ERROR;
+                  png_zstream_error(png_ptr, Z_MEM_ERROR);
                }
             }
 
-         case PNG_INFLATE_TRUNCATED:
-            /* This means that there wasn't enough space to uncompress the
-             * chunk.
-             */
-            break;
+            else
+            {
+               /* inflateReset failed, store the error message */
+               png_zstream_error(png_ptr, ret);
 
-         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;
+               if (ret == Z_STREAM_END)
+                  ret = PNG_UNEXPECTED_ZLIB_RETURN;
+            }
+         }
+
+         else if (ret == Z_OK)
+            ret = PNG_UNEXPECTED_ZLIB_RETURN;
+
+         /* Release the claimed stream */
+         png_ptr->zowner = 0;
       }
+
+      else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */
+         ret = PNG_UNEXPECTED_ZLIB_RETURN;
+
+      return ret;
    }
 
-   /* out of memory returns. */
-   png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-   return "insufficient memory";
+   else
+   {
+      /* Application/configuration limits exceeded */
+      png_zstream_error(png_ptr, Z_MEM_ERROR);
+      return Z_MEM_ERROR;
+   }
 }
 #endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */
 
@@ -1336,6 +1413,7 @@
 {
    png_uint_32 keyword_length;
    png_const_charp error_message = NULL;
+   png_bytep buffer;
 
    png_debug(1, "in png_handle_iCCP");
 
@@ -1369,28 +1447,22 @@
 
    png_ptr->mode |= PNG_HAVE_iCCP;
 
-   png_free(png_ptr, png_ptr->chunkdata);
    /* TODO: read the chunk in pieces, validating it as we go. */
-   png_ptr->chunkdata = png_voidcast(png_charp, png_malloc(png_ptr, length));
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+   buffer = png_read_buffer(png_ptr, length, 0/*!warn*/);
+
+   png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
    /* TODO: also check that the keyword contents match the spec! */
    for (keyword_length = 0;
-      keyword_length < length && png_ptr->chunkdata[keyword_length] != 0;
+      keyword_length < length && buffer[keyword_length] != 0;
       ++keyword_length)
       /* Empty loop to find end of name */ ;
 
    if (keyword_length > 79 || keyword_length < 1)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       png_chunk_benign_error(png_ptr, "Bad iCCP keyword");
       return;
    }
@@ -1401,8 +1473,6 @@
     */
    if (keyword_length + 3 >= length)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       png_chunk_benign_error(png_ptr, "iCCP chunk too short");
       return;
    }
@@ -1410,10 +1480,8 @@
    /* We only understand '0' compression - deflate - so if we get a different
     * value we can't safely decode the chunk.
     */
-   if (png_ptr->chunkdata[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
+   if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       png_chunk_benign_error(png_ptr, "Bad compression type in iCCP chunk");
       return;
    }
@@ -1436,17 +1504,15 @@
        * level memory limit, this should be split to different values for iCCP
        * and text chunks.
        */
-      error_message = png_decompress_chunk(png_ptr, length, keyword_length+2,
-         &uncompressed_length, 0/*do not terminate*/);
-
-      if (error_message == NULL)
+      if (png_decompress_chunk(png_ptr, length, keyword_length+2,
+         &uncompressed_length, 0/*do not terminate*/) == Z_STREAM_END)
       {
-         /* It worked; png_ptr->chunkdata now contains the full uncompressed ICC
-          * profile.  The cast below is safe because of the checks above, the
-          * final uncompressed length can be at most 2^32-1
+         /* It worked; png_ptr->read_buffer now contains the full uncompressed
+          * ICC profile.  The cast below is safe because of the checks above,
+          * the final uncompressed length can be at most 2^32-1
           */
          png_uint_32 profile_length = (png_uint_32)uncompressed_length;
-         png_bytep profile = (png_bytep)png_ptr->chunkdata + (keyword_length+2);
+         png_bytep profile = png_ptr->read_buffer + (keyword_length+2);
 
          /* Now perform some basic checks.
           *
@@ -1454,16 +1520,16 @@
           * check that the whole tag table is there.
           */
          if (profile_length > 132 && png_get_uint_32(profile) == profile_length)
-            png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata,
-                PNG_COMPRESSION_TYPE_BASE, profile, profile_length);
+            png_set_iCCP(png_ptr, info_ptr, (png_charp)png_ptr->read_buffer,
+               PNG_COMPRESSION_TYPE_BASE, profile, profile_length);
 
          else
             error_message = "Malformed ICC profile";
       }
-   }
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
+      else
+         error_message = png_ptr->zstream.msg;
+   }
 
    if (error_message != NULL)
       png_chunk_benign_error(png_ptr, error_message);
@@ -1475,13 +1541,12 @@
 png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 /* Note: this does not properly handle chunks that are > 64K under DOS */
 {
-   png_bytep entry_start;
+   png_bytep entry_start, buffer;
    png_sPLT_t new_palette;
    png_sPLT_entryp pp;
    png_uint_32 data_length;
    int entry_size, i;
    png_uint_32 skip = 0;
-   png_size_t slength;
    png_uint_32 dl;
    png_size_t max_dl;
 
@@ -1525,36 +1590,27 @@
    }
 #endif
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+   buffer = png_read_buffer(png_ptr, length+1, 0/*!warn*/);
 
    /* WARNING: this may break if size_t is less than 32 bits; it is assumed
     * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a
     * potential breakage point if the types in pngconf.h aren't exactly right.
     */
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+   png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, skip))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
-   slength = length;
-   png_ptr->chunkdata[slength] = 0x00;
+   buffer[length] = 0;
 
-   for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start;
-       entry_start++)
+   for (entry_start = buffer; *entry_start; entry_start++)
       /* Empty loop to find end of name */ ;
 
    ++entry_start;
 
    /* A sample depth should follow the separator, and we should be on it  */
-   if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2)
+   if (entry_start > buffer + length - 2)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       png_warning(png_ptr, "malformed sPLT chunk");
       return;
    }
@@ -1562,17 +1618,13 @@
    new_palette.depth = *entry_start++;
    entry_size = (new_palette.depth == 8 ? 6 : 10);
    /* This must fit in a png_uint_32 because it is derived from the original
-    * chunk data length (and use 'length', not 'slength' here for clarity -
-    * they are guaranteed to be the same, see the tests above.)
+    * chunk data length.
     */
-   data_length = length - (png_uint_32)(entry_start -
-      (png_bytep)png_ptr->chunkdata);
+   data_length = length - (png_uint_32)(entry_start - buffer);
 
    /* Integrity-check the data length */
    if (data_length % entry_size)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       png_warning(png_ptr, "sPLT chunk has bad length");
       return;
    }
@@ -1647,12 +1699,10 @@
 #endif
 
    /* Discard all chunk data except the name and stash that */
-   new_palette.name = png_ptr->chunkdata;
+   new_palette.name = (png_charp)buffer;
 
    png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
    png_free(png_ptr, new_palette.entries);
 }
 #endif /* PNG_READ_sPLT_SUPPORTED */
@@ -2024,7 +2074,7 @@
 {
    png_int_32 X0, X1;
    png_byte type, nparams;
-   png_charp buf, units, endptr;
+   png_bytep buffer, buf, units, endptr;
    png_charpp params;
    int i;
 
@@ -2049,31 +2099,27 @@
 
    png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
        length + 1);
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
 
-   if (png_ptr->chunkdata == NULL)
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
+
+   if (buffer == NULL)
    {
       png_warning(png_ptr, "No memory for pCAL purpose");
       return;
    }
 
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+   png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
-   png_ptr->chunkdata[length] = 0x00; /* Null terminate the last string */
+   buffer[length] = 0; /* Null terminate the last string */
 
    png_debug(3, "Finding end of pCAL purpose string");
-   for (buf = png_ptr->chunkdata; *buf; buf++)
+   for (buf = buffer; *buf; buf++)
       /* Empty loop */ ;
 
-   endptr = png_ptr->chunkdata + length;
+   endptr = buffer + length;
 
    /* We need to have at least 12 bytes after the purpose string
     * in order to get the parameter information.
@@ -2081,8 +2127,6 @@
    if (endptr <= buf + 12)
    {
       png_warning(png_ptr, "Invalid pCAL data");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
    }
 
@@ -2103,8 +2147,6 @@
        (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
    {
       png_warning(png_ptr, "Invalid pCAL parameters for equation type");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
    }
 
@@ -2118,43 +2160,37 @@
 
    png_debug(3, "Allocating pCAL parameters array");
 
-   params = (png_charpp)png_malloc_warn(png_ptr,
-       (png_size_t)(nparams * png_sizeof(png_charp)));
+   params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
+       nparams * png_sizeof(png_charp)));
 
    if (params == NULL)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       png_warning(png_ptr, "No memory for pCAL params");
       return;
    }
 
    /* Get pointers to the start of each parameter string. */
-   for (i = 0; i < (int)nparams; i++)
+   for (i = 0; i < nparams; i++)
    {
       buf++; /* Skip the null string terminator from previous parameter. */
 
       png_debug1(3, "Reading pCAL parameter %d", i);
 
-      for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++)
+      for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++)
          /* Empty loop to move past each parameter string */ ;
 
       /* Make sure we haven't run out of data yet */
       if (buf > endptr)
       {
          png_warning(png_ptr, "Invalid pCAL data");
-         png_free(png_ptr, png_ptr->chunkdata);
-         png_ptr->chunkdata = NULL;
          png_free(png_ptr, params);
          return;
       }
    }
 
-   png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams,
-      units, params);
+   png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
+      (png_charp)units, params);
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
    png_free(png_ptr, params);
 }
 #endif
@@ -2164,7 +2200,8 @@
 void /* PRIVATE */
 png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_size_t slength, i;
+   png_bytep buffer;
+   png_size_t i;
    int state;
 
    png_debug(1, "in png_handle_sCAL");
@@ -2197,32 +2234,25 @@
    png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
       length + 1);
 
-   png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
 
-   if (png_ptr->chunkdata == NULL)
+   if (buffer == NULL)
    {
       png_warning(png_ptr, "Out of memory while processing sCAL chunk");
       png_crc_finish(png_ptr, length);
       return;
    }
 
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
-   slength = length;
-   png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */
+   png_crc_read(png_ptr, buffer, length);
+   buffer[length] = 0; /* Null terminate the last string */
 
    if (png_crc_finish(png_ptr, 0))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
    /* Validate the unit. */
-   if (png_ptr->chunkdata[0] != 1 && png_ptr->chunkdata[0] != 2)
+   if (buffer[0] != 1 && buffer[0] != 2)
    {
       png_warning(png_ptr, "Invalid sCAL ignored: invalid unit");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
    }
 
@@ -2232,8 +2262,8 @@
    i = 1;
    state = 0;
 
-   if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
-       i >= slength || png_ptr->chunkdata[i++] != 0)
+   if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) ||
+       i >= length || buffer[i++] != 0)
       png_warning(png_ptr, "Invalid sCAL chunk ignored: bad width format");
 
    else if (!PNG_FP_IS_POSITIVE(state))
@@ -2244,8 +2274,8 @@
       png_size_t heighti = i;
 
       state = 0;
-      if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
-          i != slength)
+      if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) ||
+         i != length)
          png_warning(png_ptr, "Invalid sCAL chunk ignored: bad height format");
 
       else if (!PNG_FP_IS_POSITIVE(state))
@@ -2254,13 +2284,9 @@
 
       else
          /* This is the (only) success case. */
-         png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0],
-            png_ptr->chunkdata+1, png_ptr->chunkdata+heighti);
+         png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
+            (png_charp)buffer+1, (png_charp)buffer+heighti);
    }
-
-   /* Clean up - just free the temporarily allocated buffer. */
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
 }
 #endif
 
@@ -2314,11 +2340,11 @@
 void /* PRIVATE */
 png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_textp text_ptr;
+   png_text  text_info;
+   png_bytep buffer;
    png_charp key;
    png_charp text;
    png_uint_32 skip = 0;
-   int ret;
 
    png_debug(1, "in png_handle_tEXt");
 
@@ -2355,28 +2381,21 @@
    }
 #endif
 
-   png_free(png_ptr, png_ptr->chunkdata);
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
 
-   png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
-
-   if (png_ptr->chunkdata == NULL)
+   if (buffer == NULL)
    {
      png_warning(png_ptr, "No memory to process text chunk");
      return;
    }
 
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+   png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, skip))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
-   key = png_ptr->chunkdata;
-
-   key[length] = 0x00;
+   key = (png_charp)buffer;
+   key[length] = 0;
 
    for (text = key; *text; text++)
       /* Empty loop to find end of key */ ;
@@ -2384,32 +2403,15 @@
    if (text != key + length)
       text++;
 
-   text_ptr = (png_textp)png_malloc_warn(png_ptr,
-       png_sizeof(png_text));
+   text_info.compression = PNG_TEXT_COMPRESSION_NONE;
+   text_info.key = key;
+   text_info.lang = NULL;
+   text_info.lang_key = NULL;
+   text_info.itxt_length = 0;
+   text_info.text = text;
+   text_info.text_length = png_strlen(text);
 
-   if (text_ptr == NULL)
-   {
-      png_warning(png_ptr, "Not enough memory to process text chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
-
-   text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
-   text_ptr->key = key;
-   text_ptr->lang = NULL;
-   text_ptr->lang_key = NULL;
-   text_ptr->itxt_length = 0;
-   text_ptr->text = text;
-   text_ptr->text_length = png_strlen(text);
-
-   ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
-
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
-   png_free(png_ptr, text_ptr);
-
-   if (ret)
+   if (png_set_text_2(png_ptr, info_ptr, &text_info, 1))
       png_warning(png_ptr, "Insufficient memory to process text chunk");
 }
 #endif
@@ -2420,7 +2422,8 @@
 png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_const_charp errmsg = NULL;
-   png_uint_32 keyword_length;
+   png_bytep       buffer;
+   png_uint_32     keyword_length;
 
    png_debug(1, "in png_handle_zTXt");
 
@@ -2448,28 +2451,22 @@
    if (png_ptr->mode & PNG_HAVE_IDAT)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = png_voidcast(png_charp, png_malloc_warn(png_ptr,
-      length));
+   buffer = png_read_buffer(png_ptr, length, 1/*warn*/);
 
-   if (png_ptr->chunkdata == NULL)
+   if (buffer == NULL)
    {
       png_chunk_benign_error(png_ptr, "insufficient memory");
       return;
    }
 
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+   png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
    /* TODO: also check that the keyword contents match the spec! */
    for (keyword_length = 0;
-      keyword_length < length && png_ptr->chunkdata[keyword_length] != 0;
+      keyword_length < length && buffer[keyword_length] != 0;
       ++keyword_length)
       /* Empty loop to find end of name */ ;
 
@@ -2483,7 +2480,7 @@
    else if (keyword_length + 3 > length)
       errmsg = "truncated";
 
-   else if (png_ptr->chunkdata[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
+   else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
       errmsg = "unknown compression type";
 
    else
@@ -2494,22 +2491,21 @@
        * level memory limit, this should be split to different values for iCCP
        * and text chunks.
        */
-      errmsg = png_decompress_chunk(png_ptr, length, keyword_length+2,
-         &uncompressed_length, 1/*terminate*/);
-
-      if (errmsg == NULL)
+      if (png_decompress_chunk(png_ptr, length, keyword_length+2,
+         &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
       {
          png_text text;
 
-         /* It worked; png_ptr->chunkdata now looks like a tEXt chunk except for
-          * the extra compression type byte and the fact that it isn't
+         /* It worked; png_ptr->read_buffer now looks like a tEXt chunk except
+          * for the extra compression type byte and the fact that it isn't
           * necessarily '\0' terminated.
           */
-         png_ptr->chunkdata[uncompressed_length+(keyword_length+2)] = 0;
+         buffer = png_ptr->read_buffer;
+         buffer[uncompressed_length+(keyword_length+2)] = 0;
 
          text.compression = PNG_TEXT_COMPRESSION_zTXt;
-         text.key = png_ptr->chunkdata;
-         text.text = png_ptr->chunkdata + keyword_length+2;
+         text.key = (png_charp)buffer;
+         text.text = (png_charp)(buffer + keyword_length+2);
          text.text_length = uncompressed_length;
          text.itxt_length = 0;
          text.lang = NULL;
@@ -2518,10 +2514,10 @@
          if (png_set_text_2(png_ptr, info_ptr, &text, 1))
             errmsg = "insufficient memory to store zTXt chunk";
       }
-   }
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
+      else
+         errmsg = png_ptr->zstream.msg;
+   }
 
    if (errmsg != NULL)
       png_chunk_benign_error(png_ptr, errmsg);
@@ -2534,6 +2530,7 @@
 png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_const_charp errmsg = NULL;
+   png_bytep buffer;
    png_uint_32 prefix_length;
 
    png_debug(1, "in png_handle_iTXt");
@@ -2562,28 +2559,22 @@
    if (png_ptr->mode & PNG_HAVE_IDAT)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = png_voidcast(png_charp, png_malloc_warn(png_ptr,
-      length+1));
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
 
-   if (png_ptr->chunkdata == NULL)
+   if (buffer == NULL)
    {
       png_chunk_benign_error(png_ptr, "insufficient memory");
       return;
    }
 
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, length);
+   png_crc_read(png_ptr, buffer, length);
 
    if (png_crc_finish(png_ptr, 0))
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
       return;
-   }
 
    /* First the keyword. */
    for (prefix_length=0;
-      prefix_length < length && png_ptr->chunkdata[prefix_length] != 0;
+      prefix_length < length && buffer[prefix_length] != 0;
       ++prefix_length)
       /* Empty loop */ ;
 
@@ -2598,11 +2589,11 @@
    else if (prefix_length + 5 > length)
       errmsg = "truncated";
 
-   else if (png_ptr->chunkdata[prefix_length+1] == 0 ||
-      (png_ptr->chunkdata[prefix_length+1] == 1 &&
-      png_ptr->chunkdata[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
+   else if (buffer[prefix_length+1] == 0 ||
+      (buffer[prefix_length+1] == 1 &&
+      buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
    {
-      int compressed = png_ptr->chunkdata[prefix_length+1] != 0;
+      int compressed = buffer[prefix_length+1] != 0;
       png_uint_32 language_offset, translated_keyword_offset;
       png_alloc_size_t uncompressed_length = 0;
 
@@ -2610,14 +2601,14 @@
       prefix_length += 3;
       language_offset = prefix_length;
 
-      for (; prefix_length < length && png_ptr->chunkdata[prefix_length] != 0;
+      for (; prefix_length < length && buffer[prefix_length] != 0;
          ++prefix_length)
          /* Empty loop */ ;
 
       /* WARNING: the length may be invalid here, this is checked below. */
       translated_keyword_offset = ++prefix_length;
 
-      for (; prefix_length < length && png_ptr->chunkdata[prefix_length] != 0;
+      for (; prefix_length < length && buffer[prefix_length] != 0;
          ++prefix_length)
          /* Empty loop */ ;
 
@@ -2639,8 +2630,12 @@
           * level memory limit, this should be split to different values for
           * iCCP and text chunks.
           */
-         errmsg = png_decompress_chunk(png_ptr, length, prefix_length,
-            &uncompressed_length, 1/*terminate*/);
+         if (png_decompress_chunk(png_ptr, length, prefix_length,
+            &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+            buffer = png_ptr->read_buffer;
+
+         else
+            errmsg = png_ptr->zstream.msg;
       }
 
       else
@@ -2650,7 +2645,7 @@
       {
          png_text text;
 
-         png_ptr->chunkdata[uncompressed_length+prefix_length] = 0;
+         buffer[uncompressed_length+prefix_length] = 0;
 
          if (compressed)
             text.compression = PNG_ITXT_COMPRESSION_NONE;
@@ -2658,10 +2653,10 @@
          else
             text.compression = PNG_ITXT_COMPRESSION_zTXt;
 
-         text.key = png_ptr->chunkdata;
-         text.lang = png_ptr->chunkdata + language_offset;
-         text.lang_key = png_ptr->chunkdata + translated_keyword_offset;
-         text.text = png_ptr->chunkdata + prefix_length;
+         text.key = (png_charp)buffer;
+         text.lang = (png_charp)buffer + language_offset;
+         text.lang_key = (png_charp)buffer + translated_keyword_offset;
+         text.text = (png_charp)buffer + prefix_length;
          text.text_length = 0;
          text.itxt_length = uncompressed_length;
 
@@ -2673,9 +2668,6 @@
    else
       errmsg = "bad compression info";
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
-
    if (errmsg != NULL)
       png_chunk_benign_error(png_ptr, errmsg);
 }
@@ -3822,6 +3814,176 @@
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 void /* PRIVATE */
+png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
+   png_alloc_size_t avail_out)
+{
+   /* Loop reading IDATs and decompressing the result into output[avail_out] */
+   png_ptr->zstream.next_out = output;
+   png_ptr->zstream.avail_out = 0; /* safety: set below */
+
+   if (output == NULL)
+      avail_out = 0;
+
+   do
+   {
+      int ret;
+      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
+
+      if (png_ptr->zstream.avail_in == 0)
+      {
+         uInt avail_in;
+         png_bytep buffer;
+
+         while (png_ptr->idat_size == 0)
+         {
+            png_crc_finish(png_ptr, 0);
+
+            png_ptr->idat_size = png_read_chunk_header(png_ptr);
+            /* This is an error even in the 'check' case because the code just
+             * consumed a non-IDAT header.
+             */
+            if (png_ptr->chunk_name != png_IDAT)
+               png_error(png_ptr, "Not enough image data");
+         }
+
+         avail_in = png_ptr->IDAT_read_size;
+
+         if (avail_in > png_ptr->idat_size)
+            avail_in = (uInt)png_ptr->idat_size;
+
+         /* A PNG with a gradually increasing IDAT size will defeat this attempt
+          * to minimize memory usage by causing lots of re-allocs, but
+          * realistically doing IDAT_read_size re-allocs is not likely to be a
+          * big problem.
+          */
+         buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
+
+         png_crc_read(png_ptr, buffer, avail_in);
+         png_ptr->idat_size -= avail_in;
+
+         png_ptr->zstream.next_in = buffer;
+         png_ptr->zstream.avail_in = avail_in;
+      }
+
+      /* And set up the output side. */
+      if (output != NULL) /* standard read */
+      {
+         uInt out = ZLIB_IO_MAX;
+
+         if (out > avail_out)
+            out = (uInt)avail_out;
+
+         avail_out -= out;
+         png_ptr->zstream.avail_out = out;
+      }
+
+      else /* check for end */
+      {
+         png_ptr->zstream.next_out = tmpbuf;
+         png_ptr->zstream.avail_out = sizeof tmpbuf;
+      }
+
+      /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the
+       * process.
+       */
+      ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
+
+      /* Take the unconsumed output back (so, in the 'check' case this just
+       * counts up).
+       */
+      avail_out += png_ptr->zstream.avail_out;
+      png_ptr->zstream.avail_out = 0;
+
+      if (ret == Z_STREAM_END)
+      {
+         /* Do this for safety; we won't read any more into this row. */
+         png_ptr->zstream.next_out = NULL;
+
+         png_ptr->mode |= PNG_AFTER_IDAT;
+         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+
+         if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0)
+            png_chunk_benign_error(png_ptr, "Extra compressed data");
+         break;
+      }
+
+      if (ret != Z_OK)
+      {
+         png_zstream_error(png_ptr, ret);
+
+         if (output != NULL)
+            png_chunk_error(png_ptr, png_ptr->zstream.msg);
+
+         else /* checking */
+         {
+            png_chunk_benign_error(png_ptr, png_ptr->zstream.msg);
+            return;
+         }
+      }
+   } while (avail_out > 0);
+
+   if (avail_out > 0)
+   {
+      /* The stream ended before the image; this is the same as too few IDATs so
+       * should be handled the same way.
+       */
+      if (output != NULL)
+         png_error(png_ptr, "Not enough image data");
+
+      else /* checking */
+         png_chunk_benign_error(png_ptr, "Too much image data");
+   }
+}
+
+void /* PRIVATE */
+png_read_finish_IDAT(png_structrp png_ptr)
+{
+   /* We don't need any more data and the stream should have ended, however the
+    * LZ end code may actually not have been processed.  In this case we must
+    * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk
+    * may still remain to be consumed.
+    */
+   if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
+   {
+      /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in
+       * the compressed stream, but the stream may be damaged too, so even after
+       * this call we may need to terminate the zstream ownership.
+       */
+      png_read_IDAT_data(png_ptr, NULL, 0);
+      png_ptr->zstream.next_out = NULL; /* safety */
+
+      /* Now clear everything out for safety; the following may not have been
+       * done.
+       */
+      if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
+      {
+         png_ptr->mode |= PNG_AFTER_IDAT;
+         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+      }
+   }
+
+   /* If the zstream has not been released do it now *and* terminate the reading
+    * of the final IDAT chunk.
+    */
+   if (png_ptr->zowner == png_IDAT)
+   {
+      /* Always do this; the pointers otherwise point into the read buffer. */
+      png_ptr->zstream.next_in = NULL;
+      png_ptr->zstream.avail_in = 0;
+
+      /* Now we no longer own the zstream. */
+      png_ptr->zowner = 0;
+
+      /* The slightly weird semantics of the sequential IDAT reading is that we
+       * are always in or at the end of an IDAT chunk, so we always need to do a
+       * crc_finish here.  If idat_size is non-zero we also need to read the
+       * spurious bytes at the end of the chunk now.
+       */
+      (void)png_crc_finish(png_ptr, png_ptr->idat_size);
+   }
+}
+
+void /* PRIVATE */
 png_read_finish_row(png_structrp png_ptr)
 {
 #ifdef PNG_READ_INTERLACING_SUPPORTED
@@ -3886,66 +4048,7 @@
 #endif /* PNG_READ_INTERLACING_SUPPORTED */
 
    /* Here after at the end of the last row of the last pass. */
-   if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))
-   {
-      Byte extra;
-      int ret;
-
-      png_ptr->zstream.next_out = &extra;
-      png_ptr->zstream.avail_out = 1;
-
-      for (;;)
-      {
-         if (!(png_ptr->zstream.avail_in))
-         {
-            while (png_ptr->idat_size == 0)
-            {
-               png_crc_finish(png_ptr, 0);
-               png_ptr->idat_size = png_read_chunk_header(png_ptr);
-               if (png_ptr->chunk_name != png_IDAT)
-                  png_error(png_ptr, "Not enough image data");
-            }
-
-            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 = 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_NO_FLUSH);
-
-         if (ret == Z_STREAM_END)
-         {
-            if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in ||
-                png_ptr->idat_size)
-               png_warning(png_ptr, "Extra compressed data");
-
-            png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
-            break;
-         }
-
-         if (ret != Z_OK)
-            png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
-                "Decompression Error");
-
-         if (!(png_ptr->zstream.avail_out))
-         {
-            png_warning(png_ptr, "Extra compressed data");
-            break;
-         }
-      }
-      png_ptr->zstream.avail_out = 0;
-   }
-
-   if (png_ptr->idat_size || png_ptr->zstream.avail_in)
-      png_warning(png_ptr, "Extra compression data");
-
-   png_ptr->flags &= ~PNG_FLAG_ZSTREAM_IN_USE;
-   png_ptr->mode |= PNG_AFTER_IDAT;
+   png_read_finish_IDAT(png_ptr);
 }
 #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
 
@@ -4220,8 +4323,22 @@
    png_debug1(3, "irowbytes = %lu",
        (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1);
 
+   /* The sequential reader needs a buffer for IDAT, but the progressive reader
+    * does not, so free the read buffer now regardless; the sequential reader
+    * reallocates it on demand.
+    */
+   if (png_ptr->read_buffer)
+   {
+      png_bytep buffer = png_ptr->read_buffer;
+
+      png_ptr->read_buffer_size = 0;
+      png_ptr->read_buffer = NULL;
+      png_free(png_ptr, buffer);
+   }
+
    /* Finally claim the zstream for the inflate of the IDAT data. */
-   png_inflate_claim(png_ptr);
+   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/pngset.c b/pngset.c
index 4fbac40..882be97 100644
--- a/pngset.c
+++ b/pngset.c
@@ -1231,26 +1231,51 @@
     if (png_ptr == NULL)
        return;
 
-    png_free(png_ptr, png_ptr->zbuf);
+    if (size == 0 || size > PNG_UINT_31_MAX)
+       png_error(png_ptr, "invalid compression buffer size");
 
-    if (size > ZLIB_IO_MAX)
-    {
-       png_warning(png_ptr, "Attempt to set buffer size beyond max ignored");
-       png_ptr->zbuf_size = ZLIB_IO_MAX;
-       size = ZLIB_IO_MAX; /* must fit */
-    }
+#  ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+      if (png_ptr->mode & PNG_IS_READ_STRUCT)
+      {
+         png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
+         return;
+      }
+#  endif
 
-    else
-       png_ptr->zbuf_size = (uInt)size;
-
-    png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size);
-
-    /* The following ensures a relatively safe failure if this gets called while
-     * the buffer is actually in use.
-     */
-    png_ptr->zstream.next_out = png_ptr->zbuf;
-    png_ptr->zstream.avail_out = 0;
-    png_ptr->zstream.avail_in = 0;
+#  ifdef PNG_WRITE_SUPPORTED
+      if (!(png_ptr->mode & PNG_IS_READ_STRUCT))
+      {
+         if (png_ptr->zowner != 0)
+         {
+            png_warning(png_ptr,
+              "Compression buffer size cannot be changed because it is in use");
+            return;
+         }
+  
+         if (size > ZLIB_IO_MAX)
+         {
+            png_warning(png_ptr,
+               "Compression buffer size limited to system maximum");
+            size = ZLIB_IO_MAX; /* must fit */
+         }
+  
+         else if (size < 6)
+         {
+            /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
+             * if this is permitted.
+             */
+            png_warning(png_ptr,
+               "Compression buffer size cannot be reduced below 6");
+            return;
+         }
+  
+         if (png_ptr->zbuffer_size != size)
+         {
+            png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
+            png_ptr->zbuffer_size = (uInt)size;
+         }
+      }
+#  endif
 }
 
 void PNGAPI
diff --git a/pngstruct.h b/pngstruct.h
index fb29b54..88a5dda 100644
--- a/pngstruct.h
+++ b/pngstruct.h
@@ -24,7 +24,50 @@
  * in this structure and is required for decompressing the LZ compressed
  * data in PNG files.
  */
+#ifndef ZLIB_CONST
+   /* We must ensure that zlib uses 'const' in declarations. */
+#  define ZLIB_CONST
+#endif
 #include "zlib.h"
+#ifdef const
+   /* zlib.h sometimes #defines const to nothing, undo this. */
+#  undef const
+#endif
+
+/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
+ * with older builds.
+ */
+#if ZLIB_VERNUM < 0x1260
+#  define PNGZ_MSG_CAST(s) png_constcast(char*,s)
+#  define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
+#else
+#  define PNGZ_MSG_CAST(s) (s)
+#  define PNGZ_INPUT_CAST(b) (b)
+#endif
+
+/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
+ * can handle at once.  This type need be no larger than 16 bits (so maximum of
+ * 65535), this define allows us to discover how big it is, but limited by the
+ * maximuum for png_size_t.  The value can be overriden in a library build
+ * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
+ * lower value (e.g. 255 works).  A lower value may help memory usage (slightly)
+ * and may even improve performance on some systems (and degrade it on others.)
+ */
+#ifndef ZLIB_IO_MAX
+#  define ZLIB_IO_MAX ((uInt)-1)
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+/* The type of a compression buffer list used by the write code. */
+typedef struct png_compression_buffer
+{
+   struct png_compression_buffer *next;
+   png_byte                       output[1]; /* actually zbuf_size */
+} png_compression_buffer, *png_compression_bufferp;
+
+#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
+   (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
+#endif
 
 struct png_struct_def
 {
@@ -65,11 +108,13 @@
    png_uint_32 flags;         /* flags indicating various things to libpng */
    png_uint_32 transformations; /* which transformations to perform */
 
-   z_stream zstream;          /* decompression structure */
-   png_bytep zbuf;            /* buffer for zlib */
-   uInt zbuf_size;            /* size of zbuf */
+   png_uint_32 zowner;        /* ID (chunk type) of zstream owner, 0 if none */
+   z_stream    zstream;       /* decompression structure */
 
 #ifdef PNG_WRITE_SUPPORTED
+   png_compression_bufferp zbuffer_list; /* Created on demand during write */
+   uInt                    zbuffer_size; /* size of the actual buffer */
+
    int zlib_level;            /* holds zlib compression level */
    int zlib_method;           /* holds zlib compression method */
    int zlib_window_bits;      /* holds zlib compression window bits */
@@ -341,8 +386,14 @@
 /* New member added in libpng-1.2.26 */
   png_size_t old_big_row_buf_size;
 
+#ifdef PNG_READ_SUPPORTED
 /* New member added in libpng-1.2.30 */
-  png_charp chunkdata;  /* buffer for reading chunk data */
+  png_bytep        read_buffer;      /* buffer for reading chunk data */
+  png_alloc_size_t read_buffer_size; /* current size of the buffer */
+#endif
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+  uInt             IDAT_read_size;   /* limit on read buffer size for IDAT */
+#endif
 
 #ifdef PNG_IO_STATE_SUPPORTED
 /* New member added in libpng-1.4.0 */
diff --git a/pngwrite.c b/pngwrite.c
index 7fb8577..4b10b04 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -475,6 +475,8 @@
    /* Set the zlib control values to defaults; they can be overridden by the
     * application after the struct has been created.
     */
+   png_ptr->zbuffer_size = PNG_ZBUF_SIZE;
+
    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;
@@ -784,8 +786,6 @@
 void PNGAPI
 png_write_flush(png_structrp png_ptr)
 {
-   int wrote_IDAT;
-
    png_debug(1, "in png_write_flush");
 
    if (png_ptr == NULL)
@@ -795,39 +795,7 @@
    if (png_ptr->row_number >= png_ptr->num_rows)
       return;
 
-   do
-   {
-      int ret;
-
-      /* Compress the data */
-      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
-      wrote_IDAT = 0;
-
-      /* Check for compression errors */
-      if (ret != Z_OK)
-      {
-         if (png_ptr->zstream.msg != NULL)
-            png_error(png_ptr, png_ptr->zstream.msg);
-
-         else
-            png_error(png_ptr, "zlib error");
-      }
-
-      if (!(png_ptr->zstream.avail_out))
-      {
-         /* Write the IDAT and reset the zlib output buffer */
-         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
-         wrote_IDAT = 1;
-      }
-   } while (wrote_IDAT == 1);
-
-   /* If there is any data left to be output, write it into a new IDAT */
-   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
-   {
-      /* Write the IDAT and reset the zlib output buffer */
-      png_write_IDAT(png_ptr, png_ptr->zbuf,
-          png_ptr->zbuf_size - png_ptr->zstream.avail_out);
-   }
+   png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
    png_ptr->flush_rows = 0;
    png_flush(png_ptr);
 }
@@ -848,7 +816,7 @@
       deflateEnd(&png_ptr->zstream);
 
    /* Free our memory.  png_free checks NULL for us. */
-   png_free(png_ptr, png_ptr->zbuf);
+   png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
    png_free(png_ptr, png_ptr->row_buf);
 #ifdef PNG_WRITE_FILTER_SUPPORTED
    png_free(png_ptr, png_ptr->prev_row);
diff --git a/pngwutil.c b/pngwutil.c
index d4a50f6..44a8413 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -197,7 +197,7 @@
       return;
 
    /* On 64 bit architectures 'length' may not fit in a png_uint_32. */
-   if (length > PNG_UINT_32_MAX)
+   if (length > PNG_UINT_31_MAX)
       png_error(png_ptr, "length exceeds PNG maxima");
 
    png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);
@@ -257,12 +257,90 @@
       return 0xffffffffU;
 }
 
-/* Initialize the compressor for the appropriate type of compression. */
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+   /* This is the code to hack the first two bytes of the deflate stream (the
+    * deflate header) to correct the windowBits value to match the actual data
+    * size.  Note that the second argument is the *uncompressed* size but the
+    * first argument is the *compressed* data (and it must be deflate
+    * compressed.)
+    */
 static void
-png_deflate_claim(png_structrp png_ptr, int for_IDAT,
+optimize_cmf(png_bytep data, png_alloc_size_t data_size)
+{
+   /* Optimize the CMF field in the zlib stream.  The resultant zlib stream is
+    * still compliant to the stream specification.
+    */
+   if (data_size <= 16384) /* else windowBits must be 15 */
+   {
+      unsigned int z_cmf = data[0];  /* zlib compression method and flags */
+
+      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+      {
+         unsigned int z_cinfo;
+         unsigned int half_z_window_size;
+
+         z_cinfo = z_cmf >> 4;
+         half_z_window_size = 1U << (z_cinfo + 7);
+
+         if (data_size <= half_z_window_size) /* else no change */
+         {
+            unsigned int tmp;
+
+            do
+            {
+               half_z_window_size >>= 1;
+               --z_cinfo;
+            }
+            while (z_cinfo > 0 && data_size <= half_z_window_size);
+
+            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+
+            data[0] = (png_byte)z_cmf;
+            tmp = data[1] & 0xe0;
+            tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
+            data[1] = (png_byte)tmp;
+         }
+      }
+   }
+}
+#else
+#  define optimize_cmf(dp,dl) ((void)0)
+#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
+
+/* Initialize the compressor for the appropriate type of compression. */
+static int
+png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
    png_alloc_size_t data_size)
 {
-   if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_IN_USE))
+   if (png_ptr->zowner != 0)
+   {
+      char msg[64];
+
+      PNG_STRING_FROM_CHUNK(msg, owner);
+      msg[4] = ':';
+      msg[5] = ' ';
+      PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);
+      /* So the message that results is "<chunk> using zstream"; this is an
+       * internal error, but is very useful for debugging.  i18n requirements
+       * are minimal.
+       */
+      (void)png_safecat(msg, sizeof msg, 10, " using zstream");
+#     if PNG_LIBPNG_BUILD_BASE_TYPE == PNG_LIBPNG_BUILD_STABLE
+         png_warning(png_ptr, msg);
+
+         /* Attempt sane error recovery */
+         if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */
+         {
+            png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");
+            return Z_STREAM_ERROR;
+         }
+
+         png_ptr->zowner = 0;
+#     else
+         png_error(png_ptr, msg);
+#     endif
+   }
+
    {
       int level = png_ptr->zlib_level;
       int method = png_ptr->zlib_method;
@@ -271,10 +349,7 @@
       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 (owner == png_IDAT)
       {
          if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)
             strategy = png_ptr->zlib_strategy;
@@ -311,29 +386,20 @@
        * next windowBits size we need to fix up the value later.  (Because even
        * though deflate needs the extra window, inflate does not!)
        */
-      if (data_size <= 256)
-         windowBits = 8;
-
-      else if (data_size <= 16384) /* Else 15 bits required */ for (;;)
+      if (data_size <= 16384)
       {
-         png_uint_32 test = 1U << (windowBits-1);
-         if (data_size > test)
-            break;
-         --windowBits;
-      }
+         /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
+          * work round a Microsoft Visual C misbehavior which, contrary to C-90,
+          * widens the result of the following shift to 64-bits if (and,
+          * apparently, only if) it is used in a test.
+          */
+         unsigned int half_window_size = 1U << (windowBits-1);
 
-      if (windowBits < 15)
-      {
-         png_uint_32 test = 1U << windowBits;
-
-         if (data_size + 262U > test)
+         while (data_size + 262 <= half_window_size)
          {
-            ++windowBits;
-            png_ptr->flags |= PNG_FLAG_ZSTREAM_CMF_FIXUP;
+            half_window_size >>= 1;
+            --windowBits;
          }
-
-         else
-            png_ptr->flags &= ~PNG_FLAG_ZSTREAM_CMF_FIXUP;
       }
 
       /* Check against the previous initialized values, if any. */
@@ -350,6 +416,14 @@
          png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;
       }
 
+      /* For safety clear out the input and output pointers (currently zlib
+       * doesn't use them on Init, but it might in the future).
+       */
+      png_ptr->zstream.next_in = NULL;
+      png_ptr->zstream.avail_in = 0;
+      png_ptr->zstream.next_out = NULL;
+      png_ptr->zstream.avail_out = 0;
+
       /* Now initialize if required, setting the new parameters, otherwise just
        * to a simple reset to the previous parameters.
        */
@@ -369,357 +443,325 @@
        * 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;
-      }
+         png_ptr->zowner = owner;
 
       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);
+         png_zstream_error(png_ptr, ret);
 
-         else
-            png_error(png_ptr, "zlib initialization error");
-      }
+      return ret;
    }
-
-   else
-      png_error(png_ptr, "zstream already in use (internal error)");
-}
-
-/* The opposite: release the stream.  It is also reset, this API will warn on
- * error but will not fail.
- */
-static void
-png_deflate_release(png_structrp png_ptr)
-{
-   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)");
 }
 
 #ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
 /* This pair of functions encapsulates the operation of (a) compressing a
  * text string, and (b) issuing it later as a series of chunk data writes.
  * The compression_state structure is shared context for these functions
- * set up by the caller in order to make the whole mess thread-safe.
+ * set up by the caller to allow access to the relevant local variables.
+ *
+ * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
+ * temporary buffers.  From 1.6.0 it is retained in png_struct so that it will
+ * be correctly freed in the event of a write error (previous implementations
+ * just leaked memory.)
  */
-
 typedef struct
 {
-   png_const_bytep input;   /* The uncompressed input data */
-   png_size_t input_len;    /* Its length */
-   int num_output_ptr;      /* Number of output pointers used */
-   int max_output_ptr;      /* Size of output_ptr */
-   png_bytep *output_ptr;   /* Array of pointers to output */
+   png_const_bytep      input;        /* The uncompressed input data */
+   png_alloc_size_t     input_len;    /* Its length */
+   png_uint_32          output_len;   /* Final compressed length */
+   png_byte             output[1024]; /* First block of output */
 } compression_state;
 
-/* Compress given text into storage in the png_ptr structure */
-static png_size_t /* PRIVATE */
-png_text_compress(png_structrp png_ptr,
-    png_const_charp text, png_size_t text_len, int compression,
-    compression_state *comp)
+static void
+png_text_compress_init(compression_state *comp, png_const_bytep input,
+   png_alloc_size_t input_len)
+{
+   comp->input = input;
+   comp->input_len = input_len;
+   comp->output_len = 0;
+}
+
+void /* PRIVATE */
+png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
+{
+   png_compression_bufferp list = *listp;
+
+   if (list != NULL)
+   {
+      *listp = NULL;
+
+      do
+      {
+         png_compression_bufferp next = list->next;
+
+         png_free(png_ptr, list);
+         list = next;
+      }
+      while (list != NULL);
+   }
+}
+
+/* Compress the data in the compression state input */
+static int
+png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
+   compression_state *comp, png_uint_32 prefix_len)
 {
    int ret;
 
-   comp->num_output_ptr = 0;
-   comp->max_output_ptr = 0;
-   comp->output_ptr = NULL;
-   comp->input = NULL;
-   comp->input_len = text_len;
-
-   /* We may just want to pass the text right through */
-   if (compression == PNG_TEXT_COMPRESSION_NONE)
-   {
-      comp->input = (png_const_bytep)text;
-      return text_len;
-   }
-
-   if (compression >= PNG_TEXT_COMPRESSION_LAST)
-   {
-      PNG_WARNING_PARAMETERS(p)
-
-      png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d,
-         compression);
-      png_formatted_warning(png_ptr, p, "Unknown compression type @1");
-   }
-
-   /* We can't write the chunk until we find out how much data we have,
-    * which means we need to run the compressor first and save the
-    * output.  This shouldn't be a problem, as the vast majority of
-    * comments should be reasonable, but we will set up an array of
-    * malloc'd pointers to be sure.
+   /* To find the length of the output it is necessary to first compress the
+    * input, the result is buffered rather than using the two-pass algorithm
+    * that is used on the inflate side; deflate is assumed to be slower and a
+    * PNG writer is assumed to have more memory available than a PNG reader.
     *
-    * If we knew the application was well behaved, we could simplify this
-    * greatly by assuming we can always malloc an output buffer large
-    * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
-    * and malloc this directly.  The only time this would be a bad idea is
-    * if we can't malloc more than 64K and we have 64K of random input
-    * data, or if the input string is incredibly large (although this
-    * wouldn't cause a failure, just a slowdown due to swapping).
+    * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an
+    * upper limit on the output size, but it is always bigger than the input
+    * size so it is likely to be more efficient to use this linked-list
+    * approach.
     */
-   png_deflate_claim(png_ptr, 0/*!for IDAT*/, text_len);
+   ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);
 
-   /* Set up the compression buffers */
-   /* TODO: the following cast hides a ****CERTAIN**** overflow problem. */
-   png_ptr->zstream.avail_in = (uInt)text_len;
+   if (ret != Z_OK)
+      return ret;
 
-   /* NOTE: assume zlib doesn't overwrite the input */
-   png_ptr->zstream.next_in = (Bytef *)text;
-   png_ptr->zstream.avail_out = png_ptr->zbuf_size;
-   png_ptr->zstream.next_out = png_ptr->zbuf;
-
-   /* This is the same compression loop as in png_write_row() */
-   do
+   /* Set up the compression buffers, we need a loop here to avoid overflowing a
+    * uInt.  Use ZLIB_IO_MAX to limit the input.  The output is always limited
+    * by the output buffer size, so there is no need to check that.  Since this
+    * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
+    * in size.
+    */
    {
-      /* Compress the data */
-      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      png_compression_bufferp *end = &png_ptr->zbuffer_list;
+      png_alloc_size_t input_len = comp->input_len; /* may be zero! */
+      png_uint_32 output_len;
 
-      if (ret != Z_OK)
+      /* zlib updates these for us: */
+      png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);
+      png_ptr->zstream.avail_in = 0; /* Set below */
+      png_ptr->zstream.next_out = comp->output;
+      png_ptr->zstream.avail_out = sizeof comp->output;
+
+      output_len = png_ptr->zstream.avail_out;
+
+      do
       {
-         /* Error */
-         if (png_ptr->zstream.msg != NULL)
-            png_error(png_ptr, png_ptr->zstream.msg);
+         uInt avail_in = ZLIB_IO_MAX;
 
-         else
-            png_error(png_ptr, "zlib error");
-      }
+         if (avail_in > input_len)
+            avail_in = (uInt)input_len;
 
-      /* Check to see if we need more room */
-      if (!(png_ptr->zstream.avail_out))
-      {
-         /* Make sure the output array has room */
-         if (comp->num_output_ptr >= comp->max_output_ptr)
+         input_len -= avail_in;
+
+         png_ptr->zstream.avail_in = avail_in;
+
+         if (png_ptr->zstream.avail_out == 0)
          {
-            int old_max;
+            png_compression_buffer *next;
 
-            old_max = comp->max_output_ptr;
-            comp->max_output_ptr = comp->num_output_ptr + 4;
-            if (comp->output_ptr != NULL)
+            /* Chunk data is limited to 2^31 bytes in length, so the prefix
+             * length must be counted here.
+             */
+            if (output_len + prefix_len > PNG_UINT_31_MAX)
             {
-               png_bytepp old_ptr;
-
-               old_ptr = comp->output_ptr;
-
-               comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
-                   (png_alloc_size_t)
-                   (comp->max_output_ptr * png_sizeof(png_charpp)));
-
-               png_memcpy(comp->output_ptr, old_ptr, old_max
-                   * png_sizeof(png_charp));
-
-               png_free(png_ptr, old_ptr);
+               ret = Z_MEM_ERROR;
+               break;
             }
-            else
-               comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
-                   (png_alloc_size_t)
-                   (comp->max_output_ptr * png_sizeof(png_charp)));
-         }
 
-         /* Save the data */
-         comp->output_ptr[comp->num_output_ptr] =
-             (png_bytep)png_malloc(png_ptr,
-             (png_alloc_size_t)png_ptr->zbuf_size);
-
-         png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
-             png_ptr->zbuf_size);
-
-         comp->num_output_ptr++;
-
-         /* and reset the buffer */
-         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
-         png_ptr->zstream.next_out = png_ptr->zbuf;
-      }
-   /* Continue until we don't have any more to compress */
-   } while (png_ptr->zstream.avail_in);
-
-   /* Finish the compression */
-   do
-   {
-      /* Tell zlib we are finished */
-      ret = deflate(&png_ptr->zstream, Z_FINISH);
-
-      if (ret == Z_OK)
-      {
-         /* Check to see if we need more room */
-         if (!(png_ptr->zstream.avail_out))
-         {
-            /* Check to make sure our output array has room */
-            if (comp->num_output_ptr >= comp->max_output_ptr)
+            /* Need a new (malloc'ed) buffer, but there may be one present
+             * already.
+             */
+            next = *end;
+            if (next == NULL)
             {
-               int old_max;
+               next = png_voidcast(png_compression_bufferp, png_malloc_base
+                  (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
 
-               old_max = comp->max_output_ptr;
-               comp->max_output_ptr = comp->num_output_ptr + 4;
-               if (comp->output_ptr != NULL)
+               if (next == NULL)
                {
-                  png_bytepp old_ptr;
-
-                  old_ptr = comp->output_ptr;
-
-                  /* This could be optimized to realloc() */
-                  comp->output_ptr = png_voidcast(png_bytepp,png_malloc(png_ptr,
-                      comp->max_output_ptr * png_sizeof(png_charp)));
-
-                  png_memcpy(comp->output_ptr, old_ptr,
-                      old_max * png_sizeof(png_charp));
-
-                  png_free(png_ptr, old_ptr);
+                  ret = Z_MEM_ERROR;
+                  break;
                }
 
-               else
-                  comp->output_ptr = png_voidcast(png_bytepp,png_malloc(png_ptr,
-                      comp->max_output_ptr * png_sizeof(png_charp)));
+               /* Link in this buffer (so that it will be freed later) */
+               next->next = NULL;
+               *end = next;
             }
 
-            /* Save the data */
-            comp->output_ptr[comp->num_output_ptr] = png_voidcast(png_bytep,
-                png_malloc(png_ptr, png_ptr->zbuf_size));
+            png_ptr->zstream.next_out = next->output;
+            png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
+            output_len += png_ptr->zstream.avail_out;
 
-            png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
-                png_ptr->zbuf_size);
-
-            comp->num_output_ptr++;
-
-            /* and reset the buffer pointers */
-            png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
-            png_ptr->zstream.next_out = png_ptr->zbuf;
+            /* Move 'end' to the next buffer pointer. */
+            end = &next->next;
          }
+
+         /* Compress the data */
+         ret = deflate(&png_ptr->zstream,
+            input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
+
+         /* Claw back input data that was not consumed (because avail_in is
+          * reset above every time round the loop).
+          */
+         input_len += png_ptr->zstream.avail_in;
+         png_ptr->zstream.avail_in = 0; /* safety */
       }
-      else if (ret != Z_STREAM_END)
+      while (ret == Z_OK);
+
+      /* There may be some space left in the last output buffer, this needs to
+       * be subtracted from output_len.
+       */
+      output_len -= png_ptr->zstream.avail_out;
+      png_ptr->zstream.avail_out = 0; /* safety */
+      comp->output_len = output_len;
+
+      /* Now double check the output length, put in a custom message if it is
+       * too long.  Otherwise ensure the z_stream::msg pointer is set to
+       * something.
+       */
+      if (output_len + prefix_len >= PNG_UINT_31_MAX)
       {
-         /* We got an error */
-         if (png_ptr->zstream.msg != NULL)
-            png_error(png_ptr, png_ptr->zstream.msg);
-
-         else
-            png_error(png_ptr, "zlib error");
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");
+         ret = Z_MEM_ERROR;
       }
-   } while (ret != Z_STREAM_END);
 
-   /* Text length is number of buffers plus last buffer */
-   /* TODO: this ****WILL**** overflow */
-   text_len = png_ptr->zbuf_size * comp->num_output_ptr;
+      else
+         png_zstream_error(png_ptr, ret);
 
-   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
-      text_len += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+      /* Reset zlib for another zTXt/iTXt or image data */
+      png_ptr->zowner = 0;
 
-   return text_len;
+      /* The only success case is Z_STREAM_END, input_len must be 0, if not this
+       * is an internal error.
+       */
+      if (ret == Z_STREAM_END && input_len == 0)
+      {
+         /* Fix up the deflate header, if required */
+         optimize_cmf(comp->output, comp->input_len);
+
+         /* But Z_OK is returned, not Z_STREAM_END; this allows the claim
+          * function above to return Z_STREAM_END on an error (though it never
+          * does in the current versions of zlib.)
+          */
+         return Z_OK;
+      }
+
+      else
+         return ret;
+   }
 }
 
 /* Ship the compressed text out via chunk writes */
-static void /* PRIVATE */
+static void
 png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
 {
-   int i;
+   png_uint_32 output_len = comp->output_len;
+   png_const_bytep output = comp->output;
+   png_uint_32 avail = sizeof comp->output;
+   png_compression_buffer *next = png_ptr->zbuffer_list;
 
-   /* Handle the no-compression case */
-   if (comp->input)
+   for (;;)
    {
-      png_write_chunk_data(png_ptr, comp->input, comp->input_len);
+      if (avail > output_len)
+         avail = output_len;
 
-      return;
+      png_write_chunk_data(png_ptr, output, avail);
+
+      output_len -= avail;
+
+      if (output_len == 0 || next == NULL)
+         break;
+
+      avail = png_ptr->zbuffer_size;
+      output = next->output;
+      next = next->next;
    }
 
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
-   /* The zbuf_size test is because the code below doesn't work if zbuf_size is
-    * '1'; simply skip it to avoid memory overwrite.
-    */
-   if (comp->input_len >= 2 && comp->input_len < 16384 && png_ptr->zbuf_size > 1)
-   {
-      unsigned int z_cmf;  /* zlib compression method and flags */
-
-      /* Optimize the CMF field in the zlib stream.  This hack of the zlib
-       * stream is compliant to the stream specification.
-       */
-
-      if (comp->num_output_ptr)
-        z_cmf = comp->output_ptr[0][0];
-      else
-        z_cmf = png_ptr->zbuf[0];
-
-      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
-      {
-         unsigned int z_cinfo;
-         unsigned int half_z_window_size;
-         int reduced = 0;
-         png_size_t uncompressed_text_size = comp->input_len;
-
-         z_cinfo = z_cmf >> 4;
-         half_z_window_size = 1 << (z_cinfo + 7);
-
-         while (uncompressed_text_size <= half_z_window_size &&
-             half_z_window_size >= 256)
-         {
-            z_cinfo--;
-            half_z_window_size >>= 1;
-            reduced = 1;
-         }
-
-         if (reduced && !(png_ptr->flags & PNG_FLAG_ZSTREAM_CMF_FIXUP))
-            png_warning(png_ptr, "internal data size error (text)");
-
-         z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
-
-         if (comp->num_output_ptr)
-         {
-
-           if (comp->output_ptr[0][0] != z_cmf)
-           {
-              int tmp;
-
-              comp->output_ptr[0][0] = (png_byte)z_cmf;
-              tmp = comp->output_ptr[0][1] & 0xe0;
-              tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
-              comp->output_ptr[0][1] = (png_byte)tmp;
-           }
-         }
-         else
-         {
-            int tmp;
-
-            png_ptr->zbuf[0] = (png_byte)z_cmf;
-            tmp = png_ptr->zbuf[1] & 0xe0;
-            tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
-            png_ptr->zbuf[1] = (png_byte)tmp;
-         }
-      }
-
-      else
-         png_warning(png_ptr, /* must be a warning or the data isn't freed */
-             "Invalid zlib compression method or flags in non-IDAT chunk");
-   }
-#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
-
-   /* Write saved output buffers, if any */
-   /* WARNING: SIDE EFFECT; the code below is what actually frees the data */
-   for (i = 0; i < comp->num_output_ptr; i++)
-   {
-      png_write_chunk_data(png_ptr, comp->output_ptr[i],
-          (png_size_t)png_ptr->zbuf_size);
-
-      png_free(png_ptr, comp->output_ptr[i]);
-   }
-
-   if (comp->max_output_ptr != 0)
-      png_free(png_ptr, comp->output_ptr);
-
-   /* Write anything left in zbuf */
-   if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
-      png_write_chunk_data(png_ptr, png_ptr->zbuf,
-          (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out));
-
-   /* Reset zlib for another zTXt/iTXt or image data */
-   png_deflate_release(png_ptr);
+   /* This is an internal error; 'next' must have been NULL! */
+   if (output_len > 0)
+      png_error(png_ptr, "error writing ancilliary chunked compressed data");
 }
 #endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
 
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
+ *
+ * The 'new_key' buffer must be 80 characters in size (for the keyword plus a
+ * trailing '\0').  If this routine returns 0 then there was no keyword, or a
+ * valid one could not be generated, and the caller must png_error.
+ */
+static png_uint_32
+png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
+{
+   png_const_charp orig_key = key;
+   png_uint_32 key_len = 0;
+   int bad_character = 0;
+   int space = 1;
+
+   png_debug(1, "in png_check_keyword");
+
+   if (key == NULL)
+   {
+      *new_key = 0;
+      return 0;
+   }
+   
+   while (*key && key_len < 79)
+   {
+      png_byte ch = (png_byte)(0xff & *key++);
+
+      if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))
+         *new_key++ = ch, ++key_len, space = 0;
+
+      else if (!space)
+      {
+         /* A space or an invalid character when one wasn't seen immediately
+          * before; output just a space.
+          */
+         *new_key++ = 32, ++key_len, space = 1;
+
+         /* If the character was not a space then it is inalid. */
+         if (ch != 32)
+            bad_character = ch;
+      }
+
+      else if (!bad_character)
+         bad_character = ch; /* just skip it, record the first error */
+   }
+
+   if (key_len > 0 && space) /* trailing space */
+   {
+      --key_len, --new_key;
+      if (!bad_character)
+         bad_character = 32;
+   }
+
+   /* Terminate the keyword */
+   *new_key = 0;
+
+   if (key_len == 0)
+      return 0;
+
+   /* Try to only output one warning per keyword: */
+   if (*key) /* keyword too long */
+      png_warning(png_ptr, "keyword truncated");
+
+   else if (bad_character)
+   {
+      PNG_WARNING_PARAMETERS(p)
+
+      png_warning_parameter(p, 1, orig_key);
+      png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character);
+
+      png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
+   }
+
+   return key_len;
+}
+#endif
+
 /* Write the IHDR chunk, and update the png_struct with the necessary
  * information.  Note that the rest of this code depends upon this
  * information being correct.
@@ -958,78 +1000,158 @@
    png_ptr->mode |= PNG_HAVE_PLTE;
 }
 
-/* Write an IDAT chunk */
+/* This is similar to png_text_compress, above, except that it does not require
+ * all of the data at once and, instead of buffering the compressed result,
+ * writes it as IDAT chunks.  Unlike png_text_compress it *can* png_error out
+ * because it calls the write interface.  As a result it does its own error
+ * reporting and does not return an error code.  In the event of error it will
+ * just call png_error.  The input data length may exceed 32-bits.  The 'flush'
+ * parameter is exactly the same as that to deflate, with the following
+ * meanings:
+ *
+ * Z_NO_FLUSH: normal incremental output of compressed data
+ * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
+ * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
+ *
+ * The routine manages the acquire and release of the png_ptr->zstream by
+ * checking and (at the end) clearing png_ptr->zowner, it does some sanity
+ * checks on the 'mode' flags while doing this.
+ */
 void /* PRIVATE */
-png_write_IDAT(png_structrp png_ptr, png_bytep data, png_size_t length)
+png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
+   png_alloc_size_t input_len, int flush)
 {
-   png_debug(1, "in png_write_IDAT");
-
-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
-   if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
-       png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+   if (png_ptr->zowner != png_IDAT)
    {
-      /* Optimize the CMF field in the zlib stream.  This hack of the zlib
-       * stream is compliant to the stream specification.
+      /* First time.   Ensure we have a temporary buffer for compression and
+       * trim the buffer list if it has more than one entry to free memory.
        */
-      unsigned int z_cmf = data[0];  /* zlib compression method and flags */
-
-      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+      if (png_ptr->zbuffer_list == NULL)
       {
-         /* Avoid memory underflows and multiplication overflows.
-          *
-          * The conditions below are practically always satisfied;
-          * however, they still must be checked.
-          */
-         if (length >= 2 &&
-             png_ptr->height < 16384 && png_ptr->width < 16384)
-         {
-            unsigned int z_cinfo;
-            unsigned int half_z_window_size;
-            int reduced = 0;
-            png_alloc_size_t uncompressed_idat_size = png_image_size(png_ptr);
-
-            z_cinfo = z_cmf >> 4;
-            half_z_window_size = 1 << (z_cinfo + 7);
-
-            while (uncompressed_idat_size <= half_z_window_size &&
-                half_z_window_size >= 256)
-            {
-               z_cinfo--;
-               half_z_window_size >>= 1;
-               reduced = 1;
-            }
-
-            if (reduced && !(png_ptr->flags & PNG_FLAG_ZSTREAM_CMF_FIXUP))
-               png_warning(png_ptr, "internal data size error (IDAT)");
-
-            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
-
-            if (data[0] != z_cmf)
-            {
-               int tmp;
-               data[0] = (png_byte)z_cmf;
-               tmp = data[1] & 0xe0;
-               tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
-               data[1] = (png_byte)tmp;
-            }
-         }
+         png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
+            png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
+         png_ptr->zbuffer_list->next = NULL;
       }
 
       else
-         png_error(png_ptr,
-             "Invalid zlib compression method or flags in IDAT");
+         png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
+
+      /* It is a terminal error if we can't claim the zstream. */
+      if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg);
+
+      /* The output state is maintained in png_ptr->zstream, so it must be
+       * initialized here after the claim.
+       */
+      png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
+      png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
    }
-#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
 
-   png_write_complete_chunk(png_ptr, png_IDAT, data, length);
-   png_ptr->mode |= PNG_HAVE_IDAT;
-
-   /* Prior to 1.5.4 this code was replicated in every caller (except at the
-    * end, where it isn't technically necessary).  Since this function has
-    * flushed the data we can safely reset the zlib output buffer here.
+   /* Now loop reading and writing until all the input is consumed or an error
+    * terminates the operation.  The _out values are maintained across calls to
+    * this function, but the input must be reset each time.
     */
-   png_ptr->zstream.next_out = png_ptr->zbuf;
-   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
+   png_ptr->zstream.avail_in = 0; /* set below */
+   for (;;)
+   {
+      int ret;
+
+      /* INPUT: from the row data */
+      uInt avail = ZLIB_IO_MAX;
+
+      if (avail > input_len)
+         avail = (uInt)input_len; /* safe because of the check */
+
+      png_ptr->zstream.avail_in = avail;
+      input_len -= avail;
+
+      ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
+
+      /* Include as-yet unconsumed input */
+      input_len += png_ptr->zstream.avail_in;
+      png_ptr->zstream.avail_in = 0;
+
+      /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note
+       * that these two zstream fields are preserved across the calls, therefore
+       * there is no need to set these up on entry to the loop.
+       */
+      if (png_ptr->zstream.avail_out == 0)
+      {
+         png_bytep data = png_ptr->zbuffer_list->output;
+         uInt size = png_ptr->zbuffer_size;
+
+         /* Write an IDAT containing the data then reset the buffer.  The
+          * first IDAT may need deflate header optimization.
+          */
+#        ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+            if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+               png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+               optimize_cmf(data, png_image_size(png_ptr));
+#        endif
+
+         png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+         png_ptr->mode |= PNG_HAVE_IDAT;
+
+         png_ptr->zstream.next_out = data;
+         png_ptr->zstream.avail_out = size;
+
+         /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
+          * the same flush parameter until it has finished output, for NO_FLUSH
+          * it doesn't matter.
+          */
+         if (ret == Z_OK && flush != Z_NO_FLUSH)
+            continue;
+      }
+
+      /* The order of these checks doesn't matter much; it just effect which
+       * possible error might be detected if multiple things go wrong at once.
+       */
+      if (ret == Z_OK) /* most likely return code! */
+      {
+         /* If all the input has been consumed then just return.  If Z_FINISH
+          * was used as the flush parameter something has gone wrong if we get
+          * here.
+          */
+         if (input_len == 0)
+         {
+            if (flush == Z_FINISH)
+               png_error(png_ptr, "Z_OK on Z_FINISH with output space");
+
+            return;
+         }
+      }
+
+      else if (ret == Z_STREAM_END && flush == Z_FINISH)
+      {
+         /* This is the end of the IDAT data; any pending output must be
+          * flushed.  For small PNG files we may still be at the beginning.
+          */
+         png_bytep data = png_ptr->zbuffer_list->output;
+         uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
+
+#        ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+            if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+               png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+               optimize_cmf(data, png_image_size(png_ptr));
+#        endif
+
+         png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+         png_ptr->zstream.avail_out = 0;
+         png_ptr->zstream.next_out = NULL;
+         png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
+
+         png_ptr->zowner = 0; /* Release the stream */
+         return;
+      }
+
+      else
+      {
+         /* This is an error condition. */
+         png_zstream_error(png_ptr, ret);
+         png_error(png_ptr, png_ptr->zstream.msg);
+      }
+   }
 }
 
 /* Write an IEND chunk */
@@ -1081,10 +1203,9 @@
 png_write_iCCP(png_structrp png_ptr, png_const_charp name, int compression_type,
     png_const_bytep profile, png_uint_32 profile_len)
 {
-   png_size_t name_len, compressed_profile_len;
-   png_charp new_name;
+   png_uint_32 name_len;
+   png_byte new_name[81]; /* 1 byte for the compression byte */
    compression_state comp;
-   png_uint_32 embedded_profile_len = 0;
 
    png_debug(1, "in png_write_iCCP");
 
@@ -1100,64 +1221,36 @@
    if (profile_len & 0x03)
       png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
 
-   embedded_profile_len = png_get_uint_32(profile);
-
-   if (profile_len != embedded_profile_len)
-      png_error(png_ptr, "Profile length does not match profile");
-
-   if ((png_size_t)profile_len != profile_len)
    {
-      /* This isn't an application error, technically, but all the same do
-       * not short-change the app by writing a PNG without the profile!
-       */
-      png_error(png_ptr, "Profile length exceeds system limits");
+      png_uint_32 embedded_profile_len = png_get_uint_32(profile);
+
+      if (profile_len != embedded_profile_len)
+         png_error(png_ptr, "Profile length does not match profile");
    }
 
-   if ((name_len = png_check_keyword(png_ptr, name, &new_name)) == 0)
-      return;
+   name_len = png_check_keyword(png_ptr, name, new_name);
 
-   comp.num_output_ptr = 0;
-   comp.max_output_ptr = 0;
-   comp.output_ptr = NULL;
-   comp.input = NULL;
-   comp.input_len = 0;
+   if (name_len == 0)
+      png_error(png_ptr, "iCCP: invalid keyword");
 
-   compressed_profile_len = png_text_compress(png_ptr, (png_const_charp)profile,
-       (png_size_t)/*ok*/profile_len, PNG_COMPRESSION_TYPE_BASE, &comp);
-
-   /* 'name_len' is 1..79, so the following is safe: */
-   if (compressed_profile_len > PNG_UINT_31_MAX - name_len - 2)
-   {
-      png_free(png_ptr, new_name);
-
-      /* TODO: there is no 'compression_free' function */
-      if (comp.output_ptr)
-      {
-         int i;
-         for (i = 0; i < comp.num_output_ptr; i++)
-            png_free(png_ptr, comp.output_ptr[i]);
-         png_free(png_ptr, comp.output_ptr);
-      }
-
-      png_error(png_ptr, "Compressed profile exceeds PNG size limits");
-   }
+   new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;
 
    /* Make sure we include the NULL after the name and the compression type */
-   png_write_chunk_header(png_ptr, png_iCCP,
-       (png_uint_32)/*ok*/(name_len + compressed_profile_len + 2));
+   ++name_len;
 
-   new_name[name_len + 1] = 0x00;
+   png_text_compress_init(&comp, profile, profile_len);
 
-   png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2);
+   /* Allow for keyword terminator and compression byte */
+   if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
+      png_error(png_ptr, png_ptr->zstream.msg);
 
-   if (compressed_profile_len)
-   {
-      comp.input_len = compressed_profile_len;
-      png_write_compressed_data_out(png_ptr, &comp);
-   }
+   png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
+
+   png_write_chunk_data(png_ptr, new_name, name_len);
+
+   png_write_compressed_data_out(png_ptr, &comp);
 
    png_write_chunk_end(png_ptr);
-   png_free(png_ptr, new_name);
 }
 #endif
 
@@ -1166,8 +1259,8 @@
 void /* PRIVATE */
 png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
 {
-   png_size_t name_len;
-   png_charp new_name;
+   png_uint_32 name_len;
+   png_byte new_name[80];
    png_byte entrybuf[10];
    png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
    png_size_t palette_size = entry_size * spalette->nentries;
@@ -1178,8 +1271,10 @@
 
    png_debug(1, "in png_write_sPLT");
 
-   if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0)
-      return;
+   name_len = png_check_keyword(png_ptr, spalette->name, new_name);
+
+   if (name_len == 0)
+      png_error(png_ptr, "sPLT: invalid keyword");
 
    /* Make sure we include the NULL after the name */
    png_write_chunk_header(png_ptr, png_sPLT,
@@ -1212,7 +1307,7 @@
          png_save_uint_16(entrybuf + 8, ep->frequency);
       }
 
-      png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+      png_write_chunk_data(png_ptr, entrybuf, entry_size);
    }
 #else
    ep=spalette->entries;
@@ -1236,12 +1331,11 @@
          png_save_uint_16(entrybuf + 8, ep[i].frequency);
       }
 
-      png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+      png_write_chunk_data(png_ptr, entrybuf, entry_size);
    }
 #endif
 
    png_write_chunk_end(png_ptr);
-   png_free(png_ptr, new_name);
 }
 #endif
 
@@ -1496,151 +1590,21 @@
 }
 #endif
 
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
-    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
-/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
- * and if invalid, correct the keyword rather than discarding the entire
- * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
- * length, forbids leading or trailing whitespace, multiple internal spaces,
- * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
- *
- * The new_key is allocated to hold the corrected keyword and must be freed
- * by the calling routine.  This avoids problems with trying to write to
- * static keywords without having to have duplicate copies of the strings.
- */
-png_size_t /* PRIVATE */
-png_check_keyword(png_structrp png_ptr, png_const_charp key, png_charpp new_key)
-{
-   png_size_t key_len;
-   png_const_charp ikp;
-   png_charp kp, dp;
-   int kflag;
-   int kwarn=0;
-
-   png_debug(1, "in png_check_keyword");
-
-   *new_key = NULL;
-
-   if (key == NULL || (key_len = png_strlen(key)) == 0)
-   {
-      png_warning(png_ptr, "zero length keyword");
-      return ((png_size_t)0);
-   }
-
-   png_debug1(2, "Keyword to be checked is '%s'", key);
-
-   *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2));
-
-   if (*new_key == NULL)
-   {
-      png_warning(png_ptr, "Out of memory while procesing keyword");
-      return ((png_size_t)0);
-   }
-
-   /* Replace non-printing characters with a blank and print a warning */
-   for (ikp = key, dp = *new_key; *ikp != '\0'; ikp++, dp++)
-   {
-      if ((png_byte)*ikp < 0x20 ||
-         ((png_byte)*ikp > 0x7E && (png_byte)*ikp < 0xA1))
-      {
-         PNG_WARNING_PARAMETERS(p)
-
-         png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_02x,
-            (png_byte)*ikp);
-         png_formatted_warning(png_ptr, p, "invalid keyword character 0x@1");
-         *dp = ' ';
-      }
-
-      else
-      {
-         *dp = *ikp;
-      }
-   }
-   *dp = '\0';
-
-   /* Remove any trailing white space. */
-   kp = *new_key + key_len - 1;
-   if (*kp == ' ')
-   {
-      png_warning(png_ptr, "trailing spaces removed from keyword");
-
-      while (*kp == ' ')
-      {
-         *(kp--) = '\0';
-         key_len--;
-      }
-   }
-
-   /* Remove any leading white space. */
-   kp = *new_key;
-   if (*kp == ' ')
-   {
-      png_warning(png_ptr, "leading spaces removed from keyword");
-
-      while (*kp == ' ')
-      {
-         kp++;
-         key_len--;
-      }
-   }
-
-   png_debug1(2, "Checking for multiple internal spaces in '%s'", kp);
-
-   /* Remove multiple internal spaces. */
-   for (kflag = 0, dp = *new_key; *kp != '\0'; kp++)
-   {
-      if (*kp == ' ' && kflag == 0)
-      {
-         *(dp++) = *kp;
-         kflag = 1;
-      }
-
-      else if (*kp == ' ')
-      {
-         key_len--;
-         kwarn = 1;
-      }
-
-      else
-      {
-         *(dp++) = *kp;
-         kflag = 0;
-      }
-   }
-   *dp = '\0';
-   if (kwarn)
-      png_warning(png_ptr, "extra interior spaces removed from keyword");
-
-   if (key_len == 0)
-   {
-      png_free(png_ptr, *new_key);
-      png_warning(png_ptr, "Zero length keyword");
-   }
-
-   if (key_len > 79)
-   {
-      png_warning(png_ptr, "keyword length must be 1 - 79 characters");
-      (*new_key)[79] = '\0';
-      key_len = 79;
-   }
-
-   return (key_len);
-}
-#endif
-
 #ifdef PNG_WRITE_tEXt_SUPPORTED
 /* Write a tEXt chunk */
 void /* PRIVATE */
 png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
     png_size_t text_len)
 {
-   png_size_t key_len;
-   png_charp new_key;
+   png_uint_32 key_len;
+   png_byte new_key[80];
 
    png_debug(1, "in png_write_tEXt");
 
-   if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0)
-      return;
+   key_len = png_check_keyword(png_ptr, key, new_key);
+
+   if (key_len == 0)
+      png_error(png_ptr, "tEXt: invalid keyword");
 
    if (text == NULL || *text == '\0')
       text_len = 0;
@@ -1648,24 +1612,24 @@
    else
       text_len = png_strlen(text);
 
+   if (text_len > PNG_UINT_31_MAX - (key_len+1))
+      png_error(png_ptr, "tEXt: text too long");
+
    /* Make sure we include the 0 after the key */
    png_write_chunk_header(png_ptr, png_tEXt,
-       (png_uint_32)(key_len + text_len + 1));
+       (png_uint_32)/*checked above*/(key_len + text_len + 1));
    /*
     * We leave it to the application to meet PNG-1.0 requirements on the
     * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
     * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
     * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
     */
-   png_write_chunk_data(png_ptr, (png_bytep)new_key,
-       (png_size_t)(key_len + 1));
+   png_write_chunk_data(png_ptr, new_key, key_len + 1);
 
    if (text_len)
-      png_write_chunk_data(png_ptr, (png_const_bytep)text,
-          (png_size_t)text_len);
+      png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len);
 
    png_write_chunk_end(png_ptr);
-   png_free(png_ptr, new_key);
 }
 #endif
 
@@ -1675,55 +1639,45 @@
 png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
     png_size_t text_len, int compression)
 {
-   png_size_t key_len;
-   png_byte buf;
-   png_charp new_key;
+   png_uint_32 key_len;
+   png_byte new_key[81];
    compression_state comp;
 
    png_debug(1, "in png_write_zTXt");
+   PNG_UNUSED(text_len); /* Always use strlen */
 
-   comp.num_output_ptr = 0;
-   comp.max_output_ptr = 0;
-   comp.output_ptr = NULL;
-   comp.input = NULL;
-   comp.input_len = 0;
-
-   if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0)
+   if (compression == PNG_TEXT_COMPRESSION_NONE)
    {
-      png_free(png_ptr, new_key);
+      png_write_tEXt(png_ptr, key, text, 0);
       return;
    }
 
-   if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
-   {
-      png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
-      png_free(png_ptr, new_key);
-      return;
-   }
+   if (compression != PNG_TEXT_COMPRESSION_zTXt)
+      png_error(png_ptr, "zTXt: invalid compression type");
 
-   text_len = png_strlen(text);
+   key_len = png_check_keyword(png_ptr, key, new_key);
+
+   if (key_len == 0)
+      png_error(png_ptr, "zTXt: invalid keyword");
+
+   /* Add the compression method and 1 for the keyword separator. */
+   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
+   ++key_len;
 
    /* Compute the compressed data; do it now for the length */
-   text_len = png_text_compress(png_ptr, text, text_len, compression,
-       &comp);
+   png_text_compress_init(&comp, (png_const_bytep)text,
+      text == NULL ? 0 : strlen(text));
+
+   if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
+      png_error(png_ptr, png_ptr->zstream.msg);
 
    /* Write start of chunk */
-   png_write_chunk_header(png_ptr, png_zTXt,
-       (png_uint_32)(key_len+text_len + 2));
+   png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
 
    /* Write key */
-   png_write_chunk_data(png_ptr, (png_bytep)new_key,
-       (png_size_t)(key_len + 1));
-
-   png_free(png_ptr, new_key);
-
-   buf = (png_byte)compression;
-
-   /* Write compression */
-   png_write_chunk_data(png_ptr, &buf, (png_size_t)1);
+   png_write_chunk_data(png_ptr, new_key, key_len);
 
    /* Write the compressed data */
-   comp.input_len = text_len;
    png_write_compressed_data_out(png_ptr, &comp);
 
    /* Close the chunk */
@@ -1737,90 +1691,94 @@
 png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,
     png_const_charp lang, png_const_charp lang_key, png_const_charp text)
 {
-   png_size_t lang_len, key_len, lang_key_len, text_len;
-   png_charp new_lang;
-   png_charp new_key = NULL;
-   png_byte cbuf[2];
+   png_uint_32 key_len, prefix_len;
+   png_size_t lang_len, lang_key_len;
+   png_byte new_key[82];
    compression_state comp;
 
    png_debug(1, "in png_write_iTXt");
 
-   comp.num_output_ptr = 0;
-   comp.max_output_ptr = 0;
-   comp.output_ptr = NULL;
-   comp.input = NULL;
+   key_len = png_check_keyword(png_ptr, key, new_key);
+   
+   if (key_len == 0)
+      png_error(png_ptr, "iTXt: invalid keyword");
 
-   if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0)
-      return;
-
-   if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang)) == 0)
+   /* Set the compression flag */
+   switch (compression)
    {
-      png_warning(png_ptr, "Empty language field in iTXt chunk");
-      new_lang = NULL;
-      lang_len = 0;
+      case PNG_ITXT_COMPRESSION_NONE:
+      case PNG_TEXT_COMPRESSION_NONE:
+         compression = new_key[++key_len] = 0; /* no compression */
+         break;
+
+      case PNG_TEXT_COMPRESSION_zTXt:
+      case PNG_ITXT_COMPRESSION_zTXt:
+         compression = new_key[++key_len] = 1; /* compressed */
+         break;
+
+      default:
+         png_error(png_ptr, "iTXt: invalid compression");
    }
 
-   if (lang_key == NULL)
-      lang_key_len = 0;
-
-   else
-      lang_key_len = png_strlen(lang_key);
-
-   if (text == NULL)
-      text_len = 0;
-
-   else
-      text_len = png_strlen(text);
-
-   /* Compute the compressed data; do it now for the length */
-   text_len = png_text_compress(png_ptr, text, text_len, compression - 2,
-       &comp);
-
-
-   /* Make sure we include the compression flag, the compression byte,
-    * and the NULs after the key, lang, and lang_key parts
-    */
-
-   png_write_chunk_header(png_ptr, png_iTXt, (png_uint_32)(
-        5 /* comp byte, comp flag, terminators for key, lang and lang_key */
-        + key_len
-        + lang_len
-        + lang_key_len
-        + text_len));
+   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
+   ++key_len; /* for the keywod separator */
 
    /* We leave it to the application to meet PNG-1.0 requirements on the
     * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
-    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG, however,
+    * specifies that the text is UTF-8 and this really doesn't require any
+    * checking.
+    *
     * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    *
+    * TODO: validate the language tag correctly (see the spec.)
     */
-   png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1));
+   if (lang == NULL) lang = ""; /* empty language is valid */
+   lang_len = strlen(lang)+1;
+   if (lang_key == NULL) lang_key = ""; /* may be empty */
+   lang_key_len = strlen(lang_key)+1;
+   if (text == NULL) text = ""; /* may be empty */
 
-   /* Set the compression flag */
-   if (compression == PNG_ITXT_COMPRESSION_NONE ||
-       compression == PNG_TEXT_COMPRESSION_NONE)
-      cbuf[0] = 0;
+   prefix_len = key_len;
+   if (lang_len > PNG_UINT_31_MAX-prefix_len)
+      prefix_len = PNG_UINT_31_MAX;
+   else
+      prefix_len = (png_uint_32)(prefix_len + lang_len);
 
-   else /* compression == PNG_ITXT_COMPRESSION_zTXt */
-      cbuf[0] = 1;
+   if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
+      prefix_len = PNG_UINT_31_MAX;
+   else
+      prefix_len = (png_uint_32)(prefix_len + lang_key_len);
 
-   /* Set the compression method */
-   cbuf[1] = 0;
+   png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));
 
-   png_write_chunk_data(png_ptr, cbuf, (png_size_t)2);
+   if (compression)
+   {
+      if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg);
+   }
 
-   cbuf[0] = 0;
-   png_write_chunk_data(png_ptr, (new_lang ? (png_const_bytep)new_lang : cbuf),
-       (png_size_t)(lang_len + 1));
+   else
+   {
+      if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
+         png_error(png_ptr, "iTXt: uncompressed text too long");
+   }
 
-   png_write_chunk_data(png_ptr, (lang_key ? (png_const_bytep)lang_key : cbuf),
-       (png_size_t)(lang_key_len + 1));
+   png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
 
-   png_write_compressed_data_out(png_ptr, &comp);
+   png_write_chunk_data(png_ptr, new_key, key_len);
+
+   png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);
+
+   png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);
+
+   if (compression)
+      png_write_compressed_data_out(png_ptr, &comp);
+
+   else
+      png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.input_len);
 
    png_write_chunk_end(png_ptr);
-
-   png_free(png_ptr, new_key);
-   png_free(png_ptr, new_lang);
 }
 #endif
 
@@ -1851,18 +1809,25 @@
     png_int_32 X1, int type, int nparams, png_const_charp units,
     png_charpp params)
 {
-   png_size_t purpose_len, units_len, total_len;
+   png_uint_32 purpose_len;
+   png_size_t units_len, total_len;
    png_size_tp params_len;
    png_byte buf[10];
-   png_charp new_purpose;
+   png_byte new_purpose[80];
    int i;
 
    png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
 
    if (type >= PNG_EQUATION_LAST)
-      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+      png_error(png_ptr, "Unrecognized equation type for pCAL chunk");
 
-   purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
+   purpose_len = png_check_keyword(png_ptr, purpose, new_purpose);
+
+   if (purpose_len == 0)
+      png_error(png_ptr, "pCAL: invalid keyword");
+   
+   ++purpose_len; /* terminator */
+
    png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
    units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
    png_debug1(3, "pCAL units length = %d", (int)units_len);
@@ -1884,7 +1849,7 @@
 
    png_debug1(3, "pCAL total length = %d", (int)total_len);
    png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
-   png_write_chunk_data(png_ptr, (png_const_bytep)new_purpose, purpose_len);
+   png_write_chunk_data(png_ptr, new_purpose, purpose_len);
    png_save_int_32(buf, X0);
    png_save_int_32(buf + 4, X1);
    buf[8] = (png_byte)type;
@@ -1892,8 +1857,6 @@
    png_write_chunk_data(png_ptr, buf, (png_size_t)10);
    png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
 
-   png_free(png_ptr, new_purpose);
-
    for (i = 0; i < nparams; i++)
    {
       png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);
@@ -2090,10 +2053,6 @@
       png_ptr->num_rows = png_ptr->height;
       png_ptr->usr_width = png_ptr->width;
    }
-
-   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;
 }
 
 /* Internal use only.  Called when finished processing a row of data. */
@@ -2116,8 +2075,6 @@
    static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
 #endif
 
-   int ret;
-
    png_debug(1, "in png_write_finish_row");
 
    /* Next row */
@@ -2179,41 +2136,7 @@
 
    /* If we get here, we've just written the last row, so we need
       to flush the compressor */
-   do
-   {
-      /* Tell the compressor we are done */
-      ret = deflate(&png_ptr->zstream, Z_FINISH);
-
-      /* Check for an error */
-      if (ret == Z_OK)
-      {
-         /* Check to see if we need more room */
-         if (!(png_ptr->zstream.avail_out))
-         {
-            png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
-            png_ptr->zstream.next_out = png_ptr->zbuf;
-            png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
-         }
-      }
-
-      else if (ret != Z_STREAM_END)
-      {
-         if (png_ptr->zstream.msg != NULL)
-            png_error(png_ptr, png_ptr->zstream.msg);
-
-         else
-            png_error(png_ptr, "zlib error");
-      }
-   } while (ret != Z_STREAM_END);
-
-   /* Write any extra space */
-   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
-   {
-      png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
-          png_ptr->zstream.avail_out);
-   }
-
-   png_deflate_release(png_ptr);
+   png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);
 }
 
 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
@@ -3081,65 +3004,13 @@
 /* Do the actual writing of a previously filtered row. */
 static void
 png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
-   png_size_t avail/*includes filter byte*/)
+   png_size_t full_row_length/*includes filter byte*/)
 {
    png_debug(1, "in png_write_filtered_row");
 
    png_debug1(2, "filter = %d", filtered_row[0]);
-   /* Set up the zlib input buffer */
 
-   png_ptr->zstream.next_in = filtered_row;
-   png_ptr->zstream.avail_in = 0;
-   /* Repeat until we have compressed all the data */
-   do
-   {
-      int ret; /* Return of zlib */
-
-      /* Record the number of bytes available - zlib supports at least 65535
-       * bytes at one step, depending on the size of the zlib type 'uInt', the
-       * maximum size zlib can write at once is ZLIB_IO_MAX (from pngpriv.h).
-       * Use this because on 16 bit systems 'rowbytes' can be up to 65536 (i.e.
-       * one more than 16 bits) and, in this case 'rowbytes+1' can overflow a
-       * uInt.  ZLIB_IO_MAX can be safely reduced to cause zlib to be called
-       * with smaller chunks of data.
-       */
-      if (png_ptr->zstream.avail_in == 0)
-      {
-         if (avail > ZLIB_IO_MAX)
-         {
-            png_ptr->zstream.avail_in  = ZLIB_IO_MAX;
-            avail -= ZLIB_IO_MAX;
-         }
-
-         else
-         {
-            /* So this will fit in the available uInt space: */
-            png_ptr->zstream.avail_in = (uInt)avail;
-            avail = 0;
-         }
-      }
-
-      /* Compress the data */
-      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
-
-      /* Check for compression errors */
-      if (ret != Z_OK)
-      {
-         if (png_ptr->zstream.msg != NULL)
-            png_error(png_ptr, png_ptr->zstream.msg);
-
-         else
-            png_error(png_ptr, "zlib error");
-      }
-
-      /* See if it is time to write another IDAT */
-      if (!(png_ptr->zstream.avail_out))
-      {
-         /* Write the IDAT and reset the zlib output buffer */
-         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
-      }
-   /* Repeat until all data has been compressed */
-   } while (avail > 0 || png_ptr->zstream.avail_in > 0);
+   png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);
 
    /* Swap the current and previous rows */
    if (png_ptr->prev_row != NULL)
diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa
index 8eba0e2..cc8e786 100644
--- a/scripts/pnglibconf.dfa
+++ b/scripts/pnglibconf.dfa
@@ -516,6 +516,30 @@
 
 setting ZBUF_SIZE default 8192
 
+# This is the size of the decompression buffer used when counting or checking
+# the decompressed size of an LZ stream from a compressed ancilliary chunk; the
+# decompressed data is never used so a different size may be optimal.  This size
+# was determined using contrib/libtests/timepng.c with compressed zTXt data
+# around 11MByte in size.  Slight speed improvements (up to about 14% in
+# timepng) can be achieved by very large increases (to 32kbyte) on regular data,
+# but highly compressible data shows only around 2% improvement.   The size is
+# chosen to minimize the effects of DoS attacks based on using very large
+# amounts of highly compressible data.
+
+setting INFLATE_BUF_SIZE default 1024
+
+# This is the maximum amount of IDAT data that the sequential reader will
+# process at one time.  The setting does not affect the size of IDAT chunks
+# read, just the amount read at once.  Neither does it affect the progressive
+# reader, which processes just the amount of data the application gives it.
+# The sequential reader is currently unable to process more than one IDAT at
+# once - it has to read and process each one in turn.  There is no point setting
+# this to a value larger than the IDAT chunks typically encountered (it would
+# just waste memory) but there may be some point in reducing it below the value
+# of ZBUF_SIZE (the size of IDAT chunks written by libpng.)
+
+setting IDAT_READ_SIZE default PNG_ZBUF_SIZE
+
 # Ancillary chunks
 chunk bKGD
 chunk cHRM
diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt
index 40e547c..417b1c8 100644
--- a/scripts/pnglibconf.h.prebuilt
+++ b/scripts/pnglibconf.h.prebuilt
@@ -3,7 +3,7 @@
 
 /* pnglibconf.h - library build configuration */
 
-/* Libpng 1.6.0beta17 - March 6, 2012 */
+/* Libpng 1.6.0beta17 - March 9, 2012 */
 
 /* Copyright (c) 1998-2012 Glenn Randers-Pehrson */
 
@@ -26,6 +26,8 @@
 #define PNG_COST_SHIFT 3
 #define PNG_DEFAULT_READ_MACROS 1
 #define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
+#define PNG_INFLATE_BUF_SIZE 1024
 #define PNG_MAX_GAMMA_8 11
 #define PNG_QUANTIZE_BLUE_BITS 5
 #define PNG_QUANTIZE_GREEN_BITS 5