]> git.saurik.com Git - wxWidgets.git/blobdiff - src/png/pngrutil.c
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / png / pngrutil.c
index 52a73cfc394cc1a8dd66b69ffd3a9c3265f10315..01c3679dff3170a5991229f7c4cedaeffdf99b44 100644 (file)
@@ -1,8 +1,8 @@
 
 /* pngrutil.c - utilities to read a PNG file
  *
- * Last changed in libpng 1.5.7 [December 15, 2011]
- * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.2 [April 25, 2013]
+ * Copyright (c) 1998-2013 Glenn Randers-Pehrson
  * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
  * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
  *
@@ -21,7 +21,7 @@
 #define png_strtod(p,a,b) strtod(a,b)
 
 png_uint_32 PNGAPI
-png_get_uint_31(png_structp png_ptr, png_const_bytep buf)
+png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)
 {
    png_uint_32 uval = png_get_uint_32(buf);
 
@@ -40,7 +40,7 @@ png_get_uint_31(png_structp png_ptr, png_const_bytep buf)
 #define PNG_FIXED_ERROR (-1)
 
 static png_fixed_point /* PRIVATE */
-png_get_fixed_point(png_structp png_ptr, png_const_bytep buf)
+png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf)
 {
    png_uint_32 uval = png_get_uint_32(buf);
 
@@ -114,7 +114,7 @@ png_get_uint_16)(png_const_bytep buf)
 
 /* Read and check the PNG file signature */
 void /* PRIVATE */
-png_read_sig(png_structp png_ptr, png_infop info_ptr)
+png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
 {
    png_size_t num_checked, num_to_check;
 
@@ -149,7 +149,7 @@ png_read_sig(png_structp png_ptr, png_infop info_ptr)
  * Put the type name into png_ptr->chunk_name, and return the length.
  */
 png_uint_32 /* PRIVATE */
-png_read_chunk_header(png_structp png_ptr)
+png_read_chunk_header(png_structrp png_ptr)
 {
    png_byte buf[8];
    png_uint_32 length;
@@ -186,7 +186,7 @@ png_read_chunk_header(png_structp png_ptr)
 
 /* Read data, and (optionally) run it through the CRC. */
 void /* PRIVATE */
-png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
+png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length)
 {
    if (png_ptr == NULL)
       return;
@@ -196,29 +196,32 @@ png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
 }
 
 /* Optionally skip data and then check the CRC.  Depending on whether we
- * are reading a ancillary or critical chunk, and how the program has set
+ * are reading an ancillary or critical chunk, and how the program has set
  * things up, we may calculate the CRC on the data and print a message.
  * Returns '1' if there was a CRC error, '0' otherwise.
  */
 int /* PRIVATE */
-png_crc_finish(png_structp png_ptr, png_uint_32 skip)
+png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
 {
-   png_size_t i;
-   png_size_t istop = png_ptr->zbuf_size;
-
-   for (i = (png_size_t)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))
    {
-      if (PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name) ?
+      if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) ?
           !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) :
           (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))
       {
@@ -241,13 +244,13 @@ png_crc_finish(png_structp png_ptr, png_uint_32 skip)
  * the data it has read thus far.
  */
 int /* PRIVATE */
-png_crc_error(png_structp png_ptr)
+png_crc_error(png_structrp png_ptr)
 {
    png_byte crc_bytes[4];
    png_uint_32 crc;
    int need_crc = 1;
 
-   if (PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name))
+   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))
    {
       if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
           (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
@@ -277,252 +280,508 @@ png_crc_error(png_structp png_ptr)
       return (0);
 }
 
-#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
-static png_size_t
-png_inflate(png_structp png_ptr, png_bytep data, png_size_t size,
-    png_bytep output, png_size_t output_size)
+/* 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').
+ */
+static png_bytep
+png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn)
 {
-   png_size_t count = 0;
-
-   /* 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 is set below from 'size' */
-   png_ptr->zstream.avail_in = 0;
+   png_bytep buffer = png_ptr->read_buffer;
 
-   while (1)
+   if (buffer != NULL && new_size > png_ptr->read_buffer_size)
    {
-      int ret, avail;
+      png_ptr->read_buffer = NULL;
+      png_ptr->read_buffer = NULL;
+      png_ptr->read_buffer_size = 0;
+      png_free(png_ptr, buffer);
+      buffer = NULL;
+   }
 
-      /* 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 (memcpy of up to 32768 output bytes) every ZLIB_IO_MAX
-       * input bytes.
-       */
-      if (png_ptr->zstream.avail_in == 0 && size > 0)
+   if (buffer == NULL)
+   {
+      buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));
+
+      if (buffer != NULL)
       {
-         if (size <= ZLIB_IO_MAX)
-         {
-            /* The value is less than ZLIB_IO_MAX so the cast is safe: */
-            png_ptr->zstream.avail_in = (uInt)size;
-            size = 0;
-         }
+         png_ptr->read_buffer = buffer;
+         png_ptr->read_buffer_size = new_size;
+      }
 
+      else if (warn < 2) /* else silent */
+      {
+#ifdef PNG_WARNINGS_SUPPORTED
+         if (warn)
+             png_chunk_warning(png_ptr, "insufficient memory to read chunk");
          else
+#endif
          {
-            png_ptr->zstream.avail_in = ZLIB_IO_MAX;
-            size -= ZLIB_IO_MAX;
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+             png_chunk_error(png_ptr, "insufficient memory to read chunk");
+#endif
          }
       }
+   }
 
-      /* Reset the output buffer each time round - we empty it
-       * after every inflate call.
+   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, int window_bits)
+{
+   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.
        */
-      png_ptr->zstream.next_out = png_ptr->zbuf;
-      png_ptr->zstream.avail_out = png_ptr->zbuf_size;
+      (void)png_safecat(msg, (sizeof msg), 4, " using zstream");
+#     if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
+         png_chunk_warning(png_ptr, msg);
+         png_ptr->zowner = 0;
+#     else
+         png_chunk_error(png_ptr, msg);
+#     endif
+   }
 
-      ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
-      avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+   /* Implementation note: unlike 'png_deflate_claim' this internal function
+    * does not take the size of the data as an argument.  Some efficiency could
+    * be gained by using this when it is known *if* the zlib stream itself does
+    * not record the number; however, this is an illusion: the original writer
+    * of the PNG may have selected a lower window size, and we really must
+    * follow that because, for systems with with limited capabilities, we
+    * would otherwise reject the application's 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.
+    */
+   {
+      int ret; /* zlib return code */
 
-      /* First copy/count any new output - but only if we didn't
-       * get an error code.
+      /* Set this for safety, just in case the previous owner left pointers to
+       * memory allocations.
        */
-      if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0)
-      {
-         png_size_t space = avail; /* > 0, see above */
+      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 (output != 0 && output_size > count)
-         {
-            png_size_t copy = output_size - count;
+      if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
+      {
+#        if ZLIB_VERNUM < 0x1240
+            PNG_UNUSED(window_bits)
+            ret = inflateReset(&png_ptr->zstream);
+#        else
+            ret = inflateReset2(&png_ptr->zstream, window_bits);
+#        endif
+      }
 
-            if (space < copy)
-               copy = space;
+      else
+      {
+#        if ZLIB_VERNUM < 0x1240
+            ret = inflateInit(&png_ptr->zstream);
+#        else
+            ret = inflateInit2(&png_ptr->zstream, window_bits);
+#        endif
 
-            png_memcpy(output + count, png_ptr->zbuf, copy);
-         }
-         count += space;
+         if (ret == Z_OK)
+            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
       }
 
       if (ret == Z_OK)
-         continue;
+         png_ptr->zowner = owner;
 
-      /* Termination conditions - always reset the zstream, it
-       * must be left in inflateInit state.
+      else
+         png_zstream_error(png_ptr, ret);
+
+      return ret;
+   }
+}
+
+#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
+/* 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;
-      inflateReset(&png_ptr->zstream);
-
-      if (ret == Z_STREAM_END)
-         return count; /* NOTE: may be zero. */
+      png_ptr->zstream.avail_out = 0;
 
-      /* Now handle the error codes - the API always returns 0
-       * and the error message is dumped into the uncompressed
-       * buffer if available.
+      /* Read directly into the output if it is available (this is set to
+       * a local buffer below if output is NULL).
        */
-#     ifdef PNG_WARNINGS_SUPPORTED
+      if (output != NULL)
+         png_ptr->zstream.next_out = output;
+
+      do
       {
-         png_const_charp msg;
+         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 (memcpy of up to 32768 output bytes)
+          * every ZLIB_IO_MAX input bytes.
+          */
+         avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
 
-         if (png_ptr->zstream.msg != 0)
-            msg = png_ptr->zstream.msg;
+         avail = ZLIB_IO_MAX;
 
-         else switch (ret)
-         {
-            case Z_BUF_ERROR:
-               msg = "Buffer error in compressed datastream";
-               break;
+         if (avail_in < avail)
+            avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
 
-            case Z_DATA_ERROR:
-               msg = "Data error in compressed datastream";
-               break;
+         avail_in -= avail;
+         png_ptr->zstream.avail_in = avail;
 
-            default:
-               msg = "Incomplete compressed datastream";
-               break;
+         /* 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);
          }
 
-         png_chunk_warning(png_ptr, msg);
-      }
-#     endif
+         if (avail_out < avail)
+            avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
 
-      /* 0 means an error - notice that this code simply ignores
-       * zero length compressed chunks as a result.
+         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.
        */
-      return 0;
+      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
+   {
+      /* 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!
+       */
+      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
+      return Z_STREAM_ERROR;
    }
 }
 
 /*
- * 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).
  */
-void /* PRIVATE */
-png_decompress_chunk(png_structp png_ptr, int comp_type,
-    png_size_t chunklength,
-    png_size_t prefix_size, png_size_t *newlength)
+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*/)
 {
-   /* The caller should guarantee this */
-   if (prefix_size > chunklength)
-   {
-      /* The recovery is to delete the chunk. */
-      png_warning(png_ptr, "invalid chunklength");
-      prefix_size = 0; /* To delete everything */
-   }
+   /* TODO: implement different limits for different types of chunk.
+    *
+    * The caller supplies *newlength set to the maximum length of the
+    * uncompressed data, but this routine allocates space for the prefix and
+    * maybe a '\0' terminator too.  We have to assume that 'prefix_size' is
+    * limited only by the maximum chunk size.
+    */
+   png_alloc_size_t limit = PNG_SIZE_MAX;
+
+#  ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+      if (png_ptr->user_chunk_malloc_max > 0 &&
+         png_ptr->user_chunk_malloc_max < limit)
+         limit = png_ptr->user_chunk_malloc_max;
+#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
+      if (PNG_USER_CHUNK_MALLOC_MAX < limit)
+         limit = PNG_USER_CHUNK_MALLOC_MAX;
+#  endif
 
-   else if (comp_type == PNG_COMPRESSION_TYPE_BASE)
+   if (limit >= prefix_size + (terminate != 0))
    {
-      png_size_t expanded_size = png_inflate(png_ptr,
-          (png_bytep)(png_ptr->chunkdata + prefix_size),
-          chunklength - prefix_size,
-          0,            /* output */
-          0);           /* output size */
+      int ret;
 
-      /* Now check the limits on this chunk - if the limit fails the
-       * compressed data will be removed, the prefix will remain.
-       */
-#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
-      if (png_ptr->user_chunk_malloc_max &&
-          (prefix_size + expanded_size >= png_ptr->user_chunk_malloc_max - 1))
-#else
-#  ifdef PNG_USER_CHUNK_MALLOC_MAX
-      if ((PNG_USER_CHUNK_MALLOC_MAX > 0) &&
-          prefix_size + expanded_size >= PNG_USER_CHUNK_MALLOC_MAX - 1)
-#  endif
-#endif
-         png_warning(png_ptr, "Exceeded size limit while expanding chunk");
+      limit -= prefix_size + (terminate != 0);
+
+      if (limit < *newlength)
+         *newlength = limit;
 
-      /* If the size is zero either there was an error and a message
-       * has already been output (warning) or the size really is zero
-       * and we have nothing to do - the code will exit through the
-       * error case below.
+      /* Now try to claim the stream; the 'warn' setting causes zlib to be told
+       * to use the maximum window size during inflate; this hides errors in the
+       * deflate header window bits value which is used if '0' is passed.  In
+       * fact this only has an effect with zlib versions 1.2.4 and later - see
+       * the comments in png_inflate_claim above.
        */
-#if defined(PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED) || \
-    defined(PNG_USER_CHUNK_MALLOC_MAX)
-      else if (expanded_size > 0)
-#else
-      if (expanded_size > 0)
-#endif
+      ret = png_inflate_claim(png_ptr, png_ptr->chunk_name,
+         png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0);
+
+      if (ret == Z_OK)
       {
-         /* Success (maybe) - really uncompress the chunk. */
-         png_size_t new_size = 0;
-         png_charp text = (png_charp)png_malloc_warn(png_ptr,
-             prefix_size + expanded_size + 1);
+         png_uint_32 lzsize = chunklength - prefix_size;
 
-         if (text != NULL)
+         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)
          {
-            png_memcpy(text, png_ptr->chunkdata, prefix_size);
-            new_size = png_inflate(png_ptr,
-                (png_bytep)(png_ptr->chunkdata + prefix_size),
-                chunklength - prefix_size,
-                (png_bytep)(text + prefix_size), expanded_size);
-            text[prefix_size + expanded_size] = 0; /* just in case */
-
-            if (new_size == expanded_size)
+            /* Use 'inflateReset' here, not 'inflateReset2' because this
+             * preserves the previously decided window size (otherwise it would
+             * be necessary to store the previous window size.)  In practice
+             * this doesn't matter anyway, because png_inflate will call inflate
+             * with Z_FINISH in almost all cases, so the window will not be
+             * maintained.
+             */
+            if (inflateReset(&png_ptr->zstream) == Z_OK)
             {
-               png_free(png_ptr, png_ptr->chunkdata);
-               png_ptr->chunkdata = text;
-               *newlength = prefix_size + expanded_size;
-               return; /* The success return! */
+               /* Because of the limit checks above we know that the new,
+                * expanded, size will fit in a size_t (let alone an
+                * png_alloc_size_t).  Use png_malloc_base here to avoid an
+                * extra OOM message.
+                */
+               png_alloc_size_t new_size = *newlength;
+               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)
+               {
+                  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)
+                           memcpy(text, png_ptr->read_buffer, prefix_size);
+
+                        {
+                           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 */
+                        }
+                     }
+
+                     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;
+                     }
+                  }
+
+                  else if (ret == Z_OK)
+                     ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */
+
+                  /* 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);
+               }
             }
 
-            png_warning(png_ptr, "png_inflate logic error");
-            png_free(png_ptr, text);
+            else
+            {
+               /* inflateReset failed, store the error message */
+               png_zstream_error(png_ptr, ret);
+
+               if (ret == Z_STREAM_END)
+                  ret = PNG_UNEXPECTED_ZLIB_RETURN;
+            }
          }
 
-         else
-            png_warning(png_ptr, "Not enough memory to decompress chunk");
+         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;
    }
 
-   else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
+   else
    {
-      PNG_WARNING_PARAMETERS(p)
-      png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d, comp_type);
-      png_formatted_warning(png_ptr, p, "Unknown compression type @1");
-
-      /* The recovery is to simply drop the data. */
+      /* Application/configuration limits exceeded */
+      png_zstream_error(png_ptr, Z_MEM_ERROR);
+      return Z_MEM_ERROR;
    }
+}
+#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */
 
-   /* Generic error return - leave the prefix, delete the compressed
-    * data, reallocate the chunkdata to remove the potentially large
-    * amount of compressed data.
-    */
+#ifdef PNG_READ_iCCP_SUPPORTED
+/* Perform a partial read and decompress, producing 'avail_out' bytes and
+ * reading from the current chunk as required.
+ */
+static int
+png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size,
+   png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size,
+   int finish)
+{
+   if (png_ptr->zowner == png_ptr->chunk_name)
    {
-      png_charp text = (png_charp)png_malloc_warn(png_ptr, prefix_size + 1);
+      int ret;
+
+      /* next_in and avail_in must have been initialized by the caller. */
+      png_ptr->zstream.next_out = next_out;
+      png_ptr->zstream.avail_out = 0; /* set in the loop */
 
-      if (text != NULL)
+      do
       {
-         if (prefix_size > 0)
-            png_memcpy(text, png_ptr->chunkdata, prefix_size);
+         if (png_ptr->zstream.avail_in == 0)
+         {
+            if (read_size > *chunk_bytes)
+               read_size = (uInt)*chunk_bytes;
+            *chunk_bytes -= read_size;
+
+            if (read_size > 0)
+               png_crc_read(png_ptr, read_buffer, read_size);
 
-         png_free(png_ptr, png_ptr->chunkdata);
-         png_ptr->chunkdata = text;
+            png_ptr->zstream.next_in = read_buffer;
+            png_ptr->zstream.avail_in = read_size;
+         }
+
+         if (png_ptr->zstream.avail_out == 0)
+         {
+            uInt avail = ZLIB_IO_MAX;
+            if (avail > *out_size)
+               avail = (uInt)*out_size;
+            *out_size -= avail;
+
+            png_ptr->zstream.avail_out = avail;
+         }
 
-         /* This is an extra zero in the 'uncompressed' part. */
-         *(png_ptr->chunkdata + prefix_size) = 0x00;
+         /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all
+          * the available output is produced; this allows reading of truncated
+          * streams.
+          */
+         ret = inflate(&png_ptr->zstream,
+            *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
       }
-      /* Ignore a malloc error here - it is safe. */
+      while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0));
+
+      *out_size += png_ptr->zstream.avail_out;
+      png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */
+
+      /* Ensure the error message pointer is always set: */
+      png_zstream_error(png_ptr, ret);
+      return ret;
    }
 
-   *newlength = prefix_size;
+   else
+   {
+      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
+      return Z_STREAM_ERROR;
+   }
 }
-#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */
+#endif
 
 /* Read and check the IDHR chunk */
 void /* PRIVATE */
-png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[13];
    png_uint_32 width, height;
@@ -532,11 +791,11 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_IHDR");
 
    if (png_ptr->mode & PNG_HAVE_IHDR)
-      png_error(png_ptr, "Out of place IHDR");
+      png_chunk_error(png_ptr, "out of place");
 
    /* Check the length */
    if (length != 13)
-      png_error(png_ptr, "Invalid IHDR chunk");
+      png_chunk_error(png_ptr, "invalid");
 
    png_ptr->mode |= PNG_HAVE_IHDR;
 
@@ -597,7 +856,7 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
 /* Read and check the palette */
 void /* PRIVATE */
-png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_color palette[PNG_MAX_PALETTE_LENGTH];
    int num, i;
@@ -608,25 +867,32 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_PLTE");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before PLTE");
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   /* Moved to before the 'after IDAT' check below because otherwise duplicate
+    * PLTE chunks are potentially ignored (the spec says there shall not be more
+    * than one PLTE, the error is not treated as benign, so this check trumps
+    * the requirement that PLTE appears before IDAT.)
+    */
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      png_chunk_error(png_ptr, "duplicate");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid PLTE after IDAT");
+      /* This is benign because the non-benign error happened before, when an
+       * IDAT was encountered in a color-mapped image with no PLTE.
+       */
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
-   else if (png_ptr->mode & PNG_HAVE_PLTE)
-      png_error(png_ptr, "Duplicate PLTE chunk");
-
    png_ptr->mode |= PNG_HAVE_PLTE;
 
-   if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+   if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))
    {
-      png_warning(png_ptr,
-          "Ignoring PLTE chunk in grayscale PNG");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");
       return;
    }
 
@@ -640,19 +906,18 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
    if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
    {
+      png_crc_finish(png_ptr, length);
+
       if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-      {
-         png_warning(png_ptr, "Invalid palette chunk");
-         png_crc_finish(png_ptr, length);
-         return;
-      }
+         png_chunk_benign_error(png_ptr, "invalid");
 
       else
-      {
-         png_error(png_ptr, "Invalid palette chunk");
-      }
+         png_chunk_error(png_ptr, "invalid");
+
+      return;
    }
 
+   /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */
    num = (int)length / 3;
 
 #ifdef PNG_POINTER_INDEXING_SUPPORTED
@@ -697,6 +962,10 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
        * we have two options: an error abort, or a warning and we
        * ignore the data in this chunk (which should be OK, since
        * it's considered ancillary for a RGB or RGBA image).
+       *
+       * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the
+       * chunk type to determine whether to check the ancillary or the critical
+       * flags.
        */
       if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE))
       {
@@ -720,55 +989,74 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    }
 #endif
 
+   /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its
+    * own copy of the palette.  This has the side effect that when png_start_row
+    * is called (this happens after any call to png_read_update_info) the
+    * info_ptr palette gets changed.  This is extremely unexpected and
+    * confusing.
+    *
+    * Fix this by not sharing the palette in this way.
+    */
    png_set_PLTE(png_ptr, info_ptr, palette, num);
 
+   /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before
+    * IDAT.  Prior to 1.6.0 this was not checked; instead the code merely
+    * checked the apparent validity of a tRNS chunk inserted before PLTE on a
+    * palette PNG.  1.6.0 attempts to rigorously follow the standard and
+    * therefore does a benign error if the erroneous condition is detected *and*
+    * cancels the tRNS if the benign error returns.  The alternative is to
+    * amend the standard since it would be rather hypocritical of the standards
+    * maintainers to ignore it.
+    */
 #ifdef PNG_READ_tRNS_SUPPORTED
-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   if (png_ptr->num_trans > 0 ||
+      (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))
    {
-      if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
-      {
-         if (png_ptr->num_trans > (png_uint_16)num)
-         {
-            png_warning(png_ptr, "Truncating incorrect tRNS chunk length");
-            png_ptr->num_trans = (png_uint_16)num;
-         }
+      /* Cancel this because otherwise it would be used if the transforms
+       * require it.  Don't cancel the 'valid' flag because this would prevent
+       * detection of duplicate chunks.
+       */
+      png_ptr->num_trans = 0;
 
-         if (info_ptr->num_trans > (png_uint_16)num)
-         {
-            png_warning(png_ptr, "Truncating incorrect info tRNS chunk length");
-            info_ptr->num_trans = (png_uint_16)num;
-         }
-      }
+      if (info_ptr != NULL)
+         info_ptr->num_trans = 0;
+
+      png_chunk_benign_error(png_ptr, "tRNS must be after");
    }
 #endif
 
+#ifdef PNG_READ_hIST_SUPPORTED
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0)
+      png_chunk_benign_error(png_ptr, "hIST must be after");
+#endif
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0)
+      png_chunk_benign_error(png_ptr, "bKGD must be after");
+#endif
 }
 
 void /* PRIVATE */
-png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_debug(1, "in png_handle_IEND");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT))
-   {
-      png_error(png_ptr, "No image in file");
-   }
+      png_chunk_error(png_ptr, "out of place");
 
    png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
 
-   if (length != 0)
-   {
-      png_warning(png_ptr, "Incorrect IEND chunk length");
-   }
-
    png_crc_finish(png_ptr, length);
 
-   PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */
+   if (length != 0)
+      png_chunk_benign_error(png_ptr, "invalid");
+
+   PNG_UNUSED(info_ptr)
 }
 
 #ifdef PNG_READ_gAMA_SUPPORTED
 void /* PRIVATE */
-png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_fixed_point igamma;
    png_byte buf[4];
@@ -776,34 +1064,19 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_gAMA");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before gAMA");
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
    {
-      png_warning(png_ptr, "Invalid gAMA after IDAT");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   else if (png_ptr->mode & PNG_HAVE_PLTE)
-      /* Should be an error, but we can cope with it */
-      png_warning(png_ptr, "Out of place gAMA chunk");
-
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
-#ifdef PNG_READ_sRGB_SUPPORTED
-       && !(info_ptr->valid & PNG_INFO_sRGB)
-#endif
-       )
-   {
-      png_warning(png_ptr, "Duplicate gAMA chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    if (length != 4)
    {
-      png_warning(png_ptr, "Incorrect gAMA chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -814,43 +1087,16 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
    igamma = png_get_fixed_point(NULL, buf);
 
-   /* Check for zero gamma or an error. */
-   if (igamma <= 0)
-   {
-      png_warning(png_ptr,
-          "Ignoring gAMA chunk with out of range gamma");
-
-      return;
-   }
-
-#  ifdef PNG_READ_sRGB_SUPPORTED
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
-   {
-      if (PNG_OUT_OF_RANGE(igamma, 45500, 500))
-      {
-         PNG_WARNING_PARAMETERS(p)
-         png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, igamma);
-         png_formatted_warning(png_ptr, p,
-             "Ignoring incorrect gAMA value @1 when sRGB is also present");
-         return;
-      }
-   }
-#  endif /* PNG_READ_sRGB_SUPPORTED */
-
-#  ifdef PNG_READ_GAMMA_SUPPORTED
-   /* Gamma correction on read is supported. */
-   png_ptr->gamma = igamma;
-#  endif
-   /* And set the 'info' structure members. */
-   png_set_gAMA_fixed(png_ptr, info_ptr, igamma);
+   png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma);
+   png_colorspace_sync(png_ptr, info_ptr);
 }
 #endif
 
 #ifdef PNG_READ_sBIT_SUPPORTED
 void /* PRIVATE */
-png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_size_t truelen;
+   unsigned int truelen;
    png_byte buf[4];
 
    png_debug(1, "in png_handle_sBIT");
@@ -858,25 +1104,19 @@ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    buf[0] = buf[1] = buf[2] = buf[3] = 0;
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before sBIT");
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
    {
-      png_warning(png_ptr, "Invalid sBIT after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
-   else if (png_ptr->mode & PNG_HAVE_PLTE)
-   {
-      /* Should be an error, but we can cope with it */
-      png_warning(png_ptr, "Out of place sBIT chunk");
-   }
-
    if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT))
    {
-      png_warning(png_ptr, "Duplicate sBIT chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
@@ -884,11 +1124,11 @@ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
       truelen = 3;
 
    else
-      truelen = (png_size_t)png_ptr->channels;
+      truelen = png_ptr->channels;
 
    if (length != truelen || length > 4)
    {
-      png_warning(png_ptr, "Incorrect sBIT chunk length");
+      png_chunk_benign_error(png_ptr, "invalid");
       png_crc_finish(png_ptr, length);
       return;
    }
@@ -921,43 +1161,27 @@ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
 #ifdef PNG_READ_cHRM_SUPPORTED
 void /* PRIVATE */
-png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[32];
-   png_fixed_point x_white, y_white, x_red, y_red, x_green, y_green, x_blue,
-      y_blue;
+   png_xy xy;
 
    png_debug(1, "in png_handle_cHRM");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before cHRM");
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
    {
-      png_warning(png_ptr, "Invalid cHRM after IDAT");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   else if (png_ptr->mode & PNG_HAVE_PLTE)
-      /* Should be an error, but we can cope with it */
-      png_warning(png_ptr, "Out of place cHRM chunk");
-
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)
-#  ifdef PNG_READ_sRGB_SUPPORTED
-       && !(info_ptr->valid & PNG_INFO_sRGB)
-#  endif
-      )
-   {
-      png_warning(png_ptr, "Duplicate cHRM chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    if (length != 32)
    {
-      png_warning(png_ptr, "Incorrect cHRM chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -966,426 +1190,388 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    if (png_crc_finish(png_ptr, 0))
       return;
 
-   x_white = png_get_fixed_point(NULL, buf);
-   y_white = png_get_fixed_point(NULL, buf + 4);
-   x_red   = png_get_fixed_point(NULL, buf + 8);
-   y_red   = png_get_fixed_point(NULL, buf + 12);
-   x_green = png_get_fixed_point(NULL, buf + 16);
-   y_green = png_get_fixed_point(NULL, buf + 20);
-   x_blue  = png_get_fixed_point(NULL, buf + 24);
-   y_blue  = png_get_fixed_point(NULL, buf + 28);
-
-   if (x_white == PNG_FIXED_ERROR ||
-       y_white == PNG_FIXED_ERROR ||
-       x_red   == PNG_FIXED_ERROR ||
-       y_red   == PNG_FIXED_ERROR ||
-       x_green == PNG_FIXED_ERROR ||
-       y_green == PNG_FIXED_ERROR ||
-       x_blue  == PNG_FIXED_ERROR ||
-       y_blue  == PNG_FIXED_ERROR)
-   {
-      png_warning(png_ptr, "Ignoring cHRM chunk with negative chromaticities");
+   xy.whitex = png_get_fixed_point(NULL, buf);
+   xy.whitey = png_get_fixed_point(NULL, buf + 4);
+   xy.redx   = png_get_fixed_point(NULL, buf + 8);
+   xy.redy   = png_get_fixed_point(NULL, buf + 12);
+   xy.greenx = png_get_fixed_point(NULL, buf + 16);
+   xy.greeny = png_get_fixed_point(NULL, buf + 20);
+   xy.bluex  = png_get_fixed_point(NULL, buf + 24);
+   xy.bluey  = png_get_fixed_point(NULL, buf + 28);
+
+   if (xy.whitex == PNG_FIXED_ERROR ||
+       xy.whitey == PNG_FIXED_ERROR ||
+       xy.redx   == PNG_FIXED_ERROR ||
+       xy.redy   == PNG_FIXED_ERROR ||
+       xy.greenx == PNG_FIXED_ERROR ||
+       xy.greeny == PNG_FIXED_ERROR ||
+       xy.bluex  == PNG_FIXED_ERROR ||
+       xy.bluey  == PNG_FIXED_ERROR)
+   {
+      png_chunk_benign_error(png_ptr, "invalid values");
       return;
    }
 
-#ifdef PNG_READ_sRGB_SUPPORTED
-   if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB))
-   {
-      if (PNG_OUT_OF_RANGE(x_white, 31270,  1000) ||
-          PNG_OUT_OF_RANGE(y_white, 32900,  1000) ||
-          PNG_OUT_OF_RANGE(x_red,   64000,  1000) ||
-          PNG_OUT_OF_RANGE(y_red,   33000,  1000) ||
-          PNG_OUT_OF_RANGE(x_green, 30000,  1000) ||
-          PNG_OUT_OF_RANGE(y_green, 60000,  1000) ||
-          PNG_OUT_OF_RANGE(x_blue,  15000,  1000) ||
-          PNG_OUT_OF_RANGE(y_blue,   6000,  1000))
-      {
-         PNG_WARNING_PARAMETERS(p)
-
-         png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, x_white);
-         png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_fixed, y_white);
-         png_warning_parameter_signed(p, 3, PNG_NUMBER_FORMAT_fixed, x_red);
-         png_warning_parameter_signed(p, 4, PNG_NUMBER_FORMAT_fixed, y_red);
-         png_warning_parameter_signed(p, 5, PNG_NUMBER_FORMAT_fixed, x_green);
-         png_warning_parameter_signed(p, 6, PNG_NUMBER_FORMAT_fixed, y_green);
-         png_warning_parameter_signed(p, 7, PNG_NUMBER_FORMAT_fixed, x_blue);
-         png_warning_parameter_signed(p, 8, PNG_NUMBER_FORMAT_fixed, y_blue);
-
-         png_formatted_warning(png_ptr, p,
-             "Ignoring incorrect cHRM white(@1,@2) r(@3,@4)g(@5,@6)b(@7,@8) "
-             "when sRGB is also present");
-      }
+   /* If a colorspace error has already been output skip this chunk */
+   if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
       return;
-   }
-#endif /* PNG_READ_sRGB_SUPPORTED */
 
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-   /* Store the _white values as default coefficients for the rgb to gray
-    * operation if it is supported.  Check if the transform is already set to
-    * avoid destroying the transform values.
-    */
-   if (!png_ptr->rgb_to_gray_coefficients_set)
+   if (png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM)
    {
-      /* png_set_background has not been called and we haven't seen an sRGB
-       * chunk yet.  Find the XYZ of the three end points.
-       */
-      png_XYZ XYZ;
-      png_xy xy;
-
-      xy.redx = x_red;
-      xy.redy = y_red;
-      xy.greenx = x_green;
-      xy.greeny = y_green;
-      xy.bluex = x_blue;
-      xy.bluey = y_blue;
-      xy.whitex = x_white;
-      xy.whitey = y_white;
-
-      if (png_XYZ_from_xy_checked(png_ptr, &XYZ, xy))
-      {
-         /* The success case, because XYZ_from_xy normalises to a reference
-          * white Y of 1.0 we just need to scale the numbers.  This should
-          * always work just fine. It is an internal error if this overflows.
-          */
-         {
-            png_fixed_point r, g, b;
-            if (png_muldiv(&r, XYZ.redY, 32768, PNG_FP_1) &&
-               r >= 0 && r <= 32768 &&
-               png_muldiv(&g, XYZ.greenY, 32768, PNG_FP_1) &&
-               g >= 0 && g <= 32768 &&
-               png_muldiv(&b, XYZ.blueY, 32768, PNG_FP_1) &&
-               b >= 0 && b <= 32768 &&
-               r+g+b <= 32769)
-            {
-               /* We allow 0 coefficients here.  r+g+b may be 32769 if two or
-                * all of the coefficients were rounded up.  Handle this by
-                * reducing the *largest* coefficient by 1; this matches the
-                * approach used for the default coefficients in pngrtran.c
-                */
-               int add = 0;
-
-               if (r+g+b > 32768)
-                  add = -1;
-               else if (r+g+b < 32768)
-                  add = 1;
-
-               if (add != 0)
-               {
-                  if (g >= r && g >= b)
-                     g += add;
-                  else if (r >= g && r >= b)
-                     r += add;
-                  else
-                     b += add;
-               }
-
-               /* Check for an internal error. */
-               if (r+g+b != 32768)
-                  png_error(png_ptr,
-                     "internal error handling cHRM coefficients");
-
-               png_ptr->rgb_to_gray_red_coeff   = (png_uint_16)r;
-               png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g;
-            }
-
-            /* This is a png_error at present even though it could be ignored -
-             * it should never happen, but it is important that if it does, the
-             * bug is fixed.
-             */
-            else
-               png_error(png_ptr, "internal error handling cHRM->XYZ");
-         }
-      }
+      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
+      png_colorspace_sync(png_ptr, info_ptr);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
    }
-#endif
 
-   png_set_cHRM_fixed(png_ptr, info_ptr, x_white, y_white, x_red, y_red,
-      x_green, y_green, x_blue, y_blue);
+   png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
+   (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy,
+      1/*prefer cHRM values*/);
+   png_colorspace_sync(png_ptr, info_ptr);
 }
 #endif
 
 #ifdef PNG_READ_sRGB_SUPPORTED
 void /* PRIVATE */
-png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   int intent;
-   png_byte buf[1];
+   png_byte intent;
 
    png_debug(1, "in png_handle_sRGB");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before sRGB");
-
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
-   {
-      png_warning(png_ptr, "Invalid sRGB after IDAT");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   else if (png_ptr->mode & PNG_HAVE_PLTE)
-      /* Should be an error, but we can cope with it */
-      png_warning(png_ptr, "Out of place sRGB chunk");
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
+   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
    {
-      png_warning(png_ptr, "Duplicate sRGB chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    if (length != 1)
    {
-      png_warning(png_ptr, "Incorrect sRGB chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
-   png_crc_read(png_ptr, buf, 1);
+   png_crc_read(png_ptr, &intent, 1);
 
    if (png_crc_finish(png_ptr, 0))
       return;
 
-   intent = buf[0];
-
-   /* Check for bad intent */
-   if (intent >= PNG_sRGB_INTENT_LAST)
-   {
-      png_warning(png_ptr, "Unknown sRGB intent");
+   /* If a colorspace error has already been output skip this chunk */
+   if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
       return;
-   }
 
-#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA))
+   /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
+    * this.
+    */
+   if (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT)
    {
-      if (PNG_OUT_OF_RANGE(info_ptr->gamma, 45500, 500))
-      {
-         PNG_WARNING_PARAMETERS(p)
-
-         png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed,
-            info_ptr->gamma);
-
-         png_formatted_warning(png_ptr, p,
-             "Ignoring incorrect gAMA value @1 when sRGB is also present");
-      }
+      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
+      png_colorspace_sync(png_ptr, info_ptr);
+      png_chunk_benign_error(png_ptr, "too many profiles");
+      return;
    }
-#endif /* PNG_READ_gAMA_SUPPORTED */
-
-#ifdef PNG_READ_cHRM_SUPPORTED
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
-      if (PNG_OUT_OF_RANGE(info_ptr->x_white, 31270,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->y_white, 32900,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->x_red,   64000,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->y_red,   33000,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->x_green, 30000,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->y_green, 60000,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->x_blue,  15000,  1000) ||
-          PNG_OUT_OF_RANGE(info_ptr->y_blue,   6000,  1000))
-      {
-         png_warning(png_ptr,
-             "Ignoring incorrect cHRM value when sRGB is also present");
-      }
-#endif /* PNG_READ_cHRM_SUPPORTED */
 
-   /* This is recorded for use when handling the cHRM chunk above.  An sRGB
-    * chunk unconditionally overwrites the coefficients for grayscale conversion
-    * too.
-    */
-   png_ptr->is_sRGB = 1;
-
-#  ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-      /* Don't overwrite user supplied values: */
-      if (!png_ptr->rgb_to_gray_coefficients_set)
-      {
-         /* These numbers come from the sRGB specification (or, since one has to
-          * pay much money to get a copy, the wikipedia sRGB page) the
-          * chromaticity values quoted have been inverted to get the reverse
-          * transformation from RGB to XYZ and the 'Y' coefficients scaled by
-          * 32768 (then rounded).
-          *
-          * sRGB and ITU Rec-709 both truncate the values for the D65 white
-          * point to four digits and, even though it actually stores five
-          * digits, the PNG spec gives the truncated value.
-          *
-          * This means that when the chromaticities are converted back to XYZ
-          * end points we end up with (6968,23435,2366), which, as described in
-          * pngrtran.c, would overflow.  If the five digit precision and up is
-          * used we get, instead:
-          *
-          *    6968*R + 23435*G + 2365*B
-          *
-          * (Notice that this rounds the blue coefficient down, rather than the
-          * choice used in pngrtran.c which is to round the green one down.)
-          */
-         png_ptr->rgb_to_gray_red_coeff   =  6968; /* 0.212639005871510 */
-         png_ptr->rgb_to_gray_green_coeff = 23434; /* 0.715168678767756 */
-         /* png_ptr->rgb_to_gray_blue_coeff  =  2366; 0.072192315360734        */
-
-         /* The following keeps the cHRM chunk from destroying the
-          * coefficients again in the event that it follows the sRGB chunk.
-          */
-         png_ptr->rgb_to_gray_coefficients_set = 1;
-      }
-#  endif
-
-   png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent);
+   (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent);
+   png_colorspace_sync(png_ptr, info_ptr);
 }
 #endif /* PNG_READ_sRGB_SUPPORTED */
 
 #ifdef PNG_READ_iCCP_SUPPORTED
 void /* PRIVATE */
-png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
-/* Note: this does not properly handle chunks that are > 64K under DOS */
+png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+/* Note: this does not properly handle profiles that are > 64K under DOS */
 {
-   png_byte compression_type;
-   png_bytep pC;
-   png_charp profile;
-   png_uint_32 skip = 0;
-   png_uint_32 profile_size;
-   png_alloc_size_t profile_length;
-   png_size_t slength, prefix_length, data_length;
+   png_const_charp errmsg = NULL; /* error message output, or no error */
+   int finished = 0; /* crc checked */
 
    png_debug(1, "in png_handle_iCCP");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before iCCP");
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))
    {
-      png_warning(png_ptr, "Invalid iCCP after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
-   else if (png_ptr->mode & PNG_HAVE_PLTE)
-      /* Should be an error, but we can cope with it */
-      png_warning(png_ptr, "Out of place iCCP chunk");
-
-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP))
+   /* Consistent with all the above colorspace handling an obviously *invalid*
+    * chunk is just ignored, so does not invalidate the color space.  An
+    * alternative is to set the 'invalid' flags at the start of this routine
+    * and only clear them in they were not set before and all the tests pass.
+    * The minimum 'deflate' stream is assumed to be just the 2 byte header and 4
+    * byte checksum.  The keyword must be one character and there is a
+    * terminator (0) byte and the compression method.
+    */
+   if (length < 9)
    {
-      png_warning(png_ptr, "Duplicate iCCP chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too short");
       return;
    }
 
-#ifdef PNG_MAX_MALLOC_64K
-   if (length > (png_uint_32)65535L)
+   /* If a colorspace error has already been output skip this chunk */
+   if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)
    {
-      png_warning(png_ptr, "iCCP chunk too large to fit in memory");
-      skip = length - (png_uint_32)65535L;
-      length = (png_uint_32)65535L;
+      png_crc_finish(png_ptr, length);
+      return;
    }
-#endif
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
-
-   if (png_crc_finish(png_ptr, skip))
+   /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect
+    * this.
+    */
+   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0)
    {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+      uInt read_length, keyword_length;
+      char keyword[81];
 
-   png_ptr->chunkdata[slength] = 0x00;
+      /* Find the keyword; the keyword plus separator and compression method
+       * bytes can be at most 81 characters long.
+       */
+      read_length = 81; /* maximum */
+      if (read_length > length)
+         read_length = (uInt)length;
 
-   for (profile = png_ptr->chunkdata; *profile; profile++)
-      /* Empty loop to find end of name */ ;
+      png_crc_read(png_ptr, (png_bytep)keyword, read_length);
+      length -= read_length;
 
-   ++profile;
+      keyword_length = 0;
+      while (keyword_length < 80 && keyword_length < read_length &&
+         keyword[keyword_length] != 0)
+         ++keyword_length;
 
-   /* There should be at least one zero (the compression type byte)
-    * following the separator, and we should be on it
-    */
-   if (profile >= png_ptr->chunkdata + slength - 1)
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      png_warning(png_ptr, "Malformed iCCP chunk");
-      return;
-   }
+      /* TODO: make the keyword checking common */
+      if (keyword_length >= 1 && keyword_length <= 79)
+      {
+         /* We only understand '0' compression - deflate - so if we get a
+          * different value we can't safely decode the chunk.
+          */
+         if (keyword_length+1 < read_length &&
+            keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE)
+         {
+            read_length -= keyword_length+2;
 
-   /* Compression_type should always be zero */
-   compression_type = *profile++;
+            if (png_inflate_claim(png_ptr, png_iCCP,
+               png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0) == Z_OK)
+            {
+               Byte profile_header[132];
+               Byte local_buffer[PNG_INFLATE_BUF_SIZE];
+               png_alloc_size_t size = (sizeof profile_header);
 
-   if (compression_type)
-   {
-      png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk");
-      compression_type = 0x00;  /* Reset it to zero (libpng-1.0.6 through 1.0.8
-                                 wrote nonzero) */
-   }
+               png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2);
+               png_ptr->zstream.avail_in = read_length;
+               (void)png_inflate_read(png_ptr, local_buffer,
+                  (sizeof local_buffer), &length, profile_header, &size,
+                  0/*finish: don't, because the output is too small*/);
+
+               if (size == 0)
+               {
+                  /* We have the ICC profile header; do the basic header checks.
+                   */
+                  const png_uint_32 profile_length =
+                     png_get_uint_32(profile_header);
+
+                  if (png_icc_check_length(png_ptr, &png_ptr->colorspace,
+                     keyword, profile_length))
+                  {
+                     /* The length is apparently ok, so we can check the 132
+                      * byte header.
+                      */
+                     if (png_icc_check_header(png_ptr, &png_ptr->colorspace,
+                        keyword, profile_length, profile_header,
+                        png_ptr->color_type))
+                     {
+                        /* Now read the tag table; a variable size buffer is
+                         * needed at this point, allocate one for the whole
+                         * profile.  The header check has already validated
+                         * that none of these stuff will overflow.
+                         */
+                        const png_uint_32 tag_count = png_get_uint_32(
+                           profile_header+128);
+                        png_bytep profile = png_read_buffer(png_ptr,
+                           profile_length, 2/*silent*/);
+
+                        if (profile != NULL)
+                        {
+                           memcpy(profile, profile_header,
+                              (sizeof profile_header));
+
+                           size = 12 * tag_count;
+
+                           (void)png_inflate_read(png_ptr, local_buffer,
+                              (sizeof local_buffer), &length,
+                              profile + (sizeof profile_header), &size, 0);
+
+                           /* Still expect a a buffer error because we expect
+                            * there to be some tag data!
+                            */
+                           if (size == 0)
+                           {
+                              if (png_icc_check_tag_table(png_ptr,
+                                 &png_ptr->colorspace, keyword, profile_length,
+                                 profile))
+                              {
+                                 /* The profile has been validated for basic
+                                  * security issues, so read the whole thing in.
+                                  */
+                                 size = profile_length - (sizeof profile_header)
+                                    - 12 * tag_count;
+
+                                 (void)png_inflate_read(png_ptr, local_buffer,
+                                    (sizeof local_buffer), &length,
+                                    profile + (sizeof profile_header) +
+                                    12 * tag_count, &size, 1/*finish*/);
+
+                                 if (length > 0 && !(png_ptr->flags &
+                                       PNG_FLAG_BENIGN_ERRORS_WARN))
+                                    errmsg = "extra compressed data";
+
+                                 /* But otherwise allow extra data: */
+                                 else if (size == 0)
+                                 {
+                                    if (length > 0)
+                                    {
+                                       /* This can be handled completely, so
+                                        * keep going.
+                                        */
+                                       png_chunk_warning(png_ptr,
+                                          "extra compressed data");
+                                    }
+
+                                    png_crc_finish(png_ptr, length);
+                                    finished = 1;
+
+#                                   ifdef PNG_sRGB_SUPPORTED
+                                       /* Check for a match against sRGB */
+                                       png_icc_set_sRGB(png_ptr,
+                                          &png_ptr->colorspace, profile,
+                                          png_ptr->zstream.adler);
+#                                   endif
+
+                                    /* Steal the profile for info_ptr. */
+                                    if (info_ptr != NULL)
+                                    {
+                                       png_free_data(png_ptr, info_ptr,
+                                          PNG_FREE_ICCP, 0);
+
+                                       info_ptr->iccp_name = png_voidcast(char*,
+                                          png_malloc_base(png_ptr,
+                                          keyword_length+1));
+                                       if (info_ptr->iccp_name != NULL)
+                                       {
+                                          memcpy(info_ptr->iccp_name, keyword,
+                                             keyword_length+1);
+                                          info_ptr->iccp_proflen =
+                                             profile_length;
+                                          info_ptr->iccp_profile = profile;
+                                          png_ptr->read_buffer = NULL; /*steal*/
+                                          info_ptr->free_me |= PNG_FREE_ICCP;
+                                          info_ptr->valid |= PNG_INFO_iCCP;
+                                       }
+
+                                       else
+                                       {
+                                          png_ptr->colorspace.flags |=
+                                             PNG_COLORSPACE_INVALID;
+                                          errmsg = "out of memory";
+                                       }
+                                    }
+
+                                    /* else the profile remains in the read
+                                     * buffer which gets reused for subsequent
+                                     * chunks.
+                                     */
+
+                                    if (info_ptr != NULL)
+                                       png_colorspace_sync(png_ptr, info_ptr);
+
+                                    if (errmsg == NULL)
+                                    {
+                                       png_ptr->zowner = 0;
+                                       return;
+                                    }
+                                 }
+
+                                 else if (size > 0)
+                                    errmsg = "truncated";
+
+                                 else
+                                    errmsg = png_ptr->zstream.msg;
+                              }
+
+                              /* else png_icc_check_tag_table output an error */
+                           }
+
+                           else /* profile truncated */
+                              errmsg = png_ptr->zstream.msg;
+                        }
 
-   prefix_length = profile - png_ptr->chunkdata;
-   png_decompress_chunk(png_ptr, compression_type,
-       slength, prefix_length, &data_length);
+                        else
+                           errmsg = "out of memory";
+                     }
 
-   profile_length = data_length - prefix_length;
+                     /* else png_icc_check_header output an error */
+                  }
 
-   if (prefix_length > data_length || profile_length < 4)
-   {
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      png_warning(png_ptr, "Profile size field missing from iCCP chunk");
-      return;
-   }
+                  /* else png_icc_check_length output an error */
+               }
 
-   /* Check the profile_size recorded in the first 32 bits of the ICC profile */
-   pC = (png_bytep)(png_ptr->chunkdata + prefix_length);
-   profile_size = ((*(pC    )) << 24) |
-                  ((*(pC + 1)) << 16) |
-                  ((*(pC + 2)) <<  8) |
-                  ((*(pC + 3))      );
+               else /* profile truncated */
+                  errmsg = png_ptr->zstream.msg;
 
-   /* NOTE: the following guarantees that 'profile_length' fits into 32 bits,
-    * because profile_size is a 32 bit value.
-    */
-   if (profile_size < profile_length)
-      profile_length = profile_size;
+               /* Release the stream */
+               png_ptr->zowner = 0;
+            }
 
-   /* And the following guarantees that profile_size == profile_length. */
-   if (profile_size > profile_length)
-   {
-      PNG_WARNING_PARAMETERS(p)
+            else /* png_inflate_claim failed */
+               errmsg = png_ptr->zstream.msg;
+         }
 
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
+         else
+            errmsg = "bad compression method"; /* or missing */
+      }
 
-      png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_u, profile_size);
-      png_warning_parameter_unsigned(p, 2, PNG_NUMBER_FORMAT_u, profile_length);
-      png_formatted_warning(png_ptr, p,
-         "Ignoring iCCP chunk with declared size = @1 and actual length = @2");
-      return;
+      else
+         errmsg = "bad keyword";
    }
 
-   png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata,
-       compression_type, (png_bytep)png_ptr->chunkdata + prefix_length,
-       profile_size);
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
+   else
+      errmsg = "too many profiles";
+
+   /* Failure: the reason is in 'errmsg' */
+   if (!finished)
+      png_crc_finish(png_ptr, length);
+
+   png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
+   png_colorspace_sync(png_ptr, info_ptr);
+   if (errmsg != NULL) /* else already output */
+      png_chunk_benign_error(png_ptr, errmsg);
 }
 #endif /* PNG_READ_iCCP_SUPPORTED */
 
 #ifdef PNG_READ_sPLT_SUPPORTED
 void /* PRIVATE */
-png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+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;
 
    png_debug(1, "in png_handle_sPLT");
 
 #ifdef PNG_USER_LIMITS_SUPPORTED
-
    if (png_ptr->user_chunk_cache_max != 0)
    {
       if (png_ptr->user_chunk_cache_max == 1)
@@ -1404,54 +1590,52 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 #endif
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before sPLT");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid sPLT after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
 #ifdef PNG_MAX_MALLOC_64K
-   if (length > (png_uint_32)65535L)
+   if (length > 65535U)
    {
-      png_warning(png_ptr, "sPLT chunk too large to fit in memory");
-      skip = length - (png_uint_32)65535L;
-      length = (png_uint_32)65535L;
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too large to fit in memory");
+      return;
    }
 #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, 2/*silent*/);
+   if (buffer == NULL)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return;
+   }
+
 
    /* 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.
     */
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+   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;
-   }
 
-   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;
    }
@@ -1459,23 +1643,19 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    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;
    }
 
    dl = (png_int_32)(data_length / entry_size);
-   max_dl = PNG_SIZE_MAX / png_sizeof(png_sPLT_entry);
+   max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry));
 
    if (dl > max_dl)
    {
@@ -1486,7 +1666,7 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    new_palette.nentries = (png_int_32)(data_length / entry_size);
 
    new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
-       png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry));
+       png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)));
 
    if (new_palette.entries == NULL)
    {
@@ -1544,38 +1724,36 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 #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 */
 
 #ifdef PNG_READ_tRNS_SUPPORTED
 void /* PRIVATE */
-png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
 
    png_debug(1, "in png_handle_tRNS");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before tRNS");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid tRNS after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
    {
-      png_warning(png_ptr, "Duplicate tRNS chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
@@ -1585,8 +1763,8 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
       if (length != 2)
       {
-         png_warning(png_ptr, "Incorrect tRNS chunk length");
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "invalid");
          return;
       }
 
@@ -1601,12 +1779,12 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
       if (length != 6)
       {
-         png_warning(png_ptr, "Incorrect tRNS chunk length");
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "invalid");
          return;
       }
 
-      png_crc_read(png_ptr, buf, (png_size_t)length);
+      png_crc_read(png_ptr, buf, length);
       png_ptr->num_trans = 1;
       png_ptr->trans_color.red = png_get_uint_16(buf);
       png_ptr->trans_color.green = png_get_uint_16(buf + 2);
@@ -1617,33 +1795,28 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    {
       if (!(png_ptr->mode & PNG_HAVE_PLTE))
       {
-         /* Should be an error, but we can cope with it. */
-         png_warning(png_ptr, "Missing PLTE before tRNS");
-      }
-
-      if (length > (png_uint_32)png_ptr->num_palette ||
-          length > PNG_MAX_PALETTE_LENGTH)
-      {
-         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         /* TODO: is this actually an error in the ISO spec? */
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "out of place");
          return;
       }
 
-      if (length == 0)
+      if (length > png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH ||
+         length == 0)
       {
-         png_warning(png_ptr, "Zero length tRNS chunk");
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "invalid");
          return;
       }
 
-      png_crc_read(png_ptr, readbuf, (png_size_t)length);
+      png_crc_read(png_ptr, readbuf, length);
       png_ptr->num_trans = (png_uint_16)length;
    }
 
    else
    {
-      png_warning(png_ptr, "tRNS chunk not allowed with alpha channel");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid with alpha channel");
       return;
    }
 
@@ -1653,6 +1826,10 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
       return;
    }
 
+   /* TODO: this is a horrible side effect in the palette case because the
+    * png_struct ends up with a pointer to the tRNS buffer owned by the
+    * png_info.  Fix this.
+    */
    png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
        &(png_ptr->trans_color));
 }
@@ -1660,36 +1837,30 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
 #ifdef PNG_READ_bKGD_SUPPORTED
 void /* PRIVATE */
-png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_size_t truelen;
+   unsigned int truelen;
    png_byte buf[6];
    png_color_16 background;
 
    png_debug(1, "in png_handle_bKGD");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before bKGD");
-
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
-   {
-      png_warning(png_ptr, "Invalid bKGD after IDAT");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-       !(png_ptr->mode & PNG_HAVE_PLTE))
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) ||
+      (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+       !(png_ptr->mode & PNG_HAVE_PLTE)))
    {
-      png_warning(png_ptr, "Missing PLTE before bKGD");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD))
    {
-      png_warning(png_ptr, "Duplicate bKGD chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
@@ -1704,8 +1875,8 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
    if (length != truelen)
    {
-      png_warning(png_ptr, "Incorrect bKGD chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -1727,7 +1898,7 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
       {
          if (buf[0] >= info_ptr->num_palette)
          {
-            png_warning(png_ptr, "Incorrect bKGD chunk index value");
+            png_chunk_benign_error(png_ptr, "invalid index");
             return;
          }
 
@@ -1766,7 +1937,7 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
 #ifdef PNG_READ_hIST_SUPPORTED
 void /* PRIVATE */
-png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    unsigned int num, i;
    png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];
@@ -1774,36 +1945,28 @@ png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_hIST");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before hIST");
+      png_chunk_error(png_ptr, "missing IHDR");
 
-   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE))
    {
-      png_warning(png_ptr, "Invalid hIST after IDAT");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-
-   else if (!(png_ptr->mode & PNG_HAVE_PLTE))
-   {
-      png_warning(png_ptr, "Missing PLTE before hIST");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST))
    {
-      png_warning(png_ptr, "Duplicate hIST chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
    num = length / 2 ;
 
-   if (num != (unsigned int)png_ptr->num_palette || num >
-       (unsigned int)PNG_MAX_PALETTE_LENGTH)
+   if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH)
    {
-      png_warning(png_ptr, "Incorrect hIST chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -1824,7 +1987,7 @@ png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
 #ifdef PNG_READ_pHYs_SUPPORTED
 void /* PRIVATE */
-png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[9];
    png_uint_32 res_x, res_y;
@@ -1833,26 +1996,26 @@ png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_pHYs");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before pHYs");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid pHYs after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
    {
-      png_warning(png_ptr, "Duplicate pHYs chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
    if (length != 9)
    {
-      png_warning(png_ptr, "Incorrect pHYs chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -1870,7 +2033,7 @@ png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
 #ifdef PNG_READ_oFFs_SUPPORTED
 void /* PRIVATE */
-png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[9];
    png_int_32 offset_x, offset_y;
@@ -1879,26 +2042,26 @@ png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_oFFs");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before oFFs");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid oFFs after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
    {
-      png_warning(png_ptr, "Duplicate oFFs chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
    if (length != 9)
    {
-      png_warning(png_ptr, "Incorrect oFFs chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -1917,71 +2080,64 @@ png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 #ifdef PNG_READ_pCAL_SUPPORTED
 /* Read the pCAL chunk (described in the PNG Extensions document) */
 void /* PRIVATE */
-png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_int_32 X0, X1;
    png_byte type, nparams;
-   png_charp buf, units, endptr;
+   png_bytep buffer, buf, units, endptr;
    png_charpp params;
-   png_size_t slength;
    int i;
 
    png_debug(1, "in png_handle_pCAL");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before pCAL");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid pCAL after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL))
    {
-      png_warning(png_ptr, "Duplicate pCAL chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
    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, 2/*silent*/);
+
+   if (buffer == NULL)
    {
-      png_warning(png_ptr, "No memory for pCAL purpose");
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
       return;
    }
 
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+   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[slength] = 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 + slength;
+   endptr = buffer + length;
 
    /* We need to have at least 12 bytes after the purpose string
     * in order to get the parameter information.
     */
    if (endptr <= buf + 12)
    {
-      png_warning(png_ptr, "Invalid pCAL data");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -2001,15 +2157,13 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
        (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
        (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;
+      png_chunk_benign_error(png_ptr, "invalid parameter count");
       return;
    }
 
    else if (type >= PNG_EQUATION_LAST)
    {
-      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+      png_chunk_benign_error(png_ptr, "unrecognized equation type");
    }
 
    for (buf = units; *buf; buf++)
@@ -2017,43 +2171,37 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
    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 * (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");
+      png_chunk_benign_error(png_ptr, "out of memory");
       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);
+         png_chunk_benign_error(png_ptr, "invalid data");
          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
@@ -2061,67 +2209,61 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 #ifdef PNG_READ_sCAL_SUPPORTED
 /* Read the sCAL chunk */
 void /* PRIVATE */
-png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+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");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before sCAL");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (png_ptr->mode & PNG_HAVE_IDAT)
    {
-      png_warning(png_ptr, "Invalid sCAL after IDAT");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
       return;
    }
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL))
    {
-      png_warning(png_ptr, "Duplicate sCAL chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
    /* Need unit type, width, \0, height: minimum 4 bytes */
    else if (length < 4)
    {
-      png_warning(png_ptr, "sCAL chunk too short");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
    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, 2/*silent*/);
 
-   if (png_ptr->chunkdata == NULL)
+   if (buffer == NULL)
    {
-      png_warning(png_ptr, "Out of memory while processing sCAL chunk");
+      png_chunk_benign_error(png_ptr, "out of memory");
       png_crc_finish(png_ptr, length);
       return;
    }
 
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
-   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;
+      png_chunk_benign_error(png_ptr, "invalid unit");
       return;
    }
 
@@ -2131,41 +2273,36 @@ png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    i = 1;
    state = 0;
 
-   if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
-       i >= slength || png_ptr->chunkdata[i++] != 0)
-      png_warning(png_ptr, "Invalid sCAL chunk ignored: bad width format");
+   if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) ||
+       i >= length || buffer[i++] != 0)
+      png_chunk_benign_error(png_ptr, "bad width format");
 
    else if (!PNG_FP_IS_POSITIVE(state))
-      png_warning(png_ptr, "Invalid sCAL chunk ignored: non-positive width");
+      png_chunk_benign_error(png_ptr, "non-positive width");
 
    else
    {
       png_size_t heighti = i;
 
       state = 0;
-      if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
-          i != slength)
-         png_warning(png_ptr, "Invalid sCAL chunk ignored: bad height format");
+      if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) ||
+         i != length)
+         png_chunk_benign_error(png_ptr, "bad height format");
 
       else if (!PNG_FP_IS_POSITIVE(state))
-         png_warning(png_ptr,
-            "Invalid sCAL chunk ignored: non-positive height");
+         png_chunk_benign_error(png_ptr, "non-positive height");
 
       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
 
 #ifdef PNG_READ_tIME_SUPPORTED
 void /* PRIVATE */
-png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_byte buf[7];
    png_time mod_time;
@@ -2173,12 +2310,12 @@ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
    png_debug(1, "in png_handle_tIME");
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Out of place tIME chunk");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))
    {
-      png_warning(png_ptr, "Duplicate tIME chunk");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
       return;
    }
 
@@ -2187,8 +2324,8 @@ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
    if (length != 7)
    {
-      png_warning(png_ptr, "Incorrect tIME chunk length");
       png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
       return;
    }
 
@@ -2211,14 +2348,13 @@ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 #ifdef PNG_READ_tEXt_SUPPORTED
 /* Note: this does not properly handle chunks that are > 64K under DOS */
 void /* PRIVATE */
-png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+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;
-   png_size_t slength;
-   int ret;
 
    png_debug(1, "in png_handle_tEXt");
 
@@ -2233,84 +2369,59 @@ png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
-         png_warning(png_ptr, "No space in chunk cache for tEXt");
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "no space in chunk cache");
          return;
       }
    }
 #endif
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before tEXt");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    if (png_ptr->mode & PNG_HAVE_IDAT)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
 #ifdef PNG_MAX_MALLOC_64K
-   if (length > (png_uint_32)65535L)
+   if (length > 65535U)
    {
-      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
-      skip = length - (png_uint_32)65535L;
-      length = (png_uint_32)65535L;
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too large to fit in memory");
+      return;
    }
 #endif
 
-   png_free(png_ptr, png_ptr->chunkdata);
-
-   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, "No memory to process text chunk");
+     png_chunk_benign_error(png_ptr, "out of memory");
      return;
    }
 
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+   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[slength] = 0x00;
+   key = (png_charp)buffer;
+   key[length] = 0;
 
    for (text = key; *text; text++)
       /* Empty loop to find end of key */ ;
 
-   if (text != key + slength)
+   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 = 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
@@ -2318,13 +2429,11 @@ png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 #ifdef PNG_READ_zTXt_SUPPORTED
 /* Note: this does not correctly handle chunks that are > 64K under DOS */
 void /* PRIVATE */
-png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_textp text_ptr;
-   png_charp text;
-   int comp_type;
-   int ret;
-   png_size_t slength, prefix_len, data_len;
+   png_const_charp errmsg = NULL;
+   png_bytep       buffer;
+   png_uint_32     keyword_length;
 
    png_debug(1, "in png_handle_zTXt");
 
@@ -2339,123 +2448,101 @@ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
-         png_warning(png_ptr, "No space in chunk cache for zTXt");
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "no space in chunk cache");
          return;
       }
    }
 #endif
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before zTXt");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    if (png_ptr->mode & PNG_HAVE_IDAT)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-#ifdef PNG_MAX_MALLOC_64K
-   /* We will no doubt have problems with chunks even half this size, but
-    * there is no hard and fast rule to tell us where to stop.
-    */
-   if (length > (png_uint_32)65535L)
-   {
-      png_warning(png_ptr, "zTXt chunk too large to fit in memory");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-#endif
-
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   buffer = png_read_buffer(png_ptr, length, 2/*silent*/);
 
-   if (png_ptr->chunkdata == NULL)
+   if (buffer == NULL)
    {
-      png_warning(png_ptr, "Out of memory processing zTXt chunk");
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
       return;
    }
 
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+   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[slength] = 0x00;
+   /* TODO: also check that the keyword contents match the spec! */
+   for (keyword_length = 0;
+      keyword_length < length && buffer[keyword_length] != 0;
+      ++keyword_length)
+      /* Empty loop to find end of name */ ;
 
-   for (text = png_ptr->chunkdata; *text; text++)
-      /* Empty loop */ ;
+   if (keyword_length > 79 || keyword_length < 1)
+      errmsg = "bad keyword";
 
-   /* zTXt must have some text after the chunkdataword */
-   if (text >= png_ptr->chunkdata + slength - 2)
-   {
-      png_warning(png_ptr, "Truncated zTXt chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+   /* zTXt must have some LZ data after the keyword, although it may expand to
+    * zero bytes; we need a '\0' at the end of the keyword, the compression type
+    * then the LZ data:
+    */
+   else if (keyword_length + 3 > length)
+      errmsg = "truncated";
+
+   else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE)
+      errmsg = "unknown compression type";
 
    else
    {
-       comp_type = *(++text);
+      png_alloc_size_t uncompressed_length = PNG_SIZE_MAX;
 
-       if (comp_type != PNG_TEXT_COMPRESSION_zTXt)
-       {
-          png_warning(png_ptr, "Unknown compression type in zTXt chunk");
-          comp_type = PNG_TEXT_COMPRESSION_zTXt;
-       }
-
-       text++;        /* Skip the compression_method byte */
-   }
-
-   prefix_len = text - png_ptr->chunkdata;
-
-   png_decompress_chunk(png_ptr, comp_type,
-       (png_size_t)length, prefix_len, &data_len);
+      /* TODO: at present png_decompress_chunk imposes a single application
+       * level memory limit, this should be split to different values for iCCP
+       * and text chunks.
+       */
+      if (png_decompress_chunk(png_ptr, length, keyword_length+2,
+         &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+      {
+         png_text text;
 
-   text_ptr = (png_textp)png_malloc_warn(png_ptr,
-       png_sizeof(png_text));
+         /* 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.
+          */
+         buffer = png_ptr->read_buffer;
+         buffer[uncompressed_length+(keyword_length+2)] = 0;
+
+         text.compression = PNG_TEXT_COMPRESSION_zTXt;
+         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;
+         text.lang_key = NULL;
+
+         if (png_set_text_2(png_ptr, info_ptr, &text, 1))
+            errmsg = "insufficient memory";
+      }
 
-   if (text_ptr == NULL)
-   {
-      png_warning(png_ptr, "Not enough memory to process zTXt chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
+      else
+         errmsg = png_ptr->zstream.msg;
    }
 
-   text_ptr->compression = comp_type;
-   text_ptr->key = png_ptr->chunkdata;
-   text_ptr->lang = NULL;
-   text_ptr->lang_key = NULL;
-   text_ptr->itxt_length = 0;
-   text_ptr->text = png_ptr->chunkdata + prefix_len;
-   text_ptr->text_length = data_len;
-
-   ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
-
-   png_free(png_ptr, text_ptr);
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
-
-   if (ret)
-      png_error(png_ptr, "Insufficient memory to store zTXt chunk");
+   if (errmsg != NULL)
+      png_chunk_benign_error(png_ptr, errmsg);
 }
 #endif
 
 #ifdef PNG_READ_iTXt_SUPPORTED
 /* Note: this does not correctly handle chunks that are > 64K under DOS */
 void /* PRIVATE */
-png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_textp text_ptr;
-   png_charp key, lang, text, lang_key;
-   int comp_flag;
-   int comp_type = 0;
-   int ret;
-   png_size_t slength, prefix_len, data_len;
+   png_const_charp errmsg = NULL;
+   png_bytep buffer;
+   png_uint_32 prefix_length;
 
    png_debug(1, "in png_handle_iTXt");
 
@@ -2470,279 +2557,394 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
 
       if (--png_ptr->user_chunk_cache_max == 1)
       {
-         png_warning(png_ptr, "No space in chunk cache for iTXt");
          png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "no space in chunk cache");
          return;
       }
    }
 #endif
 
    if (!(png_ptr->mode & PNG_HAVE_IHDR))
-      png_error(png_ptr, "Missing IHDR before iTXt");
+      png_chunk_error(png_ptr, "missing IHDR");
 
    if (png_ptr->mode & PNG_HAVE_IDAT)
       png_ptr->mode |= PNG_AFTER_IDAT;
 
-#ifdef PNG_MAX_MALLOC_64K
-   /* We will no doubt have problems with chunks even half this size, but
-    * there is no hard and fast rule to tell us where to stop.
-    */
-   if (length > (png_uint_32)65535L)
-   {
-      png_warning(png_ptr, "iTXt chunk too large to fit in memory");
-      png_crc_finish(png_ptr, length);
-      return;
-   }
-#endif
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
 
-   png_free(png_ptr, png_ptr->chunkdata);
-   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 iTXt chunk");
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
       return;
    }
 
-   slength = (png_size_t)length;
-   png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+   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[slength] = 0x00;
-
-   for (lang = png_ptr->chunkdata; *lang; lang++)
+   /* First the keyword. */
+   for (prefix_length=0;
+      prefix_length < length && buffer[prefix_length] != 0;
+      ++prefix_length)
       /* Empty loop */ ;
 
-   lang++;        /* Skip NUL separator */
+   /* Perform a basic check on the keyword length here. */
+   if (prefix_length > 79 || prefix_length < 1)
+      errmsg = "bad keyword";
 
-   /* iTXt must have a language tag (possibly empty), two compression bytes,
-    * translated keyword (possibly empty), and possibly some text after the
-    * keyword
+   /* Expect keyword, compression flag, compression type, language, translated
+    * keyword (both may be empty but are 0 terminated) then the text, which may
+    * be empty.
     */
+   else if (prefix_length + 5 > length)
+      errmsg = "truncated";
 
-   if (lang >= png_ptr->chunkdata + slength - 3)
+   else if (buffer[prefix_length+1] == 0 ||
+      (buffer[prefix_length+1] == 1 &&
+      buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
    {
-      png_warning(png_ptr, "Truncated iTXt chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+      int compressed = buffer[prefix_length+1] != 0;
+      png_uint_32 language_offset, translated_keyword_offset;
+      png_alloc_size_t uncompressed_length = 0;
 
-   else
-   {
-      comp_flag = *lang++;
-      comp_type = *lang++;
-   }
+      /* Now the language tag */
+      prefix_length += 3;
+      language_offset = prefix_length;
 
-   if (comp_type || (comp_flag && comp_flag != PNG_TEXT_COMPRESSION_zTXt))
-   {
-      png_warning(png_ptr, "Unknown iTXt compression type or method");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+      for (; prefix_length < length && buffer[prefix_length] != 0;
+         ++prefix_length)
+         /* Empty loop */ ;
 
-   for (lang_key = lang; *lang_key; lang_key++)
-      /* Empty loop */ ;
+      /* WARNING: the length may be invalid here, this is checked below. */
+      translated_keyword_offset = ++prefix_length;
 
-   lang_key++;        /* Skip NUL separator */
+      for (; prefix_length < length && buffer[prefix_length] != 0;
+         ++prefix_length)
+         /* Empty loop */ ;
 
-   if (lang_key >= png_ptr->chunkdata + slength)
-   {
-      png_warning(png_ptr, "Truncated iTXt chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+      /* prefix_length should now be at the trailing '\0' of the translated
+       * keyword, but it may already be over the end.  None of this arithmetic
+       * can overflow because chunks are at most 2^31 bytes long, but on 16-bit
+       * systems the available allocaton may overflow.
+       */
+      ++prefix_length;
 
-   for (text = lang_key; *text; text++)
-      /* Empty loop */ ;
+      if (!compressed && prefix_length <= length)
+         uncompressed_length = length - prefix_length;
 
-   text++;        /* Skip NUL separator */
+      else if (compressed && prefix_length < length)
+      {
+         uncompressed_length = PNG_SIZE_MAX;
 
-   if (text >= png_ptr->chunkdata + slength)
-   {
-      png_warning(png_ptr, "Malformed iTXt chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+         /* TODO: at present png_decompress_chunk imposes a single application
+          * level memory limit, this should be split to different values for
+          * iCCP and text chunks.
+          */
+         if (png_decompress_chunk(png_ptr, length, prefix_length,
+            &uncompressed_length, 1/*terminate*/) == Z_STREAM_END)
+            buffer = png_ptr->read_buffer;
 
-   prefix_len = text - png_ptr->chunkdata;
+         else
+            errmsg = png_ptr->zstream.msg;
+      }
 
-   key=png_ptr->chunkdata;
+      else
+         errmsg = "truncated";
 
-   if (comp_flag)
-      png_decompress_chunk(png_ptr, comp_type,
-          (size_t)length, prefix_len, &data_len);
+      if (errmsg == NULL)
+      {
+         png_text text;
 
-   else
-      data_len = png_strlen(png_ptr->chunkdata + prefix_len);
+         buffer[uncompressed_length+prefix_length] = 0;
 
-   text_ptr = (png_textp)png_malloc_warn(png_ptr,
-       png_sizeof(png_text));
+         if (compressed)
+            text.compression = PNG_ITXT_COMPRESSION_NONE;
 
-   if (text_ptr == NULL)
-   {
-      png_warning(png_ptr, "Not enough memory to process iTXt chunk");
-      png_free(png_ptr, png_ptr->chunkdata);
-      png_ptr->chunkdata = NULL;
-      return;
-   }
+         else
+            text.compression = PNG_ITXT_COMPRESSION_zTXt;
 
-   text_ptr->compression = (int)comp_flag + 1;
-   text_ptr->lang_key = png_ptr->chunkdata + (lang_key - key);
-   text_ptr->lang = png_ptr->chunkdata + (lang - key);
-   text_ptr->itxt_length = data_len;
-   text_ptr->text_length = 0;
-   text_ptr->key = png_ptr->chunkdata;
-   text_ptr->text = png_ptr->chunkdata + prefix_len;
+         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;
 
-   ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+         if (png_set_text_2(png_ptr, info_ptr, &text, 1))
+            errmsg = "insufficient memory";
+      }
+   }
 
-   png_free(png_ptr, text_ptr);
-   png_free(png_ptr, png_ptr->chunkdata);
-   png_ptr->chunkdata = NULL;
+   else
+      errmsg = "bad compression info";
 
-   if (ret)
-      png_error(png_ptr, "Insufficient memory to store iTXt chunk");
+   if (errmsg != NULL)
+      png_chunk_benign_error(png_ptr, errmsg);
 }
 #endif
 
-/* This function is called when we haven't found a handler for a
- * chunk.  If there isn't a problem with the chunk itself (ie bad
- * chunk name, CRC, or a critical chunk), the chunk is silently ignored
- * -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which
- * case it will be saved away to be written out later.
- */
-void /* PRIVATE */
-png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+/* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */
+static int
+png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length)
 {
-   png_uint_32 skip = 0;
+   png_alloc_size_t limit = PNG_SIZE_MAX;
+
+   if (png_ptr->unknown_chunk.data != NULL)
+   {
+      png_free(png_ptr, png_ptr->unknown_chunk.data);
+      png_ptr->unknown_chunk.data = NULL;
+   }
 
-   png_debug(1, "in png_handle_unknown");
+#  ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+      if (png_ptr->user_chunk_malloc_max > 0 &&
+         png_ptr->user_chunk_malloc_max < limit)
+         limit = png_ptr->user_chunk_malloc_max;
 
-#ifdef PNG_USER_LIMITS_SUPPORTED
-   if (png_ptr->user_chunk_cache_max != 0)
+#  elif PNG_USER_CHUNK_MALLOC_MAX > 0
+      if (PNG_USER_CHUNK_MALLOC_MAX < limit)
+         limit = PNG_USER_CHUNK_MALLOC_MAX;
+#  endif
+
+   if (length <= limit)
    {
-      if (png_ptr->user_chunk_cache_max == 1)
-      {
-         png_crc_finish(png_ptr, length);
-         return;
-      }
+      PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name);
+      /* The following is safe because of the PNG_SIZE_MAX init above */
+      png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/;
+      /* 'mode' is a flag array, only the bottom four bits matter here */
+      png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/;
 
-      if (--png_ptr->user_chunk_cache_max == 1)
+      if (length == 0)
+         png_ptr->unknown_chunk.data = NULL;
+
+      else
       {
-         png_warning(png_ptr, "No space in chunk cache for unknown chunk");
-         png_crc_finish(png_ptr, length);
-         return;
+         /* Do a 'warn' here - it is handled below. */
+         png_ptr->unknown_chunk.data = png_voidcast(png_bytep,
+            png_malloc_warn(png_ptr, length));
       }
    }
-#endif
 
-   if (png_ptr->mode & PNG_HAVE_IDAT)
+   if (png_ptr->unknown_chunk.data == NULL && length > 0)
    {
-      if (png_ptr->chunk_name != png_IDAT)
-         png_ptr->mode |= PNG_AFTER_IDAT;
+      /* This is benign because we clean up correctly */
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits");
+      return 0;
    }
 
-   if (PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
+   else
    {
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-      if (png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name) !=
-          PNG_HANDLE_CHUNK_ALWAYS
-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-          && png_ptr->read_user_chunk_fn == NULL
-#endif
-          )
-#endif
-         png_chunk_error(png_ptr, "unknown critical chunk");
+      if (length > 0)
+         png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length);
+      png_crc_finish(png_ptr, 0);
+      return 1;
    }
+}
+#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
+
+/* Handle an unknown, or known but disabled, chunk */
+void /* PRIVATE */
+png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr,
+   png_uint_32 length, int keep)
+{
+   int handled = 0; /* the chunk was handled */
+
+   png_debug(1, "in png_handle_unknown");
 
 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
-   if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS)
-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-       || (png_ptr->read_user_chunk_fn != NULL)
-#endif
-       )
-   {
-#ifdef PNG_MAX_MALLOC_64K
-      if (length > 65535)
-      {
-         png_warning(png_ptr, "unknown chunk too large to fit in memory");
-         skip = length - 65535;
-         length = 65535;
-      }
-#endif
+   /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing
+    * the bug which meant that setting a non-default behavior for a specific
+    * chunk would be ignored (the default was always used unless a user
+    * callback was installed).
+    *
+    * 'keep' is the value from the png_chunk_unknown_handling, the setting for
+    * this specific chunk_name, if PNG_HANDLE_AS_UNKNOWN_SUPPORTED, if not it
+    * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here.
+    * This is just an optimization to avoid multiple calls to the lookup
+    * function.
+    */
+#  ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#     ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+         keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name);
+#     endif
+#  endif
 
-      /* TODO: this code is very close to the unknown handling in pngpread.c,
-       * maybe it can be put into a common utility routine?
-       * png_struct::unknown_chunk is just used as a temporary variable, along
-       * with the data into which the chunk is read.  These can be eliminated.
+   /* One of the following methods will read the chunk or skip it (at least one
+    * of these is always defined because this is the only way to switch on
+    * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+    */
+#  ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+      /* The user callback takes precedence over the chunk keep value, but the
+       * keep value is still required to validate a save of a critical chunk.
        */
-      PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name);
-      png_ptr->unknown_chunk.size = (png_size_t)length;
+      if (png_ptr->read_user_chunk_fn != NULL)
+      {
+         if (png_cache_unknown_chunk(png_ptr, length))
+         {
+            /* Callback to user unknown chunk handler */
+            int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr,
+               &png_ptr->unknown_chunk);
+
+            /* ret is:
+             * negative: An error occured, png_chunk_error will be called.
+             *     zero: The chunk was not handled, the chunk will be discarded
+             *           unless png_set_keep_unknown_chunks has been used to set
+             *           a 'keep' behavior for this particular chunk, in which
+             *           case that will be used.  A critical chunk will cause an
+             *           error at this point unless it is to be saved.
+             * positive: The chunk was handled, libpng will ignore/discard it.
+             */
+            if (ret < 0)
+               png_chunk_error(png_ptr, "error in user chunk");
 
-      if (length == 0)
-         png_ptr->unknown_chunk.data = NULL;
+            else if (ret == 0)
+            {
+               /* If the keep value is 'default' or 'never' override it, but
+                * still error out on critical chunks unless the keep value is
+                * 'always'  While this is weird it is the behavior in 1.4.12.
+                * A possible improvement would be to obey the value set for the
+                * chunk, but this would be an API change that would probably
+                * damage some applications.
+                *
+                * The png_app_warning below catches the case that matters, where
+                * the application has not set specific save or ignore for this
+                * chunk or global save or ignore.
+                */
+               if (keep < PNG_HANDLE_CHUNK_IF_SAFE)
+               {
+#                 ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+                     if (png_ptr->unknown_default < PNG_HANDLE_CHUNK_IF_SAFE)
+                     {
+                        png_chunk_warning(png_ptr, "Saving unknown chunk:");
+                        png_app_warning(png_ptr,
+                           "forcing save of an unhandled chunk;"
+                           " please call png_set_keep_unknown_chunks");
+                           /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */
+                     }
+#                 endif
+                  keep = PNG_HANDLE_CHUNK_IF_SAFE;
+               }
+            }
+
+            else /* chunk was handled */
+            {
+               handled = 1;
+               /* Critical chunks can be safely discarded at this point. */
+               keep = PNG_HANDLE_CHUNK_NEVER;
+            }
+         }
+
+         else
+            keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */
+      }
 
       else
+         /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */
+#  endif /* PNG_READ_USER_CHUNKS_SUPPORTED */
+
+#  ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
       {
-         png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length);
-         png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length);
+         /* keep is currently just the per-chunk setting, if there was no
+          * setting change it to the global default now (not that this may
+          * still be AS_DEFAULT) then obtain the cache of the chunk if required,
+          * if not simply skip the chunk.
+          */
+         if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+            keep = png_ptr->unknown_default;
+
+         if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
+             PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
+         {
+            if (!png_cache_unknown_chunk(png_ptr, length))
+               keep = PNG_HANDLE_CHUNK_NEVER;
+         }
+
+         else
+            png_crc_finish(png_ptr, length);
       }
+#  else
+#     ifndef PNG_READ_USER_CHUNKS_SUPPORTED
+#        error no method to support READ_UNKNOWN_CHUNKS
+#     endif
 
-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
-      if (png_ptr->read_user_chunk_fn != NULL)
       {
-         /* Callback to user unknown chunk handler */
-         int ret;
-
-         ret = (*(png_ptr->read_user_chunk_fn))
-             (png_ptr, &png_ptr->unknown_chunk);
+         /* If here there is no read callback pointer set and no support is
+          * compiled in to just save the unknown chunks, so simply skip this
+          * chunk.  If 'keep' is something other than AS_DEFAULT or NEVER then
+          * the app has erroneously asked for unknown chunk saving when there
+          * is no support.
+          */
+         if (keep > PNG_HANDLE_CHUNK_NEVER)
+            png_app_error(png_ptr, "no unknown chunk support available");
 
-         if (ret < 0)
-            png_chunk_error(png_ptr, "error in user chunk");
+         png_crc_finish(png_ptr, length);
+      }
+#  endif
 
-         if (ret == 0)
+#  ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+      /* Now store the chunk in the chunk list if appropriate, and if the limits
+       * permit it.
+       */
+      if (keep == PNG_HANDLE_CHUNK_ALWAYS ||
+         (keep == PNG_HANDLE_CHUNK_IF_SAFE &&
+          PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)))
+      {
+#     ifdef PNG_USER_LIMITS_SUPPORTED
+         switch (png_ptr->user_chunk_cache_max)
          {
-            if (PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
-            {
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-               if (png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name) !=
-                   PNG_HANDLE_CHUNK_ALWAYS)
-#endif
-                  png_chunk_error(png_ptr, "unknown critical chunk");
-            }
+            case 2:
+               png_ptr->user_chunk_cache_max = 1;
+               png_chunk_benign_error(png_ptr, "no space in chunk cache");
+               /* FALL THROUGH */
+            case 1:
+               /* NOTE: prior to 1.6.0 this case resulted in an unknown critical
+                * chunk being skipped, now there will be a hard error below.
+                */
+               break;
 
-            png_set_unknown_chunks(png_ptr, info_ptr,
-                &png_ptr->unknown_chunk, 1);
+            default: /* not at limit */
+               --(png_ptr->user_chunk_cache_max);
+               /* FALL THROUGH */
+            case 0: /* no limit */
+#     endif /* PNG_USER_LIMITS_SUPPORTED */
+               /* Here when the limit isn't reached or when limits are compiled
+                * out; store the chunk.
+                */
+               png_set_unknown_chunks(png_ptr, info_ptr,
+                  &png_ptr->unknown_chunk, 1);
+               handled = 1;
+#     ifdef PNG_USER_LIMITS_SUPPORTED
+               break;
          }
+#     endif
       }
+#  else /* no store support! */
+      PNG_UNUSED(info_ptr)
+#     error untested code (reading unknown chunks with no store support)
+#  endif
 
-      else
-#endif
-         png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1);
-
+   /* Regardless of the error handling below the cached data (if any) can be
+    * freed now.  Notice that the data is not freed if there is a png_error, but
+    * it will be freed by destroy_read_struct.
+    */
+   if (png_ptr->unknown_chunk.data != NULL)
       png_free(png_ptr, png_ptr->unknown_chunk.data);
-      png_ptr->unknown_chunk.data = NULL;
-   }
+   png_ptr->unknown_chunk.data = NULL;
 
-   else
-#endif
-      skip = length;
-
-   png_crc_finish(png_ptr, skip);
+#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
+   /* There is no support to read an unknown chunk, so just skip it. */
+   png_crc_finish(png_ptr, length);
+   PNG_UNUSED(info_ptr)
+   PNG_UNUSED(keep)
+#endif /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */
 
-#ifndef PNG_READ_USER_CHUNKS_SUPPORTED
-   PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */
-#endif
+   /* Check for unhandled critical chunks */
+   if (!handled && PNG_CHUNK_CRITICAL(png_ptr->chunk_name))
+      png_chunk_error(png_ptr, "unhandled critical chunk");
 }
 
 /* This function is called to verify that a chunk name is valid.
@@ -2758,7 +2960,7 @@ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
  */
 
 void /* PRIVATE */
-png_check_chunk_name(png_structp png_ptr, png_uint_32 chunk_name)
+png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name)
 {
    int i;
 
@@ -2783,7 +2985,7 @@ png_check_chunk_name(png_structp png_ptr, png_uint_32 chunk_name)
  * 'display' is false only those pixels present in the pass are filled in.
  */
 void /* PRIVATE */
-png_combine_row(png_structp png_ptr, png_bytep dp, int display)
+png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display)
 {
    unsigned int pixel_depth = png_ptr->transformed_pixel_depth;
    png_const_bytep sp = png_ptr->row_buf + 1;
@@ -2833,7 +3035,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
       /* end_mask is now the bits to *keep* from the destination row */
    }
 
-   /* For non-interlaced images this reduces to a png_memcpy(). A png_memcpy()
+   /* For non-interlaced images this reduces to a memcpy(). A memcpy()
     * will also happen if interlacing isn't supported or if the application
     * does not call png_set_interlace_handling().  In the latter cases the
     * caller just gets a sequence of the unexpanded rows from each interlace
@@ -3134,26 +3336,27 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
                /* Check for double byte alignment and, if possible, use a
                 * 16-bit copy.  Don't attempt this for narrow images - ones that
                 * are less than an interlace panel wide.  Don't attempt it for
-                * wide bytes_to_copy either - use the png_memcpy there.
+                * wide bytes_to_copy either - use the memcpy there.
                 */
-               if (bytes_to_copy < 16 /*else use png_memcpy*/ &&
+               if (bytes_to_copy < 16 /*else use memcpy*/ &&
                   png_isaligned(dp, png_uint_16) &&
                   png_isaligned(sp, png_uint_16) &&
-                  bytes_to_copy % sizeof (png_uint_16) == 0 &&
-                  bytes_to_jump % sizeof (png_uint_16) == 0)
+                  bytes_to_copy % (sizeof (png_uint_16)) == 0 &&
+                  bytes_to_jump % (sizeof (png_uint_16)) == 0)
                {
                   /* Everything is aligned for png_uint_16 copies, but try for
                    * png_uint_32 first.
                    */
                   if (png_isaligned(dp, png_uint_32) &&
                      png_isaligned(sp, png_uint_32) &&
-                     bytes_to_copy % sizeof (png_uint_32) == 0 &&
-                     bytes_to_jump % sizeof (png_uint_32) == 0)
+                     bytes_to_copy % (sizeof (png_uint_32)) == 0 &&
+                     bytes_to_jump % (sizeof (png_uint_32)) == 0)
                   {
-                     png_uint_32p dp32 = (png_uint_32p)dp;
-                     png_const_uint_32p sp32 = (png_const_uint_32p)sp;
-                     unsigned int skip = (bytes_to_jump-bytes_to_copy) /
-                        sizeof (png_uint_32);
+                     png_uint_32p dp32 = png_aligncast(png_uint_32p,dp);
+                     png_const_uint_32p sp32 = png_aligncastconst(
+                        png_const_uint_32p, sp);
+                     size_t skip = (bytes_to_jump-bytes_to_copy) /
+                        (sizeof (png_uint_32));
 
                      do
                      {
@@ -3161,7 +3364,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
                         do
                         {
                            *dp32++ = *sp32++;
-                           c -= sizeof (png_uint_32);
+                           c -= (sizeof (png_uint_32));
                         }
                         while (c > 0);
 
@@ -3191,10 +3394,11 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
                    */
                   else
                   {
-                     png_uint_16p dp16 = (png_uint_16p)dp;
-                     png_const_uint_16p sp16 = (png_const_uint_16p)sp;
-                     unsigned int skip = (bytes_to_jump-bytes_to_copy) /
-                        sizeof (png_uint_16);
+                     png_uint_16p dp16 = png_aligncast(png_uint_16p, dp);
+                     png_const_uint_16p sp16 = png_aligncastconst(
+                        png_const_uint_16p, sp);
+                     size_t skip = (bytes_to_jump-bytes_to_copy) /
+                        (sizeof (png_uint_16));
 
                      do
                      {
@@ -3202,7 +3406,7 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
                         do
                         {
                            *dp16++ = *sp16++;
-                           c -= sizeof (png_uint_16);
+                           c -= (sizeof (png_uint_16));
                         }
                         while (c > 0);
 
@@ -3226,10 +3430,10 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
                }
 #endif /* PNG_ALIGN_ code */
 
-               /* The true default - use a png_memcpy: */
+               /* The true default - use a memcpy: */
                for (;;)
                {
-                  png_memcpy(dp, sp, bytes_to_copy);
+                  memcpy(dp, sp, bytes_to_copy);
 
                   if (row_width <= bytes_to_jump)
                      return;
@@ -3250,11 +3454,11 @@ png_combine_row(png_structp png_ptr, png_bytep dp, int display)
    else
 #endif
 
-   /* If here then the switch above wasn't used so just png_memcpy the whole row
+   /* If here then the switch above wasn't used so just memcpy the whole row
     * from the temporary row buffer (notice that this overwrites the end of the
     * destination row if it is a partial byte.)
     */
-   png_memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));
+   memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));
 
    /* Restore the overwritten bits from the last byte if necessary. */
    if (end_ptr != NULL)
@@ -3315,8 +3519,9 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
                v = (png_byte)((*sp >> sshift) & 0x01);
                for (j = 0; j < jstop; j++)
                {
-                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
-                  *dp |= (png_byte)(v << dshift);
+                  unsigned int tmp = *dp & (0x7f7f >> (7 - dshift));
+                  tmp |= v << dshift;
+                  *dp = (png_byte)(tmp & 0xff);
 
                   if (dshift == s_end)
                   {
@@ -3377,8 +3582,9 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
                v = (png_byte)((*sp >> sshift) & 0x03);
                for (j = 0; j < jstop; j++)
                {
-                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
-                  *dp |= (png_byte)(v << dshift);
+                  unsigned int tmp = *dp & (0x3f3f >> (6 - dshift));
+                  tmp |= v << dshift;
+                  *dp = (png_byte)(tmp & 0xff);
 
                   if (dshift == s_end)
                   {
@@ -3438,8 +3644,9 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
 
                for (j = 0; j < jstop; j++)
                {
-                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
-                  *dp |= (png_byte)(v << dshift);
+                  unsigned int tmp = *dp & (0xf0f >> (4 - dshift));
+                  tmp |= v << dshift;
+                  *dp = (png_byte)(tmp & 0xff);
 
                   if (dshift == s_end)
                   {
@@ -3480,11 +3687,11 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
                png_byte v[8];
                int j;
 
-               png_memcpy(v, sp, pixel_bytes);
+               memcpy(v, sp, pixel_bytes);
 
                for (j = 0; j < jstop; j++)
                {
-                  png_memcpy(dp, v, pixel_bytes);
+                  memcpy(dp, v, pixel_bytes);
                   dp -= pixel_bytes;
                }
 
@@ -3661,68 +3868,16 @@ png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
    }
 }
 
-#ifdef PNG_ARM_NEON
-
-#ifdef __linux__
-#include <stdio.h>
-#include <elf.h>
-#include <asm/hwcap.h>
-
-static int png_have_hwcap(unsigned cap)
-{
-   FILE *f = fopen("/proc/self/auxv", "r");
-   Elf32_auxv_t aux;
-   int have_cap = 0;
-
-   if (!f)
-      return 0;
-
-   while (fread(&aux, sizeof(aux), 1, f) > 0)
-   {
-      if (aux.a_type == AT_HWCAP &&
-          aux.a_un.a_val & cap)
-      {
-         have_cap = 1;
-         break;
-      }
-   }
-
-   fclose(f);
-
-   return have_cap;
-}
-#endif /* __linux__ */
-
-static void
-png_init_filter_functions_neon(png_structp pp, unsigned int bpp)
-{
-#ifdef __linux__
-   if (!png_have_hwcap(HWCAP_NEON))
-      return;
-#endif
-
-   pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon;
-
-   if (bpp == 3)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = 
-         png_read_filter_row_paeth3_neon;
-   }
-
-   else if (bpp == 4)
-   {
-      pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon;
-      pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon;
-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
-          png_read_filter_row_paeth4_neon;
-   }
-}
-#endif /* PNG_ARM_NEON */
-
 static void
-png_init_filter_functions(png_structp pp)
+png_init_filter_functions(png_structrp pp)
+   /* This function is called once for every PNG image to set the
+    * implementations required to reverse the filtering of PNG rows.  Reversing
+    * the filter is the first transformation performed on the row data.  It is
+    * performed in place, therefore an implementation can be selected based on
+    * the image pixel format.  If the implementation depends on image width then
+    * take care to ensure that it works correctly if the image is interlaced -
+    * interlacing causes the actual row width to vary.
+    */
 {
    unsigned int bpp = (pp->pixel_depth + 7) >> 3;
 
@@ -3736,15 +3891,27 @@ png_init_filter_functions(png_structp pp)
       pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
          png_read_filter_row_paeth_multibyte_pixel;
 
-#ifdef PNG_ARM_NEON
-   png_init_filter_functions_neon(pp, bpp);
+#ifdef PNG_FILTER_OPTIMIZATIONS
+   /* To use this define PNG_FILTER_OPTIMIZATIONS as the name of a function to
+    * call to install hardware optimizations for the above functions; simply
+    * replace whatever elements of the pp->read_filter[] array with a hardware
+    * specific (or, for that matter, generic) optimization.
+    *
+    * To see an example of this examine what configure.ac does when
+    * --enable-arm-neon is specified on the command line.
+    */
+   PNG_FILTER_OPTIMIZATIONS(pp, bpp);
 #endif
 }
 
 void /* PRIVATE */
-png_read_filter_row(png_structp pp, png_row_infop row_info, png_bytep row,
+png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row,
    png_const_bytep prev_row, int filter)
 {
+   /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define
+    * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic
+    * implementations.  See png_init_filter_functions above.
+    */
    if (pp->read_filter[0] == NULL)
       png_init_filter_functions(pp);
    if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST)
@@ -3753,7 +3920,184 @@ png_read_filter_row(png_structp pp, png_row_infop row_info, png_bytep row,
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 void /* PRIVATE */
-png_read_finish_row(png_structp png_ptr)
+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 /* after last row, checking 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.  If the LZ stream is truncated the sequential reader will
+       * terminally damage the stream, above, by reading the chunk header of the
+       * following chunk (it then exits with png_error).
+       *
+       * TODO: deal more elegantly with truncated IDAT lists.
+       */
+      ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
+
+      /* Take the unconsumed output back. */
+      if (output != NULL)
+         avail_out += png_ptr->zstream.avail_out;
+
+      else /* avail_out counts the extra bytes */
+         avail_out += (sizeof tmpbuf) - 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 /* the deflate stream contained extra data */
+         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
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
@@ -3784,7 +4128,7 @@ png_read_finish_row(png_structp png_ptr)
       /* TO DO: don't do this if prev_row isn't needed (requires
        * read-ahead of the next row's filter byte.
        */
-      png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
 
       do
       {
@@ -3816,76 +4160,13 @@ png_read_finish_row(png_structp png_ptr)
    }
 #endif /* PNG_READ_INTERLACING_SUPPORTED */
 
-   if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
-   {
-      char extra;
-      int ret;
-
-      png_ptr->zstream.next_out = (Byte *)&extra;
-      png_ptr->zstream.avail_out = (uInt)1;
-
-      for (;;)
-      {
-         if (!(png_ptr->zstream.avail_in))
-         {
-            while (!png_ptr->idat_size)
-            {
-               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 = (uInt)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;
-         }
-
-         ret = inflate(&png_ptr->zstream, Z_PARTIAL_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->mode |= PNG_AFTER_IDAT;
-            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
-            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");
-            png_ptr->mode |= PNG_AFTER_IDAT;
-            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
-            break;
-         }
-
-      }
-      png_ptr->zstream.avail_out = 0;
-   }
-
-   if (png_ptr->idat_size || png_ptr->zstream.avail_in)
-      png_warning(png_ptr, "Extra compression data");
-
-   inflateReset(&png_ptr->zstream);
-
-   png_ptr->mode |= PNG_AFTER_IDAT;
+   /* Here after at the end of the last row of the last pass. */
+   png_read_finish_IDAT(png_ptr);
 }
 #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
 
 void /* PRIVATE */
-png_read_start_row(png_structp png_ptr)
+png_read_start_row(png_structrp png_ptr)
 {
 #ifdef PNG_READ_INTERLACING_SUPPORTED
    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
@@ -3907,7 +4188,7 @@ png_read_start_row(png_structp png_ptr)
    png_size_t row_bytes;
 
    png_debug(1, "in png_read_start_row");
-   png_ptr->zstream.avail_in = 0;
+
 #ifdef PNG_READ_TRANSFORMS_SUPPORTED
    png_init_read_transformations(png_ptr);
 #endif
@@ -4145,7 +4426,7 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
    if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1))
       png_error(png_ptr, "Row has too many bytes to allocate in memory");
 
-   png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+   memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
 
    png_debug1(3, "width = %u,", png_ptr->width);
    png_debug1(3, "height = %u,", png_ptr->height);
@@ -4155,6 +4436,27 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
    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, use the bits
+    * value from the stream (note that this will result in a fatal error if the
+    * IDAT stream has a bogus deflate header window_bits value, but this should
+    * not be happening any longer!)
+    */
+   if (png_inflate_claim(png_ptr, png_IDAT, 0) != Z_OK)
+      png_error(png_ptr, png_ptr->zstream.msg);
+
    png_ptr->flags |= PNG_FLAG_ROW_INIT;
 }
 #endif /* PNG_READ_SUPPORTED */