XFA: Upgrade libpng to 1.6.20.

R=tsepez@chromium.org

Review URL: https://codereview.chromium.org/1591483003 .
diff --git a/core/src/fxcodec/codec/DEPS b/core/src/fxcodec/codec/DEPS
new file mode 100644
index 0000000..9caa4d8
--- /dev/null
+++ b/core/src/fxcodec/codec/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  '+third_party/libpng/png.h',
+]
diff --git a/core/src/fxcodec/codec/fx_codec_png.cpp b/core/src/fxcodec/codec/fx_codec_png.cpp
index 3acfc19..97c2f77 100644
--- a/core/src/fxcodec/codec/fx_codec_png.cpp
+++ b/core/src/fxcodec/codec/fx_codec_png.cpp
@@ -12,7 +12,7 @@
 

 extern "C" {

 #undef FAR

-#include "third_party/lpng_v163/png.h"

+#include "third_party/libpng/png.h"

 }

 

 static void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {

diff --git a/samples/DEPS b/samples/DEPS
index 7bd794e..18ab975 100644
--- a/samples/DEPS
+++ b/samples/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
-  '+fx_lpng',
   '+public',
+  '+third_party/libpng',
   '+third_party/zlib_v128',
   '+v8',
 ]
diff --git a/samples/image_diff_png.cc b/samples/image_diff_png.cc
index 6ad7f88..01df836 100644
--- a/samples/image_diff_png.cc
+++ b/samples/image_diff_png.cc
@@ -17,7 +17,7 @@
 #include <string>
 
 #include "third_party/base/logging.h"
-#include "third_party/lpng_v163/png.h"
+#include "third_party/libpng/png.h"
 #include "third_party/zlib_v128/zlib.h"
 
 namespace image_diff_png {
diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn
index 5f35432..d8d1dd2 100644
--- a/third_party/BUILD.gn
+++ b/third_party/BUILD.gn
@@ -278,22 +278,28 @@
     "//third_party/pdfium:pdfium_config",
   ]
   sources = [
-    "lpng_v163/png.h",
-    "lpng_v163/png.c",
-    "lpng_v163/pngerror.c",
-    "lpng_v163/pngget.c",
-    "lpng_v163/pngmem.c",
-    "lpng_v163/pngpread.c",
-    "lpng_v163/pngread.c",
-    "lpng_v163/pngrio.c",
-    "lpng_v163/pngrtran.c",
-    "lpng_v163/pngrutil.c",
-    "lpng_v163/pngset.c",
-    "lpng_v163/pngtrans.c",
-    "lpng_v163/pngwio.c",
-    "lpng_v163/pngwrite.c",
-    "lpng_v163/pngwtran.c",
-    "lpng_v163/pngwutil.c",
+    "libpng/png.c",
+    "libpng/png.h",
+    "libpng/pngconf.h",
+    "libpng/pngdebug.h",
+    "libpng/pngerror.c",
+    "libpng/pngget.c",
+    "libpng/pnginfo.h",
+    "libpng/pnglibconf.h",
+    "libpng/pngmem.c",
+    "libpng/pngpread.c",
+    "libpng/pngpriv.h",
+    "libpng/pngread.c",
+    "libpng/pngrio.c",
+    "libpng/pngrtran.c",
+    "libpng/pngrutil.c",
+    "libpng/pngset.c",
+    "libpng/pngstruct.h",
+    "libpng/pngtrans.c",
+    "libpng/pngwio.c",
+    "libpng/pngwrite.c",
+    "libpng/pngwtran.c",
+    "libpng/pngwutil.c",
   ]
 }
 
diff --git a/third_party/libpng/0000-build-config.patch b/third_party/libpng/0000-build-config.patch
new file mode 100644
index 0000000..4f00139
--- /dev/null
+++ b/third_party/libpng/0000-build-config.patch
@@ -0,0 +1,43 @@
+diff a/third_party/libpng/pngmem.c b/third_party/libpng/pngmem.c
+--- a/third_party/libpng/pngmem.c
++++ b/third_party/libpng/pngmem.c
+@@ -19,6 +19,9 @@
+ 
+ #include "pngpriv.h"
+ 
++void*	FXMEM_DefaultAlloc(int byte_size, int);
++void	FXMEM_DefaultFree(void* pointer, int);
++
+ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+ /* Free a png_struct */
+ void /* PRIVATE */
+@@ -92,7 +95,7 @@
+ 
+       else
+ #endif
+-         return malloc((size_t)size); /* checked for truncation above */
++         return FXMEM_DefaultAlloc((int)size, 0);
+    }
+ 
+    else
+@@ -249,7 +252,7 @@
+       return;
+ #endif /* USER_MEM */
+ 
+-   free(ptr);
++   FXMEM_DefaultFree(ptr, 0);
+ }
+ 
+ #ifdef PNG_USER_MEM_SUPPORTED
+diff a/third_party/libpng/pngstruct.h b/third_party/libpng/pngstruct.h
+--- a/third_party/libpng/pngstruct.h
++++ b/third_party/libpng/pngstruct.h
+@@ -27,7 +27,7 @@
+    /* We must ensure that zlib uses 'const' in declarations. */
+ #  define ZLIB_CONST
+ #endif
+-#include "zlib.h"
++#include "third_party/zlib_v128/zlib.h"
+ #ifdef const
+    /* zlib.h sometimes #defines const to nothing, undo this. */
+ #  undef const
diff --git a/third_party/libpng/0001-disable-arm-neon.patch b/third_party/libpng/0001-disable-arm-neon.patch
new file mode 100644
index 0000000..b5105f7
--- /dev/null
+++ b/third_party/libpng/0001-disable-arm-neon.patch
@@ -0,0 +1,12 @@
+diff a/third_party/libpng/pngpriv.h b/third_party/libpng/pngpriv.h
+--- a/third_party/libpng/pngpriv.h
++++ b/third_party/libpng/pngpriv.h
+@@ -127,7 +127,7 @@
+     */
+ #  if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \
+    defined(PNG_ALIGNED_MEMORY_SUPPORTED)
+-#     define PNG_ARM_NEON_OPT 2
++#     define PNG_ARM_NEON_OPT 0
+ #  else
+ #     define PNG_ARM_NEON_OPT 0
+ #  endif
diff --git a/third_party/libpng/README.pdfium b/third_party/libpng/README.pdfium
new file mode 100644
index 0000000..66d2d06
--- /dev/null
+++ b/third_party/libpng/README.pdfium
@@ -0,0 +1,14 @@
+Name: libpng
+URL: http://www.libpng.org/pub/png/libpng.html
+Version: 1.6.20
+Security Critical: yes
+License: libpng license
+
+Description:
+PNG library.
+
+Local Modifications:
+
+pnglibconf.h: a copy of libpng's scripts/pnglibconf.h.prebuilt.
+0000-build-config.patch: Local build configuration changes.
+0001-disable-arm-neon.diff: Disable ARM NEON optimizations.
diff --git a/third_party/lpng_v163/png.c b/third_party/libpng/png.c
similarity index 85%
rename from third_party/lpng_v163/png.c
rename to third_party/libpng/png.c
index 93c675b..c183e3f 100644
--- a/third_party/lpng_v163/png.c
+++ b/third_party/libpng/png.c
@@ -1,4297 +1,4498 @@
-/* png.c - location for general purpose libpng functions

- *

- * 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

-#include "pngpriv.h"

-

-/* Generate a compiler error if there is an old png.h in the search path. */

-typedef png_libpng_version_1_6_3 Your_png_h_is_not_version_1_6_3;

-

-/* Tells libpng that we have already handled the first "num_bytes" bytes

- * of the PNG file signature.  If the PNG data is embedded into another

- * stream we can set num_bytes = 8 so that libpng will not attempt to read

- * or write any of the magic bytes before it starts on the IHDR.

- */

-

-#ifdef PNG_READ_SUPPORTED

-void PNGAPI

-png_set_sig_bytes(png_structrp png_ptr, int num_bytes)

-{

-   png_debug(1, "in png_set_sig_bytes");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (num_bytes > 8)

-      png_error(png_ptr, "Too many bytes for PNG signature");

-

-   png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes);

-}

-

-/* Checks whether the supplied bytes match the PNG signature.  We allow

- * checking less than the full 8-byte signature so that those apps that

- * already read the first few bytes of a file to determine the file type

- * can simply check the remaining bytes for extra assurance.  Returns

- * an integer less than, equal to, or greater than zero if sig is found,

- * respectively, to be less than, to match, or be greater than the correct

- * PNG signature (this is the same behavior as strcmp, memcmp, etc).

- */

-int PNGAPI

-png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)

-{

-   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};

-

-   if (num_to_check > 8)

-      num_to_check = 8;

-

-   else if (num_to_check < 1)

-      return (-1);

-

-   if (start > 7)

-      return (-1);

-

-   if (start + num_to_check > 8)

-      num_to_check = 8 - start;

-

-   return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));

-}

-

-#endif /* PNG_READ_SUPPORTED */

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-/* Function to allocate memory for zlib */

-PNG_FUNCTION(voidpf /* PRIVATE */,

-png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)

-{

-   png_alloc_size_t num_bytes = size;

-

-   if (png_ptr == NULL)

-      return NULL;

-

-   if (items >= (~(png_alloc_size_t)0)/size)

-   {

-      png_warning (png_voidcast(png_structrp, png_ptr),

-         "Potential overflow in png_zalloc()");

-      return NULL;

-   }

-

-   num_bytes *= items;

-   return png_malloc_warn(png_voidcast(png_structrp, png_ptr), num_bytes);

-}

-

-/* Function to free memory for zlib */

-void /* PRIVATE */

-png_zfree(voidpf png_ptr, voidpf ptr)

-{

-   png_free(png_voidcast(png_const_structrp,png_ptr), ptr);

-}

-

-/* Reset the CRC variable to 32 bits of 1's.  Care must be taken

- * in case CRC is > 32 bits to leave the top bits 0.

- */

-void /* PRIVATE */

-png_reset_crc(png_structrp png_ptr)

-{

-   /* The cast is safe because the crc is a 32 bit value. */

-   png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0);

-}

-

-/* Calculate the CRC over a section of data.  We can only pass as

- * much data to this routine as the largest single buffer size.  We

- * also check that this data will actually be used before going to the

- * trouble of calculating it.

- */

-void /* PRIVATE */

-png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length)

-{

-   int need_crc = 1;

-

-   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))

-         need_crc = 0;

-   }

-

-   else /* critical */

-   {

-      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)

-         need_crc = 0;

-   }

-

-   /* 'uLong' is defined in zlib.h as unsigned long; this means that on some

-    * systems it is a 64 bit value.  crc32, however, returns 32 bits so the

-    * following cast is safe.  'uInt' may be no more than 16 bits, so it is

-    * necessary to perform a loop here.

-    */

-   if (need_crc && length > 0)

-   {

-      uLong crc = png_ptr->crc; /* Should never issue a warning */

-

-      do

-      {

-         uInt safe_length = (uInt)length;

-         if (safe_length == 0)

-            safe_length = (uInt)-1; /* evil, but safe */

-

-         crc = crc32(crc, ptr, safe_length);

-

-         /* The following should never issue compiler warnings; if they do the

-          * target system has characteristics that will probably violate other

-          * assumptions within the libpng code.

-          */

-         ptr += safe_length;

-         length -= safe_length;

-      }

-      while (length > 0);

-

-      /* And the following is always safe because the crc is only 32 bits. */

-      png_ptr->crc = (png_uint_32)crc;

-   }

-}

-

-/* Check a user supplied version number, called from both read and write

- * functions that create a png_struct.

- */

-int

-png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)

-{

-   if (user_png_ver)

-   {

-      int i = 0;

-

-      do

-      {

-         if (user_png_ver[i] != png_libpng_ver[i])

-            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;

-      } while (png_libpng_ver[i++]);

-   }

-

-   else

-      png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;

-

-   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)

-   {

-     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so

-      * we must recompile any applications that use any older library version.

-      * For versions after libpng 1.0, we will be compatible, so we need

-      * only check the first and third digits (note that when we reach version

-      * 1.10 we will need to check the fourth symbol, namely user_png_ver[3]).

-      */

-      if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||

-          (user_png_ver[0] == '1' && (user_png_ver[2] != png_libpng_ver[2] ||

-          user_png_ver[3] != png_libpng_ver[3])) ||

-          (user_png_ver[0] == '0' && user_png_ver[2] < '9'))

-      {

-#ifdef PNG_WARNINGS_SUPPORTED

-         size_t pos = 0;

-         char m[128];

-

-         pos = png_safecat(m, (sizeof m), pos,

-             "Application built with libpng-");

-         pos = png_safecat(m, (sizeof m), pos, user_png_ver);

-         pos = png_safecat(m, (sizeof m), pos, " but running with ");

-         pos = png_safecat(m, (sizeof m), pos, png_libpng_ver);

-

-         png_warning(png_ptr, m);

-#endif

-

-#ifdef PNG_ERROR_NUMBERS_SUPPORTED

-         png_ptr->flags = 0;

-#endif

-

-         return 0;

-      }

-   }

-

-   /* Success return. */

-   return 1;

-}

-

-/* Generic function to create a png_struct for either read or write - this

- * contains the common initialization.

- */

-PNG_FUNCTION(png_structp /* PRIVATE */,

-png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,

-    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,

-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)

-{

-   png_struct create_struct;

-#  ifdef PNG_SETJMP_SUPPORTED

-      jmp_buf create_jmp_buf;

-#  endif

-

-   /* This temporary stack-allocated structure is used to provide a place to

-    * build enough context to allow the user provided memory allocator (if any)

-    * to be called.

-    */

-   memset(&create_struct, 0, (sizeof create_struct));

-

-   /* Added at libpng-1.2.6 */

-#  ifdef PNG_USER_LIMITS_SUPPORTED

-      create_struct.user_width_max = PNG_USER_WIDTH_MAX;

-      create_struct.user_height_max = PNG_USER_HEIGHT_MAX;

-

-#     ifdef PNG_USER_CHUNK_CACHE_MAX

-         /* Added at libpng-1.2.43 and 1.4.0 */

-         create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX;

-#     endif

-

-#     ifdef PNG_USER_CHUNK_MALLOC_MAX

-         /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists

-          * in png_struct regardless.

-          */

-         create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX;

-#     endif

-#  endif

-

-   /* The following two API calls simply set fields in png_struct, so it is safe

-    * to do them now even though error handling is not yet set up.

-    */

-#  ifdef PNG_USER_MEM_SUPPORTED

-      png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn);

-#  endif

-

-   /* (*error_fn) can return control to the caller after the error_ptr is set,

-    * this will result in a memory leak unless the error_fn does something

-    * extremely sophisticated.  The design lacks merit but is implicit in the

-    * API.

-    */

-   png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn);

-

-#  ifdef PNG_SETJMP_SUPPORTED

-      if (!setjmp(create_jmp_buf))

-      {

-         /* Temporarily fake out the longjmp information until we have

-          * successfully completed this function.  This only works if we have

-          * setjmp() support compiled in, but it is safe - this stuff should

-          * never happen.

-          */

-         create_struct.jmp_buf_ptr = &create_jmp_buf;

-         create_struct.jmp_buf_size = 0; /*stack allocation*/

-         create_struct.longjmp_fn = longjmp;

-#  else

-      {

-#  endif

-         /* Call the general version checker (shared with read and write code):

-          */

-         if (png_user_version_check(&create_struct, user_png_ver))

-         {

-            png_structrp png_ptr = png_voidcast(png_structrp,

-               png_malloc_warn(&create_struct, (sizeof *png_ptr)));

-

-            if (png_ptr != NULL)

-            {

-               /* png_ptr->zstream holds a back-pointer to the png_struct, so

-                * this can only be done now:

-                */

-               create_struct.zstream.zalloc = png_zalloc;

-               create_struct.zstream.zfree = png_zfree;

-               create_struct.zstream.opaque = png_ptr;

-

-#              ifdef PNG_SETJMP_SUPPORTED

-                  /* Eliminate the local error handling: */

-                  create_struct.jmp_buf_ptr = NULL;

-                  create_struct.jmp_buf_size = 0;

-                  create_struct.longjmp_fn = 0;

-#              endif

-

-               *png_ptr = create_struct;

-

-               /* This is the successful return point */

-               return png_ptr;

-            }

-         }

-      }

-

-   /* A longjmp because of a bug in the application storage allocator or a

-    * simple failure to allocate the png_struct.

-    */

-   return NULL;

-}

-

-/* Allocate the memory for an info_struct for the application. */

-PNG_FUNCTION(png_infop,PNGAPI

-png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)

-{

-   png_inforp info_ptr;

-

-   png_debug(1, "in png_create_info_struct");

-

-   if (png_ptr == NULL)

-      return NULL;

-

-   /* Use the internal API that does not (or at least should not) error out, so

-    * that this call always returns ok.  The application typically sets up the

-    * error handling *after* creating the info_struct because this is the way it

-    * has always been done in 'example.c'.

-    */

-   info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,

-      (sizeof *info_ptr)));

-

-   if (info_ptr != NULL)

-      memset(info_ptr, 0, (sizeof *info_ptr));

-

-   return info_ptr;

-}

-

-/* This function frees the memory associated with a single info struct.

- * Normally, one would use either png_destroy_read_struct() or

- * png_destroy_write_struct() to free an info struct, but this may be

- * useful for some applications.  From libpng 1.6.0 this function is also used

- * internally to implement the png_info release part of the 'struct' destroy

- * APIs.  This ensures that all possible approaches free the same data (all of

- * it).

- */

-void PNGAPI

-png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr)

-{

-   png_inforp info_ptr = NULL;

-

-   png_debug(1, "in png_destroy_info_struct");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (info_ptr_ptr != NULL)

-      info_ptr = *info_ptr_ptr;

-

-   if (info_ptr != NULL)

-   {

-      /* Do this first in case of an error below; if the app implements its own

-       * memory management this can lead to png_free calling png_error, which

-       * will abort this routine and return control to the app error handler.

-       * An infinite loop may result if it then tries to free the same info

-       * ptr.

-       */

-      *info_ptr_ptr = NULL;

-

-      png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);

-      memset(info_ptr, 0, (sizeof *info_ptr));

-      png_free(png_ptr, info_ptr);

-   }

-}

-

-/* Initialize the info structure.  This is now an internal function (0.89)

- * and applications using it are urged to use png_create_info_struct()

- * instead.  Use deprecated in 1.6.0, internal use removed (used internally it

- * is just a memset).

- *

- * NOTE: it is almost inconceivable that this API is used because it bypasses

- * the user-memory mechanism and the user error handling/warning mechanisms in

- * those cases where it does anything other than a memset.

- */

-PNG_FUNCTION(void,PNGAPI

-png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size),

-   PNG_DEPRECATED)

-{

-   png_inforp info_ptr = *ptr_ptr;

-

-   png_debug(1, "in png_info_init_3");

-

-   if (info_ptr == NULL)

-      return;

-

-   if ((sizeof (png_info)) > png_info_struct_size)

-   {

-      *ptr_ptr = NULL;

-      /* The following line is why this API should not be used: */

-      free(info_ptr);

-      info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL,

-         (sizeof *info_ptr)));

-      *ptr_ptr = info_ptr;

-   }

-

-   /* Set everything to 0 */

-   memset(info_ptr, 0, (sizeof *info_ptr));

-}

-

-/* The following API is not called internally */

-void PNGAPI

-png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr,

-   int freer, png_uint_32 mask)

-{

-   png_debug(1, "in png_data_freer");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (freer == PNG_DESTROY_WILL_FREE_DATA)

-      info_ptr->free_me |= mask;

-

-   else if (freer == PNG_USER_WILL_FREE_DATA)

-      info_ptr->free_me &= ~mask;

-

-   else

-      png_error(png_ptr, "Unknown freer parameter in png_data_freer");

-}

-

-void PNGAPI

-png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,

-   int num)

-{

-   png_debug(1, "in png_free_data");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-#ifdef PNG_TEXT_SUPPORTED

-   /* Free text item num or (if num == -1) all text items */

-   if ((mask & PNG_FREE_TEXT) & info_ptr->free_me)

-   {

-      if (num != -1)

-      {

-         if (info_ptr->text && info_ptr->text[num].key)

-         {

-            png_free(png_ptr, info_ptr->text[num].key);

-            info_ptr->text[num].key = NULL;

-         }

-      }

-

-      else

-      {

-         int i;

-         for (i = 0; i < info_ptr->num_text; i++)

-             png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i);

-         png_free(png_ptr, info_ptr->text);

-         info_ptr->text = NULL;

-         info_ptr->num_text=0;

-      }

-   }

-#endif

-

-#ifdef PNG_tRNS_SUPPORTED

-   /* Free any tRNS entry */

-   if ((mask & PNG_FREE_TRNS) & info_ptr->free_me)

-   {

-      png_free(png_ptr, info_ptr->trans_alpha);

-      info_ptr->trans_alpha = NULL;

-      info_ptr->valid &= ~PNG_INFO_tRNS;

-   }

-#endif

-

-#ifdef PNG_sCAL_SUPPORTED

-   /* Free any sCAL entry */

-   if ((mask & PNG_FREE_SCAL) & info_ptr->free_me)

-   {

-      png_free(png_ptr, info_ptr->scal_s_width);

-      png_free(png_ptr, info_ptr->scal_s_height);

-      info_ptr->scal_s_width = NULL;

-      info_ptr->scal_s_height = NULL;

-      info_ptr->valid &= ~PNG_INFO_sCAL;

-   }

-#endif

-

-#ifdef PNG_pCAL_SUPPORTED

-   /* Free any pCAL entry */

-   if ((mask & PNG_FREE_PCAL) & info_ptr->free_me)

-   {

-      png_free(png_ptr, info_ptr->pcal_purpose);

-      png_free(png_ptr, info_ptr->pcal_units);

-      info_ptr->pcal_purpose = NULL;

-      info_ptr->pcal_units = NULL;

-      if (info_ptr->pcal_params != NULL)

-         {

-            unsigned int i;

-            for (i = 0; i < info_ptr->pcal_nparams; i++)

-            {

-               png_free(png_ptr, info_ptr->pcal_params[i]);

-               info_ptr->pcal_params[i] = NULL;

-            }

-            png_free(png_ptr, info_ptr->pcal_params);

-            info_ptr->pcal_params = NULL;

-         }

-      info_ptr->valid &= ~PNG_INFO_pCAL;

-   }

-#endif

-

-#ifdef PNG_iCCP_SUPPORTED

-   /* Free any profile entry */

-   if ((mask & PNG_FREE_ICCP) & info_ptr->free_me)

-   {

-      png_free(png_ptr, info_ptr->iccp_name);

-      png_free(png_ptr, info_ptr->iccp_profile);

-      info_ptr->iccp_name = NULL;

-      info_ptr->iccp_profile = NULL;

-      info_ptr->valid &= ~PNG_INFO_iCCP;

-   }

-#endif

-

-#ifdef PNG_sPLT_SUPPORTED

-   /* Free a given sPLT entry, or (if num == -1) all sPLT entries */

-   if ((mask & PNG_FREE_SPLT) & info_ptr->free_me)

-   {

-      if (num != -1)

-      {

-         if (info_ptr->splt_palettes)

-         {

-            png_free(png_ptr, info_ptr->splt_palettes[num].name);

-            png_free(png_ptr, info_ptr->splt_palettes[num].entries);

-            info_ptr->splt_palettes[num].name = NULL;

-            info_ptr->splt_palettes[num].entries = NULL;

-         }

-      }

-

-      else

-      {

-         if (info_ptr->splt_palettes_num)

-         {

-            int i;

-            for (i = 0; i < info_ptr->splt_palettes_num; i++)

-               png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, (int)i);

-

-            png_free(png_ptr, info_ptr->splt_palettes);

-            info_ptr->splt_palettes = NULL;

-            info_ptr->splt_palettes_num = 0;

-         }

-         info_ptr->valid &= ~PNG_INFO_sPLT;

-      }

-   }

-#endif

-

-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED

-   if ((mask & PNG_FREE_UNKN) & info_ptr->free_me)

-   {

-      if (num != -1)

-      {

-          if (info_ptr->unknown_chunks)

-          {

-             png_free(png_ptr, info_ptr->unknown_chunks[num].data);

-             info_ptr->unknown_chunks[num].data = NULL;

-          }

-      }

-

-      else

-      {

-         int i;

-

-         if (info_ptr->unknown_chunks_num)

-         {

-            for (i = 0; i < info_ptr->unknown_chunks_num; i++)

-               png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, (int)i);

-

-            png_free(png_ptr, info_ptr->unknown_chunks);

-            info_ptr->unknown_chunks = NULL;

-            info_ptr->unknown_chunks_num = 0;

-         }

-      }

-   }

-#endif

-

-#ifdef PNG_hIST_SUPPORTED

-   /* Free any hIST entry */

-   if ((mask & PNG_FREE_HIST)  & info_ptr->free_me)

-   {

-      png_free(png_ptr, info_ptr->hist);

-      info_ptr->hist = NULL;

-      info_ptr->valid &= ~PNG_INFO_hIST;

-   }

-#endif

-

-   /* Free any PLTE entry that was internally allocated */

-   if ((mask & PNG_FREE_PLTE) & info_ptr->free_me)

-   {

-      png_free(png_ptr, info_ptr->palette);

-      info_ptr->palette = NULL;

-      info_ptr->valid &= ~PNG_INFO_PLTE;

-      info_ptr->num_palette = 0;

-   }

-

-#ifdef PNG_INFO_IMAGE_SUPPORTED

-   /* Free any image bits attached to the info structure */

-   if ((mask & PNG_FREE_ROWS) & info_ptr->free_me)

-   {

-      if (info_ptr->row_pointers)

-      {

-         png_uint_32 row;

-         for (row = 0; row < info_ptr->height; row++)

-         {

-            png_free(png_ptr, info_ptr->row_pointers[row]);

-            info_ptr->row_pointers[row] = NULL;

-         }

-         png_free(png_ptr, info_ptr->row_pointers);

-         info_ptr->row_pointers = NULL;

-      }

-      info_ptr->valid &= ~PNG_INFO_IDAT;

-   }

-#endif

-

-   if (num != -1)

-      mask &= ~PNG_FREE_MUL;

-

-   info_ptr->free_me &= ~mask;

-}

-#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */

-

-/* This function returns a pointer to the io_ptr associated with the user

- * functions.  The application should free any memory associated with this

- * pointer before png_write_destroy() or png_read_destroy() are called.

- */

-png_voidp PNGAPI

-png_get_io_ptr(png_const_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return (NULL);

-

-   return (png_ptr->io_ptr);

-}

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-#  ifdef PNG_STDIO_SUPPORTED

-/* Initialize the default input/output functions for the PNG file.  If you

- * use your own read or write routines, you can call either png_set_read_fn()

- * or png_set_write_fn() instead of png_init_io().  If you have defined

- * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a

- * function of your own because "FILE *" isn't necessarily available.

- */

-void PNGAPI

-png_init_io(png_structrp png_ptr, png_FILE_p fp)

-{

-   png_debug(1, "in png_init_io");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->io_ptr = (png_voidp)fp;

-}

-#  endif

-

-#ifdef PNG_SAVE_INT_32_SUPPORTED

-/* The png_save_int_32 function assumes integers are stored in two's

- * complement format.  If this isn't the case, then this routine needs to

- * be modified to write data in two's complement format.  Note that,

- * the following works correctly even if png_int_32 has more than 32 bits

- * (compare the more complex code required on read for sign extension.)

- */

-void PNGAPI

-png_save_int_32(png_bytep buf, png_int_32 i)

-{

-   buf[0] = (png_byte)((i >> 24) & 0xff);

-   buf[1] = (png_byte)((i >> 16) & 0xff);

-   buf[2] = (png_byte)((i >> 8) & 0xff);

-   buf[3] = (png_byte)(i & 0xff);

-}

-#endif

-

-#  ifdef PNG_TIME_RFC1123_SUPPORTED

-/* Convert the supplied time into an RFC 1123 string suitable for use in

- * a "Creation Time" or other text-based time string.

- */

-int PNGAPI

-png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)

-{

-   static PNG_CONST char short_months[12][4] =

-        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",

-         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

-

-   if (out == NULL)

-      return 0;

-

-   if (ptime->year > 9999 /* RFC1123 limitation */ ||

-       ptime->month == 0    ||  ptime->month > 12  ||

-       ptime->day   == 0    ||  ptime->day   > 31  ||

-       ptime->hour  > 23    ||  ptime->minute > 59 ||

-       ptime->second > 60)

-      return 0;

-

-   {

-      size_t pos = 0;

-      char number_buf[5]; /* enough for a four-digit year */

-

-#     define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string))

-#     define APPEND_NUMBER(format, value)\

-         APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value)))

-#     define APPEND(ch) if (pos < 28) out[pos++] = (ch)

-

-      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day);

-      APPEND(' ');

-      APPEND_STRING(short_months[(ptime->month - 1)]);

-      APPEND(' ');

-      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year);

-      APPEND(' ');

-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour);

-      APPEND(':');

-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute);

-      APPEND(':');

-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second);

-      APPEND_STRING(" +0000"); /* This reliably terminates the buffer */

-

-#     undef APPEND

-#     undef APPEND_NUMBER

-#     undef APPEND_STRING

-   }

-

-   return 1;

-}

-

-#     if PNG_LIBPNG_VER < 10700

-/* To do: remove the following from libpng-1.7 */

-/* Original API that uses a private buffer in png_struct.

- * Deprecated because it causes png_struct to carry a spurious temporary

- * buffer (png_struct::time_buffer), better to have the caller pass this in.

- */

-png_const_charp PNGAPI

-png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime)

-{

-   if (png_ptr != NULL)

-   {

-      /* The only failure above if png_ptr != NULL is from an invalid ptime */

-      if (!png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime))

-         png_warning(png_ptr, "Ignoring invalid time value");

-

-      else

-         return png_ptr->time_buffer;

-   }

-

-   return NULL;

-}

-#     endif

-#  endif /* PNG_TIME_RFC1123_SUPPORTED */

-

-#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */

-

-png_const_charp PNGAPI

-png_get_copyright(png_const_structrp png_ptr)

-{

-   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */

-#ifdef PNG_STRING_COPYRIGHT

-   return PNG_STRING_COPYRIGHT

-#else

-#  ifdef __STDC__

-   return PNG_STRING_NEWLINE \

-     "libpng version 1.6.3 - July 18, 2013" PNG_STRING_NEWLINE \

-     "Copyright (c) 1998-2013 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \

-     "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \

-     "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \

-     PNG_STRING_NEWLINE;

-#  else

-      return "libpng version 1.6.3 - July 18, 2013\

-      Copyright (c) 1998-2013 Glenn Randers-Pehrson\

-      Copyright (c) 1996-1997 Andreas Dilger\

-      Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";

-#  endif

-#endif

-}

-

-/* The following return the library version as a short string in the

- * format 1.0.0 through 99.99.99zz.  To get the version of *.h files

- * used with your application, print out PNG_LIBPNG_VER_STRING, which

- * is defined in png.h.

- * Note: now there is no difference between png_get_libpng_ver() and

- * png_get_header_ver().  Due to the version_nn_nn_nn typedef guard,

- * it is guaranteed that png.c uses the correct version of png.h.

- */

-png_const_charp PNGAPI

-png_get_libpng_ver(png_const_structrp png_ptr)

-{

-   /* Version of *.c files used when building libpng */

-   return png_get_header_ver(png_ptr);

-}

-

-png_const_charp PNGAPI

-png_get_header_ver(png_const_structrp png_ptr)

-{

-   /* Version of *.h files used when building libpng */

-   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */

-   return PNG_LIBPNG_VER_STRING;

-}

-

-png_const_charp PNGAPI

-png_get_header_version(png_const_structrp png_ptr)

-{

-   /* Returns longer string containing both version and date */

-   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */

-#ifdef __STDC__

-   return PNG_HEADER_VERSION_STRING

-#  ifndef PNG_READ_SUPPORTED

-   "     (NO READ SUPPORT)"

-#  endif

-   PNG_STRING_NEWLINE;

-#else

-   return PNG_HEADER_VERSION_STRING;

-#endif

-}

-

-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-int PNGAPI

-png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name)

-{

-   /* Check chunk_name and return "keep" value if it's on the list, else 0 */

-   png_const_bytep p, p_end;

-

-   if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list == 0)

-      return PNG_HANDLE_CHUNK_AS_DEFAULT;

-

-   p_end = png_ptr->chunk_list;

-   p = p_end + png_ptr->num_chunk_list*5; /* beyond end */

-

-   /* The code is the fifth byte after each four byte string.  Historically this

-    * code was always searched from the end of the list, this is no longer

-    * necessary because the 'set' routine handles duplicate entries correcty.

-    */

-   do /* num_chunk_list > 0, so at least one */

-   {

-      p -= 5;

-

-      if (!memcmp(chunk_name, p, 4))

-         return p[4];

-   }

-   while (p > p_end);

-

-   /* This means that known chunks should be processed and unknown chunks should

-    * be handled according to the value of png_ptr->unknown_default; this can be

-    * confusing because, as a result, there are two levels of defaulting for

-    * unknown chunks.

-    */

-   return PNG_HANDLE_CHUNK_AS_DEFAULT;

-}

-

-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED

-int /* PRIVATE */

-png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name)

-{

-   png_byte chunk_string[5];

-

-   PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name);

-   return png_handle_as_unknown(png_ptr, chunk_string);

-}

-#endif /* READ_UNKNOWN_CHUNKS */

-#endif /* SET_UNKNOWN_CHUNKS */

-

-#ifdef PNG_READ_SUPPORTED

-/* This function, added to libpng-1.0.6g, is untested. */

-int PNGAPI

-png_reset_zstream(png_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return Z_STREAM_ERROR;

-

-   /* WARNING: this resets the window bits to the maximum! */

-   return (inflateReset(&png_ptr->zstream));

-}

-#endif /* PNG_READ_SUPPORTED */

-

-/* This function was added to libpng-1.0.7 */

-png_uint_32 PNGAPI

-png_access_version_number(void)

-{

-   /* Version of *.c files used when building libpng */

-   return((png_uint_32)PNG_LIBPNG_VER);

-}

-

-

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-/* Ensure that png_ptr->zstream.msg holds some appropriate error message string.

- * If it doesn't 'ret' is used to set it to something appropriate, even in cases

- * like Z_OK or Z_STREAM_END where the error code is apparently a success code.

- */

-void /* PRIVATE */

-png_zstream_error(png_structrp png_ptr, int ret)

-{

-   /* Translate 'ret' into an appropriate error string, priority is given to the

-    * one in zstream if set.  This always returns a string, even in cases like

-    * Z_OK or Z_STREAM_END where the error code is a success code.

-    */

-   if (png_ptr->zstream.msg == NULL) switch (ret)

-   {

-      default:

-      case Z_OK:

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");

-         break;

-

-      case Z_STREAM_END:

-         /* Normal exit */

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");

-         break;

-

-      case Z_NEED_DICT:

-         /* This means the deflate stream did not have a dictionary; this

-          * indicates a bogus PNG.

-          */

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");

-         break;

-

-      case Z_ERRNO:

-         /* gz APIs only: should not happen */

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");

-         break;

-

-      case Z_STREAM_ERROR:

-         /* internal libpng error */

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");

-         break;

-

-      case Z_DATA_ERROR:

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");

-         break;

-

-      case Z_MEM_ERROR:

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");

-         break;

-

-      case Z_BUF_ERROR:

-         /* End of input or output; not a problem if the caller is doing

-          * incremental read or write.

-          */

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");

-         break;

-

-      case Z_VERSION_ERROR:

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");

-         break;

-

-      case PNG_UNEXPECTED_ZLIB_RETURN:

-         /* Compile errors here mean that zlib now uses the value co-opted in

-          * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above

-          * and change pngpriv.h.  Note that this message is "... return",

-          * whereas the default/Z_OK one is "... return code".

-          */

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");

-         break;

-   }

-}

-

-/* png_convert_size: a PNGAPI but no longer in png.h, so deleted

- * at libpng 1.5.5!

- */

-

-/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */

-#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */

-static int

-png_colorspace_check_gamma(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_fixed_point gAMA, int from)

-   /* This is called to check a new gamma value against an existing one.  The

-    * routine returns false if the new gamma value should not be written.

-    *

-    * 'from' says where the new gamma value comes from:

-    *

-    *    0: the new gamma value is the libpng estimate for an ICC profile

-    *    1: the new gamma value comes from a gAMA chunk

-    *    2: the new gamma value comes from an sRGB chunk

-    */

-{

-   png_fixed_point gtest;

-

-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&

-      (!png_muldiv(&gtest, colorspace->gamma, PNG_FP_1, gAMA) ||

-      png_gamma_significant(gtest)))

-   {

-      /* Either this is an sRGB image, in which case the calculated gamma

-       * approximation should match, or this is an image with a profile and the

-       * value libpng calculates for the gamma of the profile does not match the

-       * value recorded in the file.  The former, sRGB, case is an error, the

-       * latter is just a warning.

-       */

-      if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)

-      {

-         png_chunk_report(png_ptr, "gamma value does not match sRGB",

-            PNG_CHUNK_ERROR);

-         /* Do not overwrite an sRGB value */

-         return from == 2;

-      }

-

-      else /* sRGB tag not involved */

-      {

-         png_chunk_report(png_ptr, "gamma value does not match libpng estimate",

-            PNG_CHUNK_WARNING);

-         return from == 1;

-      }

-   }

-

-   return 1;

-}

-

-void /* PRIVATE */

-png_colorspace_set_gamma(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_fixed_point gAMA)

-{

-   /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't

-    * occur.  Since the fixed point representation is assymetrical it is

-    * possible for 1/gamma to overflow the limit of 21474 and this means the

-    * gamma value must be at least 5/100000 and hence at most 20000.0.  For

-    * safety the limits here are a little narrower.  The values are 0.00016 to

-    * 6250.0, which are truly ridiculous gamma values (and will produce

-    * displays that are all black or all white.)

-    *

-    * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk

-    * handling code, which only required the value to be >0.

-    */

-   png_const_charp errmsg;

-

-   if (gAMA < 16 || gAMA > 625000000)

-      errmsg = "gamma value out of range";

-

-#  ifdef PNG_READ_gAMA_SUPPORTED

-      /* Allow the application to set the gamma value more than once */

-      else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&

-         (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0)

-         errmsg = "duplicate";

-#  endif

-

-   /* Do nothing if the colorspace is already invalid */

-   else if (colorspace->flags & PNG_COLORSPACE_INVALID)

-      return;

-

-   else

-   {

-      if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, 1/*from gAMA*/))

-      {

-         /* Store this gamma value. */

-         colorspace->gamma = gAMA;

-         colorspace->flags |=

-            (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA);

-      }

-

-      /* At present if the check_gamma test fails the gamma of the colorspace is

-       * not updated however the colorspace is not invalidated.  This

-       * corresponds to the case where the existing gamma comes from an sRGB

-       * chunk or profile.  An error message has already been output.

-       */

-      return;

-   }

-

-   /* Error exit - errmsg has been set. */

-   colorspace->flags |= PNG_COLORSPACE_INVALID;

-   png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR);

-}

-

-void /* PRIVATE */

-png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr)

-{

-   if (info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)

-   {

-      /* Everything is invalid */

-      info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB|

-         PNG_INFO_iCCP);

-

-#     ifdef PNG_COLORSPACE_SUPPORTED

-         /* Clean up the iCCP profile now if it won't be used. */

-         png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/);

-#     else

-         PNG_UNUSED(png_ptr)

-#     endif

-   }

-

-   else

-   {

-#     ifdef PNG_COLORSPACE_SUPPORTED

-         /* Leave the INFO_iCCP flag set if the pngset.c code has already set

-          * it; this allows a PNG to contain a profile which matches sRGB and

-          * yet still have that profile retrievable by the application.

-          */

-         if (info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB)

-            info_ptr->valid |= PNG_INFO_sRGB;

-

-         else

-            info_ptr->valid &= ~PNG_INFO_sRGB;

-

-         if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)

-            info_ptr->valid |= PNG_INFO_cHRM;

-

-         else

-            info_ptr->valid &= ~PNG_INFO_cHRM;

-#     endif

-

-      if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA)

-         info_ptr->valid |= PNG_INFO_gAMA;

-

-      else

-         info_ptr->valid &= ~PNG_INFO_gAMA;

-   }

-}

-

-#ifdef PNG_READ_SUPPORTED

-void /* PRIVATE */

-png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr)

-{

-   if (info_ptr == NULL) /* reduce code size; check here not in the caller */

-      return;

-

-   info_ptr->colorspace = png_ptr->colorspace;

-   png_colorspace_sync_info(png_ptr, info_ptr);

-}

-#endif

-#endif

-

-#ifdef PNG_COLORSPACE_SUPPORTED

-/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for

- * cHRM, as opposed to using chromaticities.  These internal APIs return

- * non-zero on a parameter error.  The X, Y and Z values are required to be

- * positive and less than 1.0.

- */

-static int

-png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)

-{

-   png_int_32 d, dwhite, whiteX, whiteY;

-

-   d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z;

-   if (!png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d)) return 1;

-   if (!png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d)) return 1;

-   dwhite = d;

-   whiteX = XYZ->red_X;

-   whiteY = XYZ->red_Y;

-

-   d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z;

-   if (!png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d)) return 1;

-   if (!png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d)) return 1;

-   dwhite += d;

-   whiteX += XYZ->green_X;

-   whiteY += XYZ->green_Y;

-

-   d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z;

-   if (!png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d)) return 1;

-   if (!png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d)) return 1;

-   dwhite += d;

-   whiteX += XYZ->blue_X;

-   whiteY += XYZ->blue_Y;

-

-   /* The reference white is simply the sum of the end-point (X,Y,Z) vectors,

-    * thus:

-    */

-   if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1;

-   if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1;

-

-   return 0;

-}

-

-static int

-png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)

-{

-   png_fixed_point red_inverse, green_inverse, blue_scale;

-   png_fixed_point left, right, denominator;

-

-   /* Check xy and, implicitly, z.  Note that wide gamut color spaces typically

-    * have end points with 0 tristimulus values (these are impossible end

-    * points, but they are used to cover the possible colors.)

-    */

-   if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1;

-   if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1;

-   if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1;

-   if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1;

-   if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1;

-   if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1;

-   if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1;

-   if (xy->whitey < 0 || xy->whitey > PNG_FP_1-xy->whitex) return 1;

-

-   /* The reverse calculation is more difficult because the original tristimulus

-    * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8

-    * derived values were recorded in the cHRM chunk;

-    * (red,green,blue,white)x(x,y).  This loses one degree of freedom and

-    * therefore an arbitrary ninth value has to be introduced to undo the

-    * original transformations.

-    *

-    * Think of the original end-points as points in (X,Y,Z) space.  The

-    * chromaticity values (c) have the property:

-    *

-    *           C

-    *   c = ---------

-    *       X + Y + Z

-    *

-    * For each c (x,y,z) from the corresponding original C (X,Y,Z).  Thus the

-    * three chromaticity values (x,y,z) for each end-point obey the

-    * relationship:

-    *

-    *   x + y + z = 1

-    *

-    * This describes the plane in (X,Y,Z) space that intersects each axis at the

-    * value 1.0; call this the chromaticity plane.  Thus the chromaticity

-    * calculation has scaled each end-point so that it is on the x+y+z=1 plane

-    * and chromaticity is the intersection of the vector from the origin to the

-    * (X,Y,Z) value with the chromaticity plane.

-    *

-    * To fully invert the chromaticity calculation we would need the three

-    * end-point scale factors, (red-scale, green-scale, blue-scale), but these

-    * were not recorded.  Instead we calculated the reference white (X,Y,Z) and

-    * recorded the chromaticity of this.  The reference white (X,Y,Z) would have

-    * given all three of the scale factors since:

-    *

-    *    color-C = color-c * color-scale

-    *    white-C = red-C + green-C + blue-C

-    *            = red-c*red-scale + green-c*green-scale + blue-c*blue-scale

-    *

-    * But cHRM records only white-x and white-y, so we have lost the white scale

-    * factor:

-    *

-    *    white-C = white-c*white-scale

-    *

-    * To handle this the inverse transformation makes an arbitrary assumption

-    * about white-scale:

-    *

-    *    Assume: white-Y = 1.0

-    *    Hence:  white-scale = 1/white-y

-    *    Or:     red-Y + green-Y + blue-Y = 1.0

-    *

-    * Notice the last statement of the assumption gives an equation in three of

-    * the nine values we want to calculate.  8 more equations come from the

-    * above routine as summarised at the top above (the chromaticity

-    * calculation):

-    *

-    *    Given: color-x = color-X / (color-X + color-Y + color-Z)

-    *    Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0

-    *

-    * This is 9 simultaneous equations in the 9 variables "color-C" and can be

-    * solved by Cramer's rule.  Cramer's rule requires calculating 10 9x9 matrix

-    * determinants, however this is not as bad as it seems because only 28 of

-    * the total of 90 terms in the various matrices are non-zero.  Nevertheless

-    * Cramer's rule is notoriously numerically unstable because the determinant

-    * calculation involves the difference of large, but similar, numbers.  It is

-    * difficult to be sure that the calculation is stable for real world values

-    * and it is certain that it becomes unstable where the end points are close

-    * together.

-    *

-    * So this code uses the perhaps slightly less optimal but more

-    * understandable and totally obvious approach of calculating color-scale.

-    *

-    * This algorithm depends on the precision in white-scale and that is

-    * (1/white-y), so we can immediately see that as white-y approaches 0 the

-    * accuracy inherent in the cHRM chunk drops off substantially.

-    *

-    * libpng arithmetic: a simple invertion of the above equations

-    * ------------------------------------------------------------

-    *

-    *    white_scale = 1/white-y

-    *    white-X = white-x * white-scale

-    *    white-Y = 1.0

-    *    white-Z = (1 - white-x - white-y) * white_scale

-    *

-    *    white-C = red-C + green-C + blue-C

-    *            = red-c*red-scale + green-c*green-scale + blue-c*blue-scale

-    *

-    * This gives us three equations in (red-scale,green-scale,blue-scale) where

-    * all the coefficients are now known:

-    *

-    *    red-x*red-scale + green-x*green-scale + blue-x*blue-scale

-    *       = white-x/white-y

-    *    red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1

-    *    red-z*red-scale + green-z*green-scale + blue-z*blue-scale

-    *       = (1 - white-x - white-y)/white-y

-    *

-    * In the last equation color-z is (1 - color-x - color-y) so we can add all

-    * three equations together to get an alternative third:

-    *

-    *    red-scale + green-scale + blue-scale = 1/white-y = white-scale

-    *

-    * So now we have a Cramer's rule solution where the determinants are just

-    * 3x3 - far more tractible.  Unfortunately 3x3 determinants still involve

-    * multiplication of three coefficients so we can't guarantee to avoid

-    * overflow in the libpng fixed point representation.  Using Cramer's rule in

-    * floating point is probably a good choice here, but it's not an option for

-    * fixed point.  Instead proceed to simplify the first two equations by

-    * eliminating what is likely to be the largest value, blue-scale:

-    *

-    *    blue-scale = white-scale - red-scale - green-scale

-    *

-    * Hence:

-    *

-    *    (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale =

-    *                (white-x - blue-x)*white-scale

-    *

-    *    (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale =

-    *                1 - blue-y*white-scale

-    *

-    * And now we can trivially solve for (red-scale,green-scale):

-    *

-    *    green-scale =

-    *                (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale

-    *                -----------------------------------------------------------

-    *                                  green-x - blue-x

-    *

-    *    red-scale =

-    *                1 - blue-y*white-scale - (green-y - blue-y) * green-scale

-    *                ---------------------------------------------------------

-    *                                  red-y - blue-y

-    *

-    * Hence:

-    *

-    *    red-scale =

-    *          ( (green-x - blue-x) * (white-y - blue-y) -

-    *            (green-y - blue-y) * (white-x - blue-x) ) / white-y

-    * -------------------------------------------------------------------------

-    *  (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)

-    *

-    *    green-scale =

-    *          ( (red-y - blue-y) * (white-x - blue-x) -

-    *            (red-x - blue-x) * (white-y - blue-y) ) / white-y

-    * -------------------------------------------------------------------------

-    *  (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)

-    *

-    * Accuracy:

-    * The input values have 5 decimal digits of accuracy.  The values are all in

-    * the range 0 < value < 1, so simple products are in the same range but may

-    * need up to 10 decimal digits to preserve the original precision and avoid

-    * underflow.  Because we are using a 32-bit signed representation we cannot

-    * match this; the best is a little over 9 decimal digits, less than 10.

-    *

-    * The approach used here is to preserve the maximum precision within the

-    * signed representation.  Because the red-scale calculation above uses the

-    * difference between two products of values that must be in the range -1..+1

-    * it is sufficient to divide the product by 7; ceil(100,000/32767*2).  The

-    * factor is irrelevant in the calculation because it is applied to both

-    * numerator and denominator.

-    *

-    * Note that the values of the differences of the products of the

-    * chromaticities in the above equations tend to be small, for example for

-    * the sRGB chromaticities they are:

-    *

-    * red numerator:    -0.04751

-    * green numerator:  -0.08788

-    * denominator:      -0.2241 (without white-y multiplication)

-    *

-    *  The resultant Y coefficients from the chromaticities of some widely used

-    *  color space definitions are (to 15 decimal places):

-    *

-    *  sRGB

-    *    0.212639005871510 0.715168678767756 0.072192315360734

-    *  Kodak ProPhoto

-    *    0.288071128229293 0.711843217810102 0.000085653960605

-    *  Adobe RGB

-    *    0.297344975250536 0.627363566255466 0.075291458493998

-    *  Adobe Wide Gamut RGB

-    *    0.258728243040113 0.724682314948566 0.016589442011321

-    */

-   /* By the argument, above overflow should be impossible here. The return

-    * value of 2 indicates an internal error to the caller.

-    */

-   if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7))

-      return 2;

-   if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7))

-      return 2;

-   denominator = left - right;

-

-   /* Now find the red numerator. */

-   if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7))

-      return 2;

-   if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7))

-      return 2;

-

-   /* Overflow is possible here and it indicates an extreme set of PNG cHRM

-    * chunk values.  This calculation actually returns the reciprocal of the

-    * scale value because this allows us to delay the multiplication of white-y

-    * into the denominator, which tends to produce a small number.

-    */

-   if (!png_muldiv(&red_inverse, xy->whitey, denominator, left-right) ||

-       red_inverse <= xy->whitey /* r+g+b scales = white scale */)

-      return 1;

-

-   /* Similarly for green_inverse: */

-   if (!png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7))

-      return 2;

-   if (!png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7))

-      return 2;

-   if (!png_muldiv(&green_inverse, xy->whitey, denominator, left-right) ||

-       green_inverse <= xy->whitey)

-      return 1;

-

-   /* And the blue scale, the checks above guarantee this can't overflow but it

-    * can still produce 0 for extreme cHRM values.

-    */

-   blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) -

-      png_reciprocal(green_inverse);

-   if (blue_scale <= 0) return 1;

-

-

-   /* And fill in the png_XYZ: */

-   if (!png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse)) return 1;

-   if (!png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse)) return 1;

-   if (!png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1,

-      red_inverse))

-      return 1;

-

-   if (!png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse))

-      return 1;

-   if (!png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse))

-      return 1;

-   if (!png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1,

-      green_inverse))

-      return 1;

-

-   if (!png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1)) return 1;

-   if (!png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1)) return 1;

-   if (!png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale,

-      PNG_FP_1))

-      return 1;

-

-   return 0; /*success*/

-}

-

-static int

-png_XYZ_normalize(png_XYZ *XYZ)

-{

-   png_int_32 Y;

-

-   if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 ||

-      XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 ||

-      XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0)

-      return 1;

-

-   /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1.

-    * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore

-    * relying on addition of two positive values producing a negative one is not

-    * safe.

-    */

-   Y = XYZ->red_Y;

-   if (0x7fffffff - Y < XYZ->green_X) return 1;

-   Y += XYZ->green_Y;

-   if (0x7fffffff - Y < XYZ->blue_X) return 1;

-   Y += XYZ->blue_Y;

-

-   if (Y != PNG_FP_1)

-   {

-      if (!png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y)) return 1;

-      if (!png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y)) return 1;

-      if (!png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y)) return 1;

-

-      if (!png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y)) return 1;

-      if (!png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y)) return 1;

-      if (!png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y)) return 1;

-

-      if (!png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y)) return 1;

-      if (!png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y)) return 1;

-      if (!png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y)) return 1;

-   }

-

-   return 0;

-}

-

-static int

-png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta)

-{

-   /* Allow an error of +/-0.01 (absolute value) on each chromaticity */

-   return !(PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) ||

-      PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) ||

-      PNG_OUT_OF_RANGE(xy1->redx,   xy2->redx,  delta) ||

-      PNG_OUT_OF_RANGE(xy1->redy,   xy2->redy,  delta) ||

-      PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) ||

-      PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) ||

-      PNG_OUT_OF_RANGE(xy1->bluex,  xy2->bluex, delta) ||

-      PNG_OUT_OF_RANGE(xy1->bluey,  xy2->bluey, delta));

-}

-

-/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM

- * chunk chromaticities.  Earlier checks used to simply look for the overflow

- * condition (where the determinant of the matrix to solve for XYZ ends up zero

- * because the chromaticity values are not all distinct.)  Despite this it is

- * theoretically possible to produce chromaticities that are apparently valid

- * but that rapidly degrade to invalid, potentially crashing, sets because of

- * arithmetic inaccuracies when calculations are performed on them.  The new

- * check is to round-trip xy -> XYZ -> xy and then check that the result is

- * within a small percentage of the original.

- */

-static int

-png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy)

-{

-   int result;

-   png_xy xy_test;

-

-   /* As a side-effect this routine also returns the XYZ endpoints. */

-   result = png_XYZ_from_xy(XYZ, xy);

-   if (result) return result;

-

-   result = png_xy_from_XYZ(&xy_test, XYZ);

-   if (result) return result;

-

-   if (png_colorspace_endpoints_match(xy, &xy_test,

-      5/*actually, the math is pretty accurate*/))

-      return 0;

-

-   /* Too much slip */

-   return 1;

-}

-

-/* This is the check going the other way.  The XYZ is modified to normalize it

- * (another side-effect) and the xy chromaticities are returned.

- */

-static int

-png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ)

-{

-   int result;

-   png_XYZ XYZtemp;

-

-   result = png_XYZ_normalize(XYZ);

-   if (result) return result;

-

-   result = png_xy_from_XYZ(xy, XYZ);

-   if (result) return result;

-

-   XYZtemp = *XYZ;

-   return png_colorspace_check_xy(&XYZtemp, xy);

-}

-

-/* Used to check for an endpoint match against sRGB */

-static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */

-{

-   /* color      x       y */

-   /* red   */ 64000, 33000,

-   /* green */ 30000, 60000,

-   /* blue  */ 15000,  6000,

-   /* white */ 31270, 32900

-};

-

-static int

-png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,

-   int preferred)

-{

-   if (colorspace->flags & PNG_COLORSPACE_INVALID)

-      return 0;

-

-   /* The consistency check is performed on the chromaticities; this factors out

-    * variations because of the normalization (or not) of the end point Y

-    * values.

-    */

-   if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS))

-   {

-      /* The end points must be reasonably close to any we already have.  The

-       * following allows an error of up to +/-.001

-       */

-      if (!png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, 100))

-      {

-         colorspace->flags |= PNG_COLORSPACE_INVALID;

-         png_benign_error(png_ptr, "inconsistent chromaticities");

-         return 0; /* failed */

-      }

-

-      /* Only overwrite with preferred values */

-      if (!preferred)

-         return 1; /* ok, but no change */

-   }

-

-   colorspace->end_points_xy = *xy;

-   colorspace->end_points_XYZ = *XYZ;

-   colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS;

-

-   /* The end points are normally quoted to two decimal digits, so allow +/-0.01

-    * on this test.

-    */

-   if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000))

-      colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB;

-

-   else

-      colorspace->flags &= PNG_COLORSPACE_CANCEL(

-         PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);

-

-   return 2; /* ok and changed */

-}

-

-int /* PRIVATE */

-png_colorspace_set_chromaticities(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, const png_xy *xy, int preferred)

-{

-   /* We must check the end points to ensure they are reasonable - in the past

-    * color management systems have crashed as a result of getting bogus

-    * colorant values, while this isn't the fault of libpng it is the

-    * responsibility of libpng because PNG carries the bomb and libpng is in a

-    * position to protect against it.

-    */

-   png_XYZ XYZ;

-

-   switch (png_colorspace_check_xy(&XYZ, xy))

-   {

-      case 0: /* success */

-         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,

-            preferred);

-

-      case 1:

-         /* We can't invert the chromaticities so we can't produce value XYZ

-          * values.  Likely as not a color management system will fail too.

-          */

-         colorspace->flags |= PNG_COLORSPACE_INVALID;

-         png_benign_error(png_ptr, "invalid chromaticities");

-         break;

-

-      default:

-         /* libpng is broken; this should be a warning but if it happens we

-          * want error reports so for the moment it is an error.

-          */

-         colorspace->flags |= PNG_COLORSPACE_INVALID;

-         png_error(png_ptr, "internal error checking chromaticities");

-         break;

-   }

-

-   return 0; /* failed */

-}

-

-int /* PRIVATE */

-png_colorspace_set_endpoints(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)

-{

-   png_XYZ XYZ = *XYZ_in;

-   png_xy xy;

-

-   switch (png_colorspace_check_XYZ(&xy, &XYZ))

-   {

-      case 0:

-         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ,

-            preferred);

-

-      case 1:

-         /* End points are invalid. */

-         colorspace->flags |= PNG_COLORSPACE_INVALID;

-         png_benign_error(png_ptr, "invalid end points");

-         break;

-

-      default:

-         colorspace->flags |= PNG_COLORSPACE_INVALID;

-         png_error(png_ptr, "internal error checking chromaticities");

-         break;

-   }

-

-   return 0; /* failed */

-}

-

-#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED)

-/* Error message generation */

-static char

-png_icc_tag_char(png_uint_32 byte)

-{

-   byte &= 0xff;

-   if (byte >= 32 && byte <= 126)

-      return (char)byte;

-   else

-      return '?';

-}

-

-static void

-png_icc_tag_name(char *name, png_uint_32 tag)

-{

-   name[0] = '\'';

-   name[1] = png_icc_tag_char(tag >> 24);

-   name[2] = png_icc_tag_char(tag >> 16);

-   name[3] = png_icc_tag_char(tag >>  8);

-   name[4] = png_icc_tag_char(tag      );

-   name[5] = '\'';

-}

-

-static int

-is_ICC_signature_char(png_alloc_size_t it)

-{

-   return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) ||

-      (it >= 97 && it <= 122);

-}

-

-static int is_ICC_signature(png_alloc_size_t it)

-{

-   return is_ICC_signature_char(it >> 24) /* checks all the top bits */ &&

-      is_ICC_signature_char((it >> 16) & 0xff) &&

-      is_ICC_signature_char((it >> 8) & 0xff) &&

-      is_ICC_signature_char(it & 0xff);

-}

-

-static int

-png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,

-   png_const_charp name, png_alloc_size_t value, png_const_charp reason)

-{

-   size_t pos;

-   char message[196]; /* see below for calculation */

-

-   if (colorspace != NULL)

-      colorspace->flags |= PNG_COLORSPACE_INVALID;

-

-   pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */

-   pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */

-   pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */

-   if (is_ICC_signature(value))

-   {

-      /* So 'value' is at most 4 bytes and the following cast is safe */

-      png_icc_tag_name(message+pos, (png_uint_32)value);

-      pos += 6; /* total +8; less than the else clause */

-      message[pos++] = ':';

-      message[pos++] = ' ';

-   }

-#  ifdef PNG_WARNINGS_SUPPORTED

-   else

-      {

-         char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/

-

-         pos = png_safecat(message, (sizeof message), pos,

-            png_format_number(number, number+(sizeof number),

-               PNG_NUMBER_FORMAT_x, value));

-         pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/

-      }

-#  endif

-   /* The 'reason' is an arbitrary message, allow +79 maximum 195 */

-   pos = png_safecat(message, (sizeof message), pos, reason);

-

-   /* This is recoverable, but make it unconditionally an app_error on write to

-    * avoid writing invalid ICC profiles into PNG files.  (I.e.  we handle them

-    * on read, with a warning, but on write unless the app turns off

-    * application errors the PNG won't be written.)

-    */

-   png_chunk_report(png_ptr, message,

-      (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);

-

-   return 0;

-}

-#endif /* sRGB || iCCP */

-

-#ifdef PNG_sRGB_SUPPORTED

-int /* PRIVATE */

-png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,

-   int intent)

-{

-   /* sRGB sets known gamma, end points and (from the chunk) intent. */

-   /* IMPORTANT: these are not necessarily the values found in an ICC profile

-    * because ICC profiles store values adapted to a D50 environment; it is

-    * expected that the ICC profile mediaWhitePointTag will be D50, see the

-    * checks and code elsewhere to understand this better.

-    *

-    * These XYZ values, which are accurate to 5dp, produce rgb to gray

-    * coefficients of (6968,23435,2366), which are reduced (because they add up

-    * to 32769 not 32768) to (6968,23434,2366).  These are the values that

-    * libpng has traditionally used (and are the best values given the 15bit

-    * algorithm used by the rgb to gray code.)

-    */

-   static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */

-   {

-      /* color      X      Y      Z */

-      /* red   */ 41239, 21264,  1933,

-      /* green */ 35758, 71517, 11919,

-      /* blue  */ 18048,  7219, 95053

-   };

-

-   /* Do nothing if the colorspace is already invalidated. */

-   if (colorspace->flags & PNG_COLORSPACE_INVALID)

-      return 0;

-

-   /* Check the intent, then check for existing settings.  It is valid for the

-    * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must

-    * be consistent with the correct values.  If, however, this function is

-    * called below because an iCCP chunk matches sRGB then it is quite

-    * conceivable that an older app recorded incorrect gAMA and cHRM because of

-    * an incorrect calculation based on the values in the profile - this does

-    * *not* invalidate the profile (though it still produces an error, which can

-    * be ignored.)

-    */

-   if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)

-      return png_icc_profile_error(png_ptr, colorspace, "sRGB",

-         (unsigned)intent, "invalid sRGB rendering intent");

-

-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&

-      colorspace->rendering_intent != intent)

-      return png_icc_profile_error(png_ptr, colorspace, "sRGB",

-         (unsigned)intent, "inconsistent rendering intents");

-

-   if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0)

-   {

-      png_benign_error(png_ptr, "duplicate sRGB information ignored");

-      return 0;

-   }

-

-   /* If the standard sRGB cHRM chunk does not match the one from the PNG file

-    * warn but overwrite the value with the correct one.

-    */

-   if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&

-      !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,

-         100))

-      png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",

-         PNG_CHUNK_ERROR);

-

-   /* This check is just done for the error reporting - the routine always

-    * returns true when the 'from' argument corresponds to sRGB (2).

-    */

-   (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,

-      2/*from sRGB*/);

-

-   /* intent: bugs in GCC force 'int' to be used as the parameter type. */

-   colorspace->rendering_intent = (png_uint_16)intent;

-   colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT;

-

-   /* endpoints */

-   colorspace->end_points_xy = sRGB_xy;

-   colorspace->end_points_XYZ = sRGB_XYZ;

-   colorspace->flags |=

-      (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);

-

-   /* gamma */

-   colorspace->gamma = PNG_GAMMA_sRGB_INVERSE;

-   colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA;

-

-   /* Finally record that we have an sRGB profile */

-   colorspace->flags |=

-      (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB);

-

-   return 1; /* set */

-}

-#endif /* sRGB */

-

-#ifdef PNG_iCCP_SUPPORTED

-/* Encoded value of D50 as an ICC XYZNumber.  From the ICC 2010 spec the value

- * is XYZ(0.9642,1.0,0.8249), which scales to:

- *

- *    (63189.8112, 65536, 54060.6464)

- */

-static const png_byte D50_nCIEXYZ[12] =

-   { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d };

-

-int /* PRIVATE */

-png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,

-   png_const_charp name, png_uint_32 profile_length)

-{

-   if (profile_length < 132)

-      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,

-         "too short");

-

-   if (profile_length & 3)

-      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,

-         "invalid length");

-

-   return 1;

-}

-

-int /* PRIVATE */

-png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,

-   png_const_charp name, png_uint_32 profile_length,

-   png_const_bytep profile/* first 132 bytes only */, int color_type)

-{

-   png_uint_32 temp;

-

-   /* Length check; this cannot be ignored in this code because profile_length

-    * is used later to check the tag table, so even if the profile seems over

-    * long profile_length from the caller must be correct.  The caller can fix

-    * this up on read or write by just passing in the profile header length.

-    */

-   temp = png_get_uint_32(profile);

-   if (temp != profile_length)

-      return png_icc_profile_error(png_ptr, colorspace, name, temp,

-         "length does not match profile");

-

-   temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */

-   if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */

-      profile_length < 132+12*temp) /* truncated tag table */

-      return png_icc_profile_error(png_ptr, colorspace, name, temp,

-         "tag count too large");

-

-   /* The 'intent' must be valid or we can't store it, ICC limits the intent to

-    * 16 bits.

-    */

-   temp = png_get_uint_32(profile+64);

-   if (temp >= 0xffff) /* The ICC limit */

-      return png_icc_profile_error(png_ptr, colorspace, name, temp,

-         "invalid rendering intent");

-

-   /* This is just a warning because the profile may be valid in future

-    * versions.

-    */

-   if (temp >= PNG_sRGB_INTENT_LAST)

-      (void)png_icc_profile_error(png_ptr, NULL, name, temp,

-         "intent outside defined range");

-

-   /* At this point the tag table can't be checked because it hasn't necessarily

-    * been loaded; however, various header fields can be checked.  These checks

-    * are for values permitted by the PNG spec in an ICC profile; the PNG spec

-    * restricts the profiles that can be passed in an iCCP chunk (they must be

-    * appropriate to processing PNG data!)

-    */

-

-   /* Data checks (could be skipped).  These checks must be independent of the

-    * version number; however, the version number doesn't accomodate changes in

-    * the header fields (just the known tags and the interpretation of the

-    * data.)

-    */

-   temp = png_get_uint_32(profile+36); /* signature 'ascp' */

-   if (temp != 0x61637370)

-      return png_icc_profile_error(png_ptr, colorspace, name, temp,

-         "invalid signature");

-

-   /* Currently the PCS illuminant/adopted white point (the computational

-    * white point) are required to be D50,

-    * however the profile contains a record of the illuminant so perhaps ICC

-    * expects to be able to change this in the future (despite the rationale in

-    * the introduction for using a fixed PCS adopted white.)  Consequently the

-    * following is just a warning.

-    */

-   if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0)

-      (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/,

-         "PCS illuminant is not D50");

-

-   /* The PNG spec requires this:

-    * "If the iCCP chunk is present, the image samples conform to the colour

-    * space represented by the embedded ICC profile as defined by the

-    * International Color Consortium [ICC]. The colour space of the ICC profile

-    * shall be an RGB colour space for colour images (PNG colour types 2, 3, and

-    * 6), or a greyscale colour space for greyscale images (PNG colour types 0

-    * and 4)."

-    *

-    * This checking code ensures the embedded profile (on either read or write)

-    * conforms to the specification requirements.  Notice that an ICC 'gray'

-    * color-space profile contains the information to transform the monochrome

-    * data to XYZ or L*a*b (according to which PCS the profile uses) and this

-    * should be used in preference to the standard libpng K channel replication

-    * into R, G and B channels.

-    *

-    * Previously it was suggested that an RGB profile on grayscale data could be

-    * handled.  However it it is clear that using an RGB profile in this context

-    * must be an error - there is no specification of what it means.  Thus it is

-    * almost certainly more correct to ignore the profile.

-    */

-   temp = png_get_uint_32(profile+16); /* data colour space field */

-   switch (temp)

-   {

-      case 0x52474220: /* 'RGB ' */

-         if (!(color_type & PNG_COLOR_MASK_COLOR))

-            return png_icc_profile_error(png_ptr, colorspace, name, temp,

-               "RGB color space not permitted on grayscale PNG");

-         break;

-

-      case 0x47524159: /* 'GRAY' */

-         if (color_type & PNG_COLOR_MASK_COLOR)

-            return png_icc_profile_error(png_ptr, colorspace, name, temp,

-               "Gray color space not permitted on RGB PNG");

-         break;

-

-      default:

-         return png_icc_profile_error(png_ptr, colorspace, name, temp,

-            "invalid ICC profile color space");

-   }

-

-   /* It is up to the application to check that the profile class matches the

-    * application requirements; the spec provides no guidance, but it's pretty

-    * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer

-    * ('prtr') or 'spac' (for generic color spaces).  Issue a warning in these

-    * cases.  Issue an error for device link or abstract profiles - these don't

-    * contain the records necessary to transform the color-space to anything

-    * other than the target device (and not even that for an abstract profile).

-    * Profiles of these classes may not be embedded in images.

-    */

-   temp = png_get_uint_32(profile+12); /* profile/device class */

-   switch (temp)

-   {

-      case 0x73636E72: /* 'scnr' */

-      case 0x6D6E7472: /* 'mntr' */

-      case 0x70727472: /* 'prtr' */

-      case 0x73706163: /* 'spac' */

-         /* All supported */

-         break;

-

-      case 0x61627374: /* 'abst' */

-         /* May not be embedded in an image */

-         return png_icc_profile_error(png_ptr, colorspace, name, temp,

-            "invalid embedded Abstract ICC profile");

-

-      case 0x6C696E6B: /* 'link' */

-         /* DeviceLink profiles cannnot be interpreted in a non-device specific

-          * fashion, if an app uses the AToB0Tag in the profile the results are

-          * undefined unless the result is sent to the intended device,

-          * therefore a DeviceLink profile should not be found embedded in a

-          * PNG.

-          */

-         return png_icc_profile_error(png_ptr, colorspace, name, temp,

-            "unexpected DeviceLink ICC profile class");

-

-      case 0x6E6D636C: /* 'nmcl' */

-         /* A NamedColor profile is also device specific, however it doesn't

-          * contain an AToB0 tag that is open to misintrepretation.  Almost

-          * certainly it will fail the tests below.

-          */

-         (void)png_icc_profile_error(png_ptr, NULL, name, temp,

-            "unexpected NamedColor ICC profile class");

-         break;

-

-      default:

-         /* To allow for future enhancements to the profile accept unrecognized

-          * profile classes with a warning, these then hit the test below on the

-          * tag content to ensure they are backward compatible with one of the

-          * understood profiles.

-          */

-         (void)png_icc_profile_error(png_ptr, NULL, name, temp,

-            "unrecognized ICC profile class");

-         break;

-   }

-

-   /* For any profile other than a device link one the PCS must be encoded

-    * either in XYZ or Lab.

-    */

-   temp = png_get_uint_32(profile+20);

-   switch (temp)

-   {

-      case 0x58595A20: /* 'XYZ ' */

-      case 0x4C616220: /* 'Lab ' */

-         break;

-

-      default:

-         return png_icc_profile_error(png_ptr, colorspace, name, temp,

-            "unexpected ICC PCS encoding");

-   }

-

-   return 1;

-}

-

-int /* PRIVATE */

-png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,

-   png_const_charp name, png_uint_32 profile_length,

-   png_const_bytep profile /* header plus whole tag table */)

-{

-   png_uint_32 tag_count = png_get_uint_32(profile+128);

-   png_uint_32 itag;

-   png_const_bytep tag = profile+132; /* The first tag */

-

-   /* First scan all the tags in the table and add bits to the icc_info value

-    * (temporarily in 'tags').

-    */

-   for (itag=0; itag < tag_count; ++itag, tag += 12)

-   {

-      png_uint_32 tag_id = png_get_uint_32(tag+0);

-      png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */

-      png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */

-

-      /* The ICC specification does not exclude zero length tags, therefore the

-       * start might actually be anywhere if there is no data, but this would be

-       * a clear abuse of the intent of the standard so the start is checked for

-       * being in range.  All defined tag types have an 8 byte header - a 4 byte

-       * type signature then 0.

-       */

-      if ((tag_start & 3) != 0)

-      {

-         /* CNHP730S.icc shipped with Microsoft Windows 64 violates this, it is

-          * only a warning here because libpng does not care about the

-          * alignment.

-          */

-         (void)png_icc_profile_error(png_ptr, NULL, name, tag_id,

-            "ICC profile tag start not a multiple of 4");

-      }

-

-      /* This is a hard error; potentially it can cause read outside the

-       * profile.

-       */

-      if (tag_start > profile_length || tag_length > profile_length - tag_start)

-         return png_icc_profile_error(png_ptr, colorspace, name, tag_id,

-            "ICC profile tag outside profile");

-   }

-

-   return 1; /* success, maybe with warnings */

-}

-

-#ifdef PNG_sRGB_SUPPORTED

-/* Information about the known ICC sRGB profiles */

-static const struct

-{

-   png_uint_32 adler, crc, length;

-   png_uint_32 md5[4];

-   png_byte    have_md5;

-   png_byte    is_broken;

-   png_uint_16 intent;

-

-#  define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0)

-#  define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\

-      { adler, crc, length, md5, broke, intent },

-

-} png_sRGB_checks[] =

-{

-   /* This data comes from contrib/tools/checksum-icc run on downloads of

-    * all four ICC sRGB profiles from www.color.org.

-    */

-   /* adler32, crc32, MD5[4], intent, date, length, file-name */

-   PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,

-      PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,

-      "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")

-

-   /* ICC sRGB v2 perceptual no black-compensation: */

-   PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,

-      PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,

-      "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")

-

-   PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,

-      PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,

-      "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")

-

-   /* ICC sRGB v4 perceptual */

-   PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,

-      PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,

-      "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")

-

-   /* The following profiles have no known MD5 checksum. If there is a match

-    * on the (empty) MD5 the other fields are used to attempt a match and

-    * a warning is produced.  The first two of these profiles have a 'cprt' tag

-    * which suggests that they were also made by Hewlett Packard.

-    */

-   PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,

-      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,

-      "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")

-

-   /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not

-    * match the D50 PCS illuminant in the header (it is in fact the D65 values,

-    * so the white point is recorded as the un-adapted value.)  The profiles

-    * below only differ in one byte - the intent - and are basically the same as

-    * the previous profile except for the mediaWhitePointTag error and a missing

-    * chromaticAdaptationTag.

-    */

-   PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,

-      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,

-      "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")

-

-   PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,

-      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,

-      "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")

-};

-

-static int

-png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,

-   png_const_bytep profile, uLong adler)

-{

-   /* The quick check is to verify just the MD5 signature and trust the

-    * rest of the data.  Because the profile has already been verified for

-    * correctness this is safe.  png_colorspace_set_sRGB will check the 'intent'

-    * field too, so if the profile has been edited with an intent not defined

-    * by sRGB (but maybe defined by a later ICC specification) the read of

-    * the profile will fail at that point.

-    */

-   png_uint_32 length = 0;

-   png_uint_32 intent = 0x10000; /* invalid */

-#if PNG_sRGB_PROFILE_CHECKS > 1

-   uLong crc = 0; /* the value for 0 length data */

-#endif

-   unsigned int i;

-

-   for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i)

-   {

-      if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] &&

-         png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] &&

-         png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] &&

-         png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3])

-      {

-         /* This may be one of the old HP profiles without an MD5, in that

-          * case we can only use the length and Adler32 (note that these

-          * are not used by default if there is an MD5!)

-          */

-#        if PNG_sRGB_PROFILE_CHECKS == 0

-            if (png_sRGB_checks[i].have_md5)

-               return 1+png_sRGB_checks[i].is_broken;

-#        endif

-

-         /* Profile is unsigned or more checks have been configured in. */

-         if (length == 0)

-         {

-            length = png_get_uint_32(profile);

-            intent = png_get_uint_32(profile+64);

-         }

-

-         /* Length *and* intent must match */

-         if (length == png_sRGB_checks[i].length &&

-            intent == png_sRGB_checks[i].intent)

-         {

-            /* Now calculate the adler32 if not done already. */

-            if (adler == 0)

-            {

-               adler = adler32(0, NULL, 0);

-               adler = adler32(adler, profile, length);

-            }

-

-            if (adler == png_sRGB_checks[i].adler)

-            {

-               /* These basic checks suggest that the data has not been

-                * modified, but if the check level is more than 1 perform

-                * our own crc32 checksum on the data.

-                */

-#              if PNG_sRGB_PROFILE_CHECKS > 1

-                  if (crc == 0)

-                  {

-                     crc = crc32(0, NULL, 0);

-                     crc = crc32(crc, profile, length);

-                  }

-

-                  /* So this check must pass for the 'return' below to happen.

-                   */

-                  if (crc == png_sRGB_checks[i].crc)

-#              endif

-               {

-                  if (png_sRGB_checks[i].is_broken)

-                  {

-                     /* These profiles are known to have bad data that may cause

-                      * problems if they are used, therefore attempt to

-                      * discourage their use, skip the 'have_md5' warning below,

-                      * which is made irrelevant by this error.

-                      */

-                     png_chunk_report(png_ptr, "known incorrect sRGB profile",

-                        PNG_CHUNK_ERROR);

-                  }

-

-                  /* Warn that this being done; this isn't even an error since

-                   * the profile is perfectly valid, but it would be nice if

-                   * people used the up-to-date ones.

-                   */

-                  else if (!png_sRGB_checks[i].have_md5)

-                  {

-                     png_chunk_report(png_ptr,

-                        "out-of-date sRGB profile with no signature",

-                        PNG_CHUNK_WARNING);

-                  }

-

-                  return 1+png_sRGB_checks[i].is_broken;

-               }

-            }

-         }

-

-#        if PNG_sRGB_PROFILE_CHECKS > 0

-            /* The signature matched, but the profile had been changed in some

-             * way.  This is an apparent violation of the ICC terms of use and,

-             * anyway, probably indicates a data error or uninformed hacking.

-             */

-            if (png_sRGB_checks[i].have_md5)

-               png_benign_error(png_ptr,

-                  "copyright violation: edited ICC profile ignored");

-#        endif

-      }

-   }

-

-   return 0; /* no match */

-}

-#endif

-

-#ifdef PNG_sRGB_SUPPORTED

-void /* PRIVATE */

-png_icc_set_sRGB(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_const_bytep profile, uLong adler)

-{

-   /* Is this profile one of the known ICC sRGB profiles?  If it is, just set

-    * the sRGB information.

-    */

-   if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler))

-      (void)png_colorspace_set_sRGB(png_ptr, colorspace,

-         (int)/*already checked*/png_get_uint_32(profile+64));

-}

-#endif /* PNG_READ_sRGB_SUPPORTED */

-

-int /* PRIVATE */

-png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,

-   png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,

-   int color_type)

-{

-   if (colorspace->flags & PNG_COLORSPACE_INVALID)

-      return 0;

-

-   if (png_icc_check_length(png_ptr, colorspace, name, profile_length) &&

-      png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,

-         color_type) &&

-      png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,

-         profile))

-   {

-#     ifdef PNG_sRGB_SUPPORTED

-         /* If no sRGB support, don't try storing sRGB information */

-         png_icc_set_sRGB(png_ptr, colorspace, profile, 0);

-#     endif

-      return 1;

-   }

-

-   /* Failure case */

-   return 0;

-}

-#endif /* iCCP */

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-void /* PRIVATE */

-png_colorspace_set_rgb_coefficients(png_structrp png_ptr)

-{

-   /* Set the rgb_to_gray coefficients from the colorspace. */

-   if (!png_ptr->rgb_to_gray_coefficients_set &&

-      (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)

-   {

-      /* png_set_background has not been called, get the coefficients from the Y

-       * values of the colorspace colorants.

-       */

-      png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y;

-      png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y;

-      png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y;

-      png_fixed_point total = r+g+b;

-

-      if (total > 0 &&

-         r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 &&

-         g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 &&

-         b >= 0 && png_muldiv(&b, b, 32768, total) && 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");

-

-         else

-         {

-            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");

-   }

-}

-#endif

-

-#endif /* COLORSPACE */

-

-void /* PRIVATE */

-png_check_IHDR(png_const_structrp png_ptr,

-   png_uint_32 width, png_uint_32 height, int bit_depth,

-   int color_type, int interlace_type, int compression_type,

-   int filter_type)

-{

-   int error = 0;

-

-   /* Check for width and height valid values */

-   if (width == 0)

-   {

-      png_warning(png_ptr, "Image width is zero in IHDR");

-      error = 1;

-   }

-

-   if (height == 0)

-   {

-      png_warning(png_ptr, "Image height is zero in IHDR");

-      error = 1;

-   }

-

-#  ifdef PNG_SET_USER_LIMITS_SUPPORTED

-   if (width > png_ptr->user_width_max)

-

-#  else

-   if (width > PNG_USER_WIDTH_MAX)

-#  endif

-   {

-      png_warning(png_ptr, "Image width exceeds user limit in IHDR");

-      error = 1;

-   }

-

-#  ifdef PNG_SET_USER_LIMITS_SUPPORTED

-   if (height > png_ptr->user_height_max)

-#  else

-   if (height > PNG_USER_HEIGHT_MAX)

-#  endif

-   {

-      png_warning(png_ptr, "Image height exceeds user limit in IHDR");

-      error = 1;

-   }

-

-   if (width > PNG_UINT_31_MAX)

-   {

-      png_warning(png_ptr, "Invalid image width in IHDR");

-      error = 1;

-   }

-

-   if (height > PNG_UINT_31_MAX)

-   {

-      png_warning(png_ptr, "Invalid image height in IHDR");

-      error = 1;

-   }

-

-   if (width > (PNG_UINT_32_MAX

-                 >> 3)      /* 8-byte RGBA pixels */

-                 - 48       /* bigrowbuf hack */

-                 - 1        /* filter byte */

-                 - 7*8      /* rounding of width to multiple of 8 pixels */

-                 - 8)       /* extra max_pixel_depth pad */

-      png_warning(png_ptr, "Width is too large for libpng to process pixels");

-

-   /* Check other values */

-   if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&

-       bit_depth != 8 && bit_depth != 16)

-   {

-      png_warning(png_ptr, "Invalid bit depth in IHDR");

-      error = 1;

-   }

-

-   if (color_type < 0 || color_type == 1 ||

-       color_type == 5 || color_type > 6)

-   {

-      png_warning(png_ptr, "Invalid color type in IHDR");

-      error = 1;

-   }

-

-   if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) ||

-       ((color_type == PNG_COLOR_TYPE_RGB ||

-         color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||

-         color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))

-   {

-      png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR");

-      error = 1;

-   }

-

-   if (interlace_type >= PNG_INTERLACE_LAST)

-   {

-      png_warning(png_ptr, "Unknown interlace method in IHDR");

-      error = 1;

-   }

-

-   if (compression_type != PNG_COMPRESSION_TYPE_BASE)

-   {

-      png_warning(png_ptr, "Unknown compression method in IHDR");

-      error = 1;

-   }

-

-#  ifdef PNG_MNG_FEATURES_SUPPORTED

-   /* Accept filter_method 64 (intrapixel differencing) only if

-    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and

-    * 2. Libpng did not read a PNG signature (this filter_method is only

-    *    used in PNG datastreams that are embedded in MNG datastreams) and

-    * 3. The application called png_permit_mng_features with a mask that

-    *    included PNG_FLAG_MNG_FILTER_64 and

-    * 4. The filter_method is 64 and

-    * 5. The color_type is RGB or RGBA

-    */

-   if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) &&

-       png_ptr->mng_features_permitted)

-      png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");

-

-   if (filter_type != PNG_FILTER_TYPE_BASE)

-   {

-      if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&

-          (filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&

-          ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&

-          (color_type == PNG_COLOR_TYPE_RGB ||

-          color_type == PNG_COLOR_TYPE_RGB_ALPHA)))

-      {

-         png_warning(png_ptr, "Unknown filter method in IHDR");

-         error = 1;

-      }

-

-      if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE)

-      {

-         png_warning(png_ptr, "Invalid filter method in IHDR");

-         error = 1;

-      }

-   }

-

-#  else

-   if (filter_type != PNG_FILTER_TYPE_BASE)

-   {

-      png_warning(png_ptr, "Unknown filter method in IHDR");

-      error = 1;

-   }

-#  endif

-

-   if (error == 1)

-      png_error(png_ptr, "Invalid IHDR data");

-}

-

-#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)

-/* ASCII to fp functions */

-/* Check an ASCII formated floating point value, see the more detailed

- * comments in pngpriv.h

- */

-/* The following is used internally to preserve the sticky flags */

-#define png_fp_add(state, flags) ((state) |= (flags))

-#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY))

-

-int /* PRIVATE */

-png_check_fp_number(png_const_charp string, png_size_t size, int *statep,

-   png_size_tp whereami)

-{

-   int state = *statep;

-   png_size_t i = *whereami;

-

-   while (i < size)

-   {

-      int type;

-      /* First find the type of the next character */

-      switch (string[i])

-      {

-      case 43:  type = PNG_FP_SAW_SIGN;                   break;

-      case 45:  type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break;

-      case 46:  type = PNG_FP_SAW_DOT;                    break;

-      case 48:  type = PNG_FP_SAW_DIGIT;                  break;

-      case 49: case 50: case 51: case 52:

-      case 53: case 54: case 55: case 56:

-      case 57:  type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break;

-      case 69:

-      case 101: type = PNG_FP_SAW_E;                      break;

-      default:  goto PNG_FP_End;

-      }

-

-      /* Now deal with this type according to the current

-       * state, the type is arranged to not overlap the

-       * bits of the PNG_FP_STATE.

-       */

-      switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY))

-      {

-      case PNG_FP_INTEGER + PNG_FP_SAW_SIGN:

-         if (state & PNG_FP_SAW_ANY)

-            goto PNG_FP_End; /* not a part of the number */

-

-         png_fp_add(state, type);

-         break;

-

-      case PNG_FP_INTEGER + PNG_FP_SAW_DOT:

-         /* Ok as trailer, ok as lead of fraction. */

-         if (state & PNG_FP_SAW_DOT) /* two dots */

-            goto PNG_FP_End;

-

-         else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */

-            png_fp_add(state, type);

-

-         else

-            png_fp_set(state, PNG_FP_FRACTION | type);

-

-         break;

-

-      case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT:

-         if (state & PNG_FP_SAW_DOT) /* delayed fraction */

-            png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);

-

-         png_fp_add(state, type | PNG_FP_WAS_VALID);

-

-         break;

-

-      case PNG_FP_INTEGER + PNG_FP_SAW_E:

-         if ((state & PNG_FP_SAW_DIGIT) == 0)

-            goto PNG_FP_End;

-

-         png_fp_set(state, PNG_FP_EXPONENT);

-

-         break;

-

-   /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN:

-         goto PNG_FP_End; ** no sign in fraction */

-

-   /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT:

-         goto PNG_FP_End; ** Because SAW_DOT is always set */

-

-      case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT:

-         png_fp_add(state, type | PNG_FP_WAS_VALID);

-         break;

-

-      case PNG_FP_FRACTION + PNG_FP_SAW_E:

-         /* This is correct because the trailing '.' on an

-          * integer is handled above - so we can only get here

-          * with the sequence ".E" (with no preceding digits).

-          */

-         if ((state & PNG_FP_SAW_DIGIT) == 0)

-            goto PNG_FP_End;

-

-         png_fp_set(state, PNG_FP_EXPONENT);

-

-         break;

-

-      case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN:

-         if (state & PNG_FP_SAW_ANY)

-            goto PNG_FP_End; /* not a part of the number */

-

-         png_fp_add(state, PNG_FP_SAW_SIGN);

-

-         break;

-

-   /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT:

-         goto PNG_FP_End; */

-

-      case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT:

-         png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID);

-

-         break;

-

-   /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E:

-         goto PNG_FP_End; */

-

-      default: goto PNG_FP_End; /* I.e. break 2 */

-      }

-

-      /* The character seems ok, continue. */

-      ++i;

-   }

-

-PNG_FP_End:

-   /* Here at the end, update the state and return the correct

-    * return code.

-    */

-   *statep = state;

-   *whereami = i;

-

-   return (state & PNG_FP_SAW_DIGIT) != 0;

-}

-

-

-/* The same but for a complete string. */

-int

-png_check_fp_string(png_const_charp string, png_size_t size)

-{

-   int        state=0;

-   png_size_t char_index=0;

-

-   if (png_check_fp_number(string, size, &state, &char_index) &&

-      (char_index == size || string[char_index] == 0))

-      return state /* must be non-zero - see above */;

-

-   return 0; /* i.e. fail */

-}

-#endif /* pCAL or sCAL */

-

-#ifdef PNG_sCAL_SUPPORTED

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-/* Utility used below - a simple accurate power of ten from an integral

- * exponent.

- */

-static double

-png_pow10(int power)

-{

-   int recip = 0;

-   double d = 1;

-

-   /* Handle negative exponent with a reciprocal at the end because

-    * 10 is exact whereas .1 is inexact in base 2

-    */

-   if (power < 0)

-   {

-      if (power < DBL_MIN_10_EXP) return 0;

-      recip = 1, power = -power;

-   }

-

-   if (power > 0)

-   {

-      /* Decompose power bitwise. */

-      double mult = 10;

-      do

-      {

-         if (power & 1) d *= mult;

-         mult *= mult;

-         power >>= 1;

-      }

-      while (power > 0);

-

-      if (recip) d = 1/d;

-   }

-   /* else power is 0 and d is 1 */

-

-   return d;

-}

-

-/* Function to format a floating point value in ASCII with a given

- * precision.

- */

-void /* PRIVATE */

-png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,

-    double fp, unsigned int precision)

-{

-   /* We use standard functions from math.h, but not printf because

-    * that would require stdio.  The caller must supply a buffer of

-    * sufficient size or we will png_error.  The tests on size and

-    * the space in ascii[] consumed are indicated below.

-    */

-   if (precision < 1)

-      precision = DBL_DIG;

-

-   /* Enforce the limit of the implementation precision too. */

-   if (precision > DBL_DIG+1)

-      precision = DBL_DIG+1;

-

-   /* Basic sanity checks */

-   if (size >= precision+5) /* See the requirements below. */

-   {

-      if (fp < 0)

-      {

-         fp = -fp;

-         *ascii++ = 45; /* '-'  PLUS 1 TOTAL 1 */

-         --size;

-      }

-

-      if (fp >= DBL_MIN && fp <= DBL_MAX)

-      {

-         int exp_b10;       /* A base 10 exponent */

-         double base;   /* 10^exp_b10 */

-

-         /* First extract a base 10 exponent of the number,

-          * the calculation below rounds down when converting

-          * from base 2 to base 10 (multiply by log10(2) -

-          * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to

-          * be increased.  Note that the arithmetic shift

-          * performs a floor() unlike C arithmetic - using a

-          * C multiply would break the following for negative

-          * exponents.

-          */

-         (void)frexp(fp, &exp_b10); /* exponent to base 2 */

-

-         exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */

-

-         /* Avoid underflow here. */

-         base = png_pow10(exp_b10); /* May underflow */

-

-         while (base < DBL_MIN || base < fp)

-         {

-            /* And this may overflow. */

-            double test = png_pow10(exp_b10+1);

-

-            if (test <= DBL_MAX)

-               ++exp_b10, base = test;

-

-            else

-               break;

-         }

-

-         /* Normalize fp and correct exp_b10, after this fp is in the

-          * range [.1,1) and exp_b10 is both the exponent and the digit

-          * *before* which the decimal point should be inserted

-          * (starting with 0 for the first digit).  Note that this

-          * works even if 10^exp_b10 is out of range because of the

-          * test on DBL_MAX above.

-          */

-         fp /= base;

-         while (fp >= 1) fp /= 10, ++exp_b10;

-

-         /* Because of the code above fp may, at this point, be

-          * less than .1, this is ok because the code below can

-          * handle the leading zeros this generates, so no attempt

-          * is made to correct that here.

-          */

-

-         {

-            int czero, clead, cdigits;

-            char exponent[10];

-

-            /* Allow up to two leading zeros - this will not lengthen

-             * the number compared to using E-n.

-             */

-            if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */

-            {

-               czero = -exp_b10; /* PLUS 2 digits: TOTAL 3 */

-               exp_b10 = 0;      /* Dot added below before first output. */

-            }

-            else

-               czero = 0;    /* No zeros to add */

-

-            /* Generate the digit list, stripping trailing zeros and

-             * inserting a '.' before a digit if the exponent is 0.

-             */

-            clead = czero; /* Count of leading zeros */

-            cdigits = 0;   /* Count of digits in list. */

-

-            do

-            {

-               double d;

-

-               fp *= 10;

-               /* Use modf here, not floor and subtract, so that

-                * the separation is done in one step.  At the end

-                * of the loop don't break the number into parts so

-                * that the final digit is rounded.

-                */

-               if (cdigits+czero-clead+1 < (int)precision)

-                  fp = modf(fp, &d);

-

-               else

-               {

-                  d = floor(fp + .5);

-

-                  if (d > 9)

-                  {

-                     /* Rounding up to 10, handle that here. */

-                     if (czero > 0)

-                     {

-                        --czero, d = 1;

-                        if (cdigits == 0) --clead;

-                     }

-                     else

-                     {

-                        while (cdigits > 0 && d > 9)

-                        {

-                           int ch = *--ascii;

-

-                           if (exp_b10 != (-1))

-                              ++exp_b10;

-

-                           else if (ch == 46)

-                           {

-                              ch = *--ascii, ++size;

-                              /* Advance exp_b10 to '1', so that the

-                               * decimal point happens after the

-                               * previous digit.

-                               */

-                              exp_b10 = 1;

-                           }

-

-                           --cdigits;

-                           d = ch - 47;  /* I.e. 1+(ch-48) */

-                        }

-

-                        /* Did we reach the beginning? If so adjust the

-                         * exponent but take into account the leading

-                         * decimal point.

-                         */

-                        if (d > 9)  /* cdigits == 0 */

-                        {

-                           if (exp_b10 == (-1))

-                           {

-                              /* Leading decimal point (plus zeros?), if

-                               * we lose the decimal point here it must

-                               * be reentered below.

-                               */

-                              int ch = *--ascii;

-

-                              if (ch == 46)

-                                 ++size, exp_b10 = 1;

-

-                              /* Else lost a leading zero, so 'exp_b10' is

-                               * still ok at (-1)

-                               */

-                           }

-                           else

-                              ++exp_b10;

-

-                           /* In all cases we output a '1' */

-                           d = 1;

-                        }

-                     }

-                  }

-                  fp = 0; /* Guarantees termination below. */

-               }

-

-               if (d == 0)

-               {

-                  ++czero;

-                  if (cdigits == 0) ++clead;

-               }

-               else

-               {

-                  /* Included embedded zeros in the digit count. */

-                  cdigits += czero - clead;

-                  clead = 0;

-

-                  while (czero > 0)

-                  {

-                     /* exp_b10 == (-1) means we just output the decimal

-                      * place - after the DP don't adjust 'exp_b10' any

-                      * more!

-                      */

-                     if (exp_b10 != (-1))

-                     {

-                        if (exp_b10 == 0) *ascii++ = 46, --size;

-                        /* PLUS 1: TOTAL 4 */

-                        --exp_b10;

-                     }

-                     *ascii++ = 48, --czero;

-                  }

-

-                  if (exp_b10 != (-1))

-                  {

-                     if (exp_b10 == 0) *ascii++ = 46, --size; /* counted

-                                                                 above */

-                     --exp_b10;

-                  }

-                  *ascii++ = (char)(48 + (int)d), ++cdigits;

-               }

-            }

-            while (cdigits+czero-clead < (int)precision && fp > DBL_MIN);

-

-            /* The total output count (max) is now 4+precision */

-

-            /* Check for an exponent, if we don't need one we are

-             * done and just need to terminate the string.  At

-             * this point exp_b10==(-1) is effectively if flag - it got

-             * to '-1' because of the decrement after outputing

-             * the decimal point above (the exponent required is

-             * *not* -1!)

-             */

-            if (exp_b10 >= (-1) && exp_b10 <= 2)

-            {

-               /* The following only happens if we didn't output the

-                * leading zeros above for negative exponent, so this

-                * doest add to the digit requirement.  Note that the

-                * two zeros here can only be output if the two leading

-                * zeros were *not* output, so this doesn't increase

-                * the output count.

-                */

-               while (--exp_b10 >= 0) *ascii++ = 48;

-

-               *ascii = 0;

-

-               /* Total buffer requirement (including the '\0') is

-                * 5+precision - see check at the start.

-                */

-               return;

-            }

-

-            /* Here if an exponent is required, adjust size for

-             * the digits we output but did not count.  The total

-             * digit output here so far is at most 1+precision - no

-             * decimal point and no leading or trailing zeros have

-             * been output.

-             */

-            size -= cdigits;

-

-            *ascii++ = 69, --size;    /* 'E': PLUS 1 TOTAL 2+precision */

-

-            /* The following use of an unsigned temporary avoids ambiguities in

-             * the signed arithmetic on exp_b10 and permits GCC at least to do

-             * better optimization.

-             */

-            {

-               unsigned int uexp_b10;

-

-               if (exp_b10 < 0)

-               {

-                  *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */

-                  uexp_b10 = -exp_b10;

-               }

-

-               else

-                  uexp_b10 = exp_b10;

-

-               cdigits = 0;

-

-               while (uexp_b10 > 0)

-               {

-                  exponent[cdigits++] = (char)(48 + uexp_b10 % 10);

-                  uexp_b10 /= 10;

-               }

-            }

-

-            /* Need another size check here for the exponent digits, so

-             * this need not be considered above.

-             */

-            if ((int)size > cdigits)

-            {

-               while (cdigits > 0) *ascii++ = exponent[--cdigits];

-

-               *ascii = 0;

-

-               return;

-            }

-         }

-      }

-      else if (!(fp >= DBL_MIN))

-      {

-         *ascii++ = 48; /* '0' */

-         *ascii = 0;

-         return;

-      }

-      else

-      {

-         *ascii++ = 105; /* 'i' */

-         *ascii++ = 110; /* 'n' */

-         *ascii++ = 102; /* 'f' */

-         *ascii = 0;

-         return;

-      }

-   }

-

-   /* Here on buffer too small. */

-   png_error(png_ptr, "ASCII conversion buffer too small");

-}

-

-#  endif /* FLOATING_POINT */

-

-#  ifdef PNG_FIXED_POINT_SUPPORTED

-/* Function to format a fixed point value in ASCII.

- */

-void /* PRIVATE */

-png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,

-    png_size_t size, png_fixed_point fp)

-{

-   /* Require space for 10 decimal digits, a decimal point, a minus sign and a

-    * trailing \0, 13 characters:

-    */

-   if (size > 12)

-   {

-      png_uint_32 num;

-

-      /* Avoid overflow here on the minimum integer. */

-      if (fp < 0)

-         *ascii++ = 45, --size, num = -fp;

-      else

-         num = fp;

-

-      if (num <= 0x80000000) /* else overflowed */

-      {

-         unsigned int ndigits = 0, first = 16 /* flag value */;

-         char digits[10];

-

-         while (num)

-         {

-            /* Split the low digit off num: */

-            unsigned int tmp = num/10;

-            num -= tmp*10;

-            digits[ndigits++] = (char)(48 + num);

-            /* Record the first non-zero digit, note that this is a number

-             * starting at 1, it's not actually the array index.

-             */

-            if (first == 16 && num > 0)

-               first = ndigits;

-            num = tmp;

-         }

-

-         if (ndigits > 0)

-         {

-            while (ndigits > 5) *ascii++ = digits[--ndigits];

-            /* The remaining digits are fractional digits, ndigits is '5' or

-             * smaller at this point.  It is certainly not zero.  Check for a

-             * non-zero fractional digit:

-             */

-            if (first <= 5)

-            {

-               unsigned int i;

-               *ascii++ = 46; /* decimal point */

-               /* ndigits may be <5 for small numbers, output leading zeros

-                * then ndigits digits to first:

-                */

-               i = 5;

-               while (ndigits < i) *ascii++ = 48, --i;

-               while (ndigits >= first) *ascii++ = digits[--ndigits];

-               /* Don't output the trailing zeros! */

-            }

-         }

-         else

-            *ascii++ = 48;

-

-         /* And null terminate the string: */

-         *ascii = 0;

-         return;

-      }

-   }

-

-   /* Here on buffer too small. */

-   png_error(png_ptr, "ASCII conversion buffer too small");

-}

-#   endif /* FIXED_POINT */

-#endif /* READ_SCAL */

-

-#if defined(PNG_FLOATING_POINT_SUPPORTED) && \

-   !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \

-   (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \

-   defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \

-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \

-   (defined(PNG_sCAL_SUPPORTED) && \

-   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))

-png_fixed_point

-png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text)

-{

-   double r = floor(100000 * fp + .5);

-

-   if (r > 2147483647. || r < -2147483648.)

-      png_fixed_error(png_ptr, text);

-

-   return (png_fixed_point)r;

-}

-#endif

-

-#if defined(PNG_READ_GAMMA_SUPPORTED) || \

-    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)

-/* muldiv functions */

-/* This API takes signed arguments and rounds the result to the nearest

- * integer (or, for a fixed point number - the standard argument - to

- * the nearest .00001).  Overflow and divide by zero are signalled in

- * the result, a boolean - true on success, false on overflow.

- */

-int

-png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,

-    png_int_32 divisor)

-{

-   /* Return a * times / divisor, rounded. */

-   if (divisor != 0)

-   {

-      if (a == 0 || times == 0)

-      {

-         *res = 0;

-         return 1;

-      }

-      else

-      {

-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-         double r = a;

-         r *= times;

-         r /= divisor;

-         r = floor(r+.5);

-

-         /* A png_fixed_point is a 32-bit integer. */

-         if (r <= 2147483647. && r >= -2147483648.)

-         {

-            *res = (png_fixed_point)r;

-            return 1;

-         }

-#else

-         int negative = 0;

-         png_uint_32 A, T, D;

-         png_uint_32 s16, s32, s00;

-

-         if (a < 0)

-            negative = 1, A = -a;

-         else

-            A = a;

-

-         if (times < 0)

-            negative = !negative, T = -times;

-         else

-            T = times;

-

-         if (divisor < 0)

-            negative = !negative, D = -divisor;

-         else

-            D = divisor;

-

-         /* Following can't overflow because the arguments only

-          * have 31 bits each, however the result may be 32 bits.

-          */

-         s16 = (A >> 16) * (T & 0xffff) +

-                           (A & 0xffff) * (T >> 16);

-         /* Can't overflow because the a*times bit is only 30

-          * bits at most.

-          */

-         s32 = (A >> 16) * (T >> 16) + (s16 >> 16);

-         s00 = (A & 0xffff) * (T & 0xffff);

-

-         s16 = (s16 & 0xffff) << 16;

-         s00 += s16;

-

-         if (s00 < s16)

-            ++s32; /* carry */

-

-         if (s32 < D) /* else overflow */

-         {

-            /* s32.s00 is now the 64-bit product, do a standard

-             * division, we know that s32 < D, so the maximum

-             * required shift is 31.

-             */

-            int bitshift = 32;

-            png_fixed_point result = 0; /* NOTE: signed */

-

-            while (--bitshift >= 0)

-            {

-               png_uint_32 d32, d00;

-

-               if (bitshift > 0)

-                  d32 = D >> (32-bitshift), d00 = D << bitshift;

-

-               else

-                  d32 = 0, d00 = D;

-

-               if (s32 > d32)

-               {

-                  if (s00 < d00) --s32; /* carry */

-                  s32 -= d32, s00 -= d00, result += 1<<bitshift;

-               }

-

-               else

-                  if (s32 == d32 && s00 >= d00)

-                     s32 = 0, s00 -= d00, result += 1<<bitshift;

-            }

-

-            /* Handle the rounding. */

-            if (s00 >= (D >> 1))

-               ++result;

-

-            if (negative)

-               result = -result;

-

-            /* Check for overflow. */

-            if ((negative && result <= 0) || (!negative && result >= 0))

-            {

-               *res = result;

-               return 1;

-            }

-         }

-#endif

-      }

-   }

-

-   return 0;

-}

-#endif /* READ_GAMMA || INCH_CONVERSIONS */

-

-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)

-/* The following is for when the caller doesn't much care about the

- * result.

- */

-png_fixed_point

-png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times,

-    png_int_32 divisor)

-{

-   png_fixed_point result;

-

-   if (png_muldiv(&result, a, times, divisor))

-      return result;

-

-   png_warning(png_ptr, "fixed point overflow ignored");

-   return 0;

-}

-#endif

-

-#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */

-/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */

-png_fixed_point

-png_reciprocal(png_fixed_point a)

-{

-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-   double r = floor(1E10/a+.5);

-

-   if (r <= 2147483647. && r >= -2147483648.)

-      return (png_fixed_point)r;

-#else

-   png_fixed_point res;

-

-   if (png_muldiv(&res, 100000, 100000, a))

-      return res;

-#endif

-

-   return 0; /* error/overflow */

-}

-

-/* This is the shared test on whether a gamma value is 'significant' - whether

- * it is worth doing gamma correction.

- */

-int /* PRIVATE */

-png_gamma_significant(png_fixed_point gamma_val)

-{

-   return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED ||

-       gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;

-}

-#endif

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-/* A local convenience routine. */

-static png_fixed_point

-png_product2(png_fixed_point a, png_fixed_point b)

-{

-   /* The required result is 1/a * 1/b; the following preserves accuracy. */

-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-   double r = a * 1E-5;

-   r *= b;

-   r = floor(r+.5);

-

-   if (r <= 2147483647. && r >= -2147483648.)

-      return (png_fixed_point)r;

-#else

-   png_fixed_point res;

-

-   if (png_muldiv(&res, a, b, 100000))

-      return res;

-#endif

-

-   return 0; /* overflow */

-}

-

-/* The inverse of the above. */

-png_fixed_point

-png_reciprocal2(png_fixed_point a, png_fixed_point b)

-{

-   /* The required result is 1/a * 1/b; the following preserves accuracy. */

-#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-   double r = 1E15/a;

-   r /= b;

-   r = floor(r+.5);

-

-   if (r <= 2147483647. && r >= -2147483648.)

-      return (png_fixed_point)r;

-#else

-   /* This may overflow because the range of png_fixed_point isn't symmetric,

-    * but this API is only used for the product of file and screen gamma so it

-    * doesn't matter that the smallest number it can produce is 1/21474, not

-    * 1/100000

-    */

-   png_fixed_point res = png_product2(a, b);

-

-   if (res != 0)

-      return png_reciprocal(res);

-#endif

-

-   return 0; /* overflow */

-}

-#endif /* READ_GAMMA */

-

-#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */

-#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED

-/* Fixed point gamma.

- *

- * The code to calculate the tables used below can be found in the shell script

- * contrib/tools/intgamma.sh

- *

- * To calculate gamma this code implements fast log() and exp() calls using only

- * fixed point arithmetic.  This code has sufficient precision for either 8-bit

- * or 16-bit sample values.

- *

- * The tables used here were calculated using simple 'bc' programs, but C double

- * precision floating point arithmetic would work fine.

- *

- * 8-bit log table

- *   This is a table of -log(value/255)/log(2) for 'value' in the range 128 to

- *   255, so it's the base 2 logarithm of a normalized 8-bit floating point

- *   mantissa.  The numbers are 32-bit fractions.

- */

-static const png_uint_32

-png_8bit_l2[128] =

-{

-   4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,

-   3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,

-   3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,

-   3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,

-   3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,

-   2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,

-   2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,

-   2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,

-   2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,

-   2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,

-   1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,

-   1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,

-   1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,

-   1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,

-   1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,

-   971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,

-   803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,

-   639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,

-   479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,

-   324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,

-   172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,

-   24347096U, 0U

-

-#if 0

-   /* The following are the values for 16-bit tables - these work fine for the

-    * 8-bit conversions but produce very slightly larger errors in the 16-bit

-    * log (about 1.2 as opposed to 0.7 absolute error in the final value).  To

-    * use these all the shifts below must be adjusted appropriately.

-    */

-   65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,

-   57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,

-   50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,

-   43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,

-   37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,

-   31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,

-   25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,

-   20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,

-   15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,

-   10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,

-   6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,

-   1119, 744, 372

-#endif

-};

-

-static png_int_32

-png_log8bit(unsigned int x)

-{

-   unsigned int lg2 = 0;

-   /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,

-    * because the log is actually negate that means adding 1.  The final

-    * returned value thus has the range 0 (for 255 input) to 7.994 (for 1

-    * input), return -1 for the overflow (log 0) case, - so the result is

-    * always at most 19 bits.

-    */

-   if ((x &= 0xff) == 0)

-      return -1;

-

-   if ((x & 0xf0) == 0)

-      lg2  = 4, x <<= 4;

-

-   if ((x & 0xc0) == 0)

-      lg2 += 2, x <<= 2;

-

-   if ((x & 0x80) == 0)

-      lg2 += 1, x <<= 1;

-

-   /* result is at most 19 bits, so this cast is safe: */

-   return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16));

-}

-

-/* The above gives exact (to 16 binary places) log2 values for 8-bit images,

- * for 16-bit images we use the most significant 8 bits of the 16-bit value to

- * get an approximation then multiply the approximation by a correction factor

- * determined by the remaining up to 8 bits.  This requires an additional step

- * in the 16-bit case.

- *

- * We want log2(value/65535), we have log2(v'/255), where:

- *

- *    value = v' * 256 + v''

- *          = v' * f

- *

- * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128

- * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less

- * than 258.  The final factor also needs to correct for the fact that our 8-bit

- * value is scaled by 255, whereas the 16-bit values must be scaled by 65535.

- *

- * This gives a final formula using a calculated value 'x' which is value/v' and

- * scaling by 65536 to match the above table:

- *

- *   log2(x/257) * 65536

- *

- * Since these numbers are so close to '1' we can use simple linear

- * interpolation between the two end values 256/257 (result -368.61) and 258/257

- * (result 367.179).  The values used below are scaled by a further 64 to give

- * 16-bit precision in the interpolation:

- *

- * Start (256): -23591

- * Zero  (257):      0

- * End   (258):  23499

- */

-static png_int_32

-png_log16bit(png_uint_32 x)

-{

-   unsigned int lg2 = 0;

-

-   /* As above, but now the input has 16 bits. */

-   if ((x &= 0xffff) == 0)

-      return -1;

-

-   if ((x & 0xff00) == 0)

-      lg2  = 8, x <<= 8;

-

-   if ((x & 0xf000) == 0)

-      lg2 += 4, x <<= 4;

-

-   if ((x & 0xc000) == 0)

-      lg2 += 2, x <<= 2;

-

-   if ((x & 0x8000) == 0)

-      lg2 += 1, x <<= 1;

-

-   /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional

-    * value.

-    */

-   lg2 <<= 28;

-   lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4;

-

-   /* Now we need to interpolate the factor, this requires a division by the top

-    * 8 bits.  Do this with maximum precision.

-    */

-   x = ((x << 16) + (x >> 9)) / (x >> 8);

-

-   /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,

-    * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly

-    * 16 bits to interpolate to get the low bits of the result.  Round the

-    * answer.  Note that the end point values are scaled by 64 to retain overall

-    * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust

-    * the overall scaling by 6-12.  Round at every step.

-    */

-   x -= 1U << 24;

-

-   if (x <= 65536U) /* <= '257' */

-      lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12);

-

-   else

-      lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);

-

-   /* Safe, because the result can't have more than 20 bits: */

-   return (png_int_32)((lg2 + 2048) >> 12);

-}

-

-/* The 'exp()' case must invert the above, taking a 20-bit fixed point

- * logarithmic value and returning a 16 or 8-bit number as appropriate.  In

- * each case only the low 16 bits are relevant - the fraction - since the

- * integer bits (the top 4) simply determine a shift.

- *

- * The worst case is the 16-bit distinction between 65535 and 65534, this

- * requires perhaps spurious accuracty in the decoding of the logarithm to

- * distinguish log2(65535/65534.5) - 10^-5 or 17 bits.  There is little chance

- * of getting this accuracy in practice.

- *

- * To deal with this the following exp() function works out the exponent of the

- * frational part of the logarithm by using an accurate 32-bit value from the

- * top four fractional bits then multiplying in the remaining bits.

- */

-static const png_uint_32

-png_32bit_exp[16] =

-{

-   /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */

-   4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,

-   3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,

-   2553802834U, 2445529972U, 2341847524U, 2242560872U

-};

-

-/* Adjustment table; provided to explain the numbers in the code below. */

-#if 0

-for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}

-   11 44937.64284865548751208448

-   10 45180.98734845585101160448

-    9 45303.31936980687359311872

-    8 45364.65110595323018870784

-    7 45395.35850361789624614912

-    6 45410.72259715102037508096

-    5 45418.40724413220722311168

-    4 45422.25021786898173001728

-    3 45424.17186732298419044352

-    2 45425.13273269940811464704

-    1 45425.61317555035558641664

-    0 45425.85339951654943850496

-#endif

-

-static png_uint_32

-png_exp(png_fixed_point x)

-{

-   if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */

-   {

-      /* Obtain a 4-bit approximation */

-      png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf];

-

-      /* Incorporate the low 12 bits - these decrease the returned value by

-       * multiplying by a number less than 1 if the bit is set.  The multiplier

-       * is determined by the above table and the shift. Notice that the values

-       * converge on 45426 and this is used to allow linear interpolation of the

-       * low bits.

-       */

-      if (x & 0x800)

-         e -= (((e >> 16) * 44938U) +  16U) >> 5;

-

-      if (x & 0x400)

-         e -= (((e >> 16) * 45181U) +  32U) >> 6;

-

-      if (x & 0x200)

-         e -= (((e >> 16) * 45303U) +  64U) >> 7;

-

-      if (x & 0x100)

-         e -= (((e >> 16) * 45365U) + 128U) >> 8;

-

-      if (x & 0x080)

-         e -= (((e >> 16) * 45395U) + 256U) >> 9;

-

-      if (x & 0x040)

-         e -= (((e >> 16) * 45410U) + 512U) >> 10;

-

-      /* And handle the low 6 bits in a single block. */

-      e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;

-

-      /* Handle the upper bits of x. */

-      e >>= x >> 16;

-      return e;

-   }

-

-   /* Check for overflow */

-   if (x <= 0)

-      return png_32bit_exp[0];

-

-   /* Else underflow */

-   return 0;

-}

-

-static png_byte

-png_exp8bit(png_fixed_point lg2)

-{

-   /* Get a 32-bit value: */

-   png_uint_32 x = png_exp(lg2);

-

-   /* Convert the 32-bit value to 0..255 by multiplying by 256-1, note that the

-    * second, rounding, step can't overflow because of the first, subtraction,

-    * step.

-    */

-   x -= x >> 8;

-   return (png_byte)((x + 0x7fffffU) >> 24);

-}

-

-static png_uint_16

-png_exp16bit(png_fixed_point lg2)

-{

-   /* Get a 32-bit value: */

-   png_uint_32 x = png_exp(lg2);

-

-   /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */

-   x -= x >> 16;

-   return (png_uint_16)((x + 32767U) >> 16);

-}

-#endif /* FLOATING_ARITHMETIC */

-

-png_byte

-png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)

-{

-   if (value > 0 && value < 255)

-   {

-#     ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-         double r = floor(255*pow(value/255.,gamma_val*.00001)+.5);

-         return (png_byte)r;

-#     else

-         png_int_32 lg2 = png_log8bit(value);

-         png_fixed_point res;

-

-         if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1))

-            return png_exp8bit(res);

-

-         /* Overflow. */

-         value = 0;

-#     endif

-   }

-

-   return (png_byte)value;

-}

-

-png_uint_16

-png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)

-{

-   if (value > 0 && value < 65535)

-   {

-#     ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-         double r = floor(65535*pow(value/65535.,gamma_val*.00001)+.5);

-         return (png_uint_16)r;

-#     else

-         png_int_32 lg2 = png_log16bit(value);

-         png_fixed_point res;

-

-         if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1))

-            return png_exp16bit(res);

-

-         /* Overflow. */

-         value = 0;

-#     endif

-   }

-

-   return (png_uint_16)value;

-}

-

-/* This does the right thing based on the bit_depth field of the

- * png_struct, interpreting values as 8-bit or 16-bit.  While the result

- * is nominally a 16-bit value if bit depth is 8 then the result is

- * 8-bit (as are the arguments.)

- */

-png_uint_16 /* PRIVATE */

-png_gamma_correct(png_structrp png_ptr, unsigned int value,

-    png_fixed_point gamma_val)

-{

-   if (png_ptr->bit_depth == 8)

-      return png_gamma_8bit_correct(value, gamma_val);

-

-   else

-      return png_gamma_16bit_correct(value, gamma_val);

-}

-

-/* Internal function to build a single 16-bit table - the table consists of

- * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount

- * to shift the input values right (or 16-number_of_signifiant_bits).

- *

- * The caller is responsible for ensuring that the table gets cleaned up on

- * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument

- * should be somewhere that will be cleaned.

- */

-static void

-png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable,

-   PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)

-{

-   /* Various values derived from 'shift': */

-   PNG_CONST unsigned int num = 1U << (8U - shift);

-   PNG_CONST unsigned int max = (1U << (16U - shift))-1U;

-   PNG_CONST unsigned int max_by_2 = 1U << (15U-shift);

-   unsigned int i;

-

-   png_uint_16pp table = *ptable =

-       (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p)));

-

-   for (i = 0; i < num; i++)

-   {

-      png_uint_16p sub_table = table[i] =

-          (png_uint_16p)png_malloc(png_ptr, 256 * (sizeof (png_uint_16)));

-

-      /* The 'threshold' test is repeated here because it can arise for one of

-       * the 16-bit tables even if the others don't hit it.

-       */

-      if (png_gamma_significant(gamma_val))

-      {

-         /* The old code would overflow at the end and this would cause the

-          * 'pow' function to return a result >1, resulting in an

-          * arithmetic error.  This code follows the spec exactly; ig is

-          * the recovered input sample, it always has 8-16 bits.

-          *

-          * We want input * 65535/max, rounded, the arithmetic fits in 32

-          * bits (unsigned) so long as max <= 32767.

-          */

-         unsigned int j;

-         for (j = 0; j < 256; j++)

-         {

-            png_uint_32 ig = (j << (8-shift)) + i;

-#           ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED

-               /* Inline the 'max' scaling operation: */

-               double d = floor(65535*pow(ig/(double)max, gamma_val*.00001)+.5);

-               sub_table[j] = (png_uint_16)d;

-#           else

-               if (shift)

-                  ig = (ig * 65535U + max_by_2)/max;

-

-               sub_table[j] = png_gamma_16bit_correct(ig, gamma_val);

-#           endif

-         }

-      }

-      else

-      {

-         /* We must still build a table, but do it the fast way. */

-         unsigned int j;

-

-         for (j = 0; j < 256; j++)

-         {

-            png_uint_32 ig = (j << (8-shift)) + i;

-

-            if (shift)

-               ig = (ig * 65535U + max_by_2)/max;

-

-            sub_table[j] = (png_uint_16)ig;

-         }

-      }

-   }

-}

-

-/* NOTE: this function expects the *inverse* of the overall gamma transformation

- * required.

- */

-static void

-png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable,

-   PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)

-{

-   PNG_CONST unsigned int num = 1U << (8U - shift);

-   PNG_CONST unsigned int max = (1U << (16U - shift))-1U;

-   unsigned int i;

-   png_uint_32 last;

-

-   png_uint_16pp table = *ptable =

-       (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p)));

-

-   /* 'num' is the number of tables and also the number of low bits of low

-    * bits of the input 16-bit value used to select a table.  Each table is

-    * itself index by the high 8 bits of the value.

-    */

-   for (i = 0; i < num; i++)

-      table[i] = (png_uint_16p)png_malloc(png_ptr,

-          256 * (sizeof (png_uint_16)));

-

-   /* 'gamma_val' is set to the reciprocal of the value calculated above, so

-    * pow(out,g) is an *input* value.  'last' is the last input value set.

-    *

-    * In the loop 'i' is used to find output values.  Since the output is

-    * 8-bit there are only 256 possible values.  The tables are set up to

-    * select the closest possible output value for each input by finding

-    * the input value at the boundary between each pair of output values

-    * and filling the table up to that boundary with the lower output

-    * value.

-    *

-    * The boundary values are 0.5,1.5..253.5,254.5.  Since these are 9-bit

-    * values the code below uses a 16-bit value in i; the values start at

-    * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last

-    * entries are filled with 255).  Start i at 128 and fill all 'last'

-    * table entries <= 'max'

-    */

-   last = 0;

-   for (i = 0; i < 255; ++i) /* 8-bit output value */

-   {

-      /* Find the corresponding maximum input value */

-      png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */

-

-      /* Find the boundary value in 16 bits: */

-      png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val);

-

-      /* Adjust (round) to (16-shift) bits: */

-      bound = (bound * max + 32768U)/65535U + 1U;

-

-      while (last < bound)

-      {

-         table[last & (0xffU >> shift)][last >> (8U - shift)] = out;

-         last++;

-      }

-   }

-

-   /* And fill in the final entries. */

-   while (last < (num << 8))

-   {

-      table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U;

-      last++;

-   }

-}

-

-/* Build a single 8-bit table: same as the 16-bit case but much simpler (and

- * typically much faster).  Note that libpng currently does no sBIT processing

- * (apparently contrary to the spec) so a 256 entry table is always generated.

- */

-static void

-png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable,

-   PNG_CONST png_fixed_point gamma_val)

-{

-   unsigned int i;

-   png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256);

-

-   if (png_gamma_significant(gamma_val)) for (i=0; i<256; i++)

-      table[i] = png_gamma_8bit_correct(i, gamma_val);

-

-   else for (i=0; i<256; ++i)

-      table[i] = (png_byte)i;

-}

-

-/* Used from png_read_destroy and below to release the memory used by the gamma

- * tables.

- */

-void /* PRIVATE */

-png_destroy_gamma_table(png_structrp png_ptr)

-{

-   png_free(png_ptr, png_ptr->gamma_table);

-   png_ptr->gamma_table = NULL;

-

-   if (png_ptr->gamma_16_table != NULL)

-   {

-      int i;

-      int istop = (1 << (8 - png_ptr->gamma_shift));

-      for (i = 0; i < istop; i++)

-      {

-         png_free(png_ptr, png_ptr->gamma_16_table[i]);

-      }

-   png_free(png_ptr, png_ptr->gamma_16_table);

-   png_ptr->gamma_16_table = NULL;

-   }

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \

-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)

-   png_free(png_ptr, png_ptr->gamma_from_1);

-   png_ptr->gamma_from_1 = NULL;

-   png_free(png_ptr, png_ptr->gamma_to_1);

-   png_ptr->gamma_to_1 = NULL;

-

-   if (png_ptr->gamma_16_from_1 != NULL)

-   {

-      int i;

-      int istop = (1 << (8 - png_ptr->gamma_shift));

-      for (i = 0; i < istop; i++)

-      {

-         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);

-      }

-   png_free(png_ptr, png_ptr->gamma_16_from_1);

-   png_ptr->gamma_16_from_1 = NULL;

-   }

-   if (png_ptr->gamma_16_to_1 != NULL)

-   {

-      int i;

-      int istop = (1 << (8 - png_ptr->gamma_shift));

-      for (i = 0; i < istop; i++)

-      {

-         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);

-      }

-   png_free(png_ptr, png_ptr->gamma_16_to_1);

-   png_ptr->gamma_16_to_1 = NULL;

-   }

-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */

-}

-

-/* We build the 8- or 16-bit gamma tables here.  Note that for 16-bit

- * tables, we don't make a full table if we are reducing to 8-bit in

- * the future.  Note also how the gamma_16 tables are segmented so that

- * we don't need to allocate > 64K chunks for a full 16-bit table.

- */

-void /* PRIVATE */

-png_build_gamma_table(png_structrp png_ptr, int bit_depth)

-{

-  png_debug(1, "in png_build_gamma_table");

-

-  /* Remove any existing table; this copes with multiple calls to

-   * png_read_update_info.  The warning is because building the gamma tables

-   * multiple times is a performance hit - it's harmless but the ability to call

-   * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible

-   * to warn if the app introduces such a hit.

-   */

-  if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL)

-  {

-    png_warning(png_ptr, "gamma table being rebuilt");

-    png_destroy_gamma_table(png_ptr);

-  }

-

-  if (bit_depth <= 8)

-  {

-     png_build_8bit_table(png_ptr, &png_ptr->gamma_table,

-         png_ptr->screen_gamma > 0 ?  png_reciprocal2(png_ptr->colorspace.gamma,

-         png_ptr->screen_gamma) : PNG_FP_1);

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \

-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)

-     if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY))

-     {

-        png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,

-            png_reciprocal(png_ptr->colorspace.gamma));

-

-        png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,

-            png_ptr->screen_gamma > 0 ?  png_reciprocal(png_ptr->screen_gamma) :

-            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);

-     }

-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */

-  }

-  else

-  {

-     png_byte shift, sig_bit;

-

-     if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)

-     {

-        sig_bit = png_ptr->sig_bit.red;

-

-        if (png_ptr->sig_bit.green > sig_bit)

-           sig_bit = png_ptr->sig_bit.green;

-

-        if (png_ptr->sig_bit.blue > sig_bit)

-           sig_bit = png_ptr->sig_bit.blue;

-     }

-     else

-        sig_bit = png_ptr->sig_bit.gray;

-

-     /* 16-bit gamma code uses this equation:

-      *

-      *   ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]

-      *

-      * Where 'iv' is the input color value and 'ov' is the output value -

-      * pow(iv, gamma).

-      *

-      * Thus the gamma table consists of up to 256 256 entry tables.  The table

-      * is selected by the (8-gamma_shift) most significant of the low 8 bits of

-      * the color value then indexed by the upper 8 bits:

-      *

-      *   table[low bits][high 8 bits]

-      *

-      * So the table 'n' corresponds to all those 'iv' of:

-      *

-      *   <all high 8-bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>

-      *

-      */

-     if (sig_bit > 0 && sig_bit < 16U)

-        shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */

-

-     else

-        shift = 0; /* keep all 16 bits */

-

-     if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8))

-     {

-        /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively

-         * the significant bits in the *input* when the output will

-         * eventually be 8 bits.  By default it is 11.

-         */

-        if (shift < (16U - PNG_MAX_GAMMA_8))

-           shift = (16U - PNG_MAX_GAMMA_8);

-     }

-

-     if (shift > 8U)

-        shift = 8U; /* Guarantees at least one table! */

-

-     png_ptr->gamma_shift = shift;

-

-#ifdef PNG_16BIT_SUPPORTED

-     /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now

-      * PNG_COMPOSE).  This effectively smashed the background calculation for

-      * 16-bit output because the 8-bit table assumes the result will be reduced

-      * to 8 bits.

-      */

-     if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8))

-#endif

-         png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,

-         png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma,

-         png_ptr->screen_gamma) : PNG_FP_1);

-

-#ifdef PNG_16BIT_SUPPORTED

-     else

-         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,

-         png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,

-         png_ptr->screen_gamma) : PNG_FP_1);

-#endif

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \

-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)

-     if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY))

-     {

-        png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,

-            png_reciprocal(png_ptr->colorspace.gamma));

-

-        /* Notice that the '16 from 1' table should be full precision, however

-         * the lookup on this table still uses gamma_shift, so it can't be.

-         * TODO: fix this.

-         */

-        png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,

-            png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :

-            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);

-     }

-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */

-  }

-}

-#endif /* READ_GAMMA */

-

-/* HARDWARE OPTION SUPPORT */

-#ifdef PNG_SET_OPTION_SUPPORTED

-int PNGAPI

-png_set_option(png_structrp png_ptr, int option, int onoff)

-{

-   if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT &&

-      (option & 1) == 0)

-   {

-      int mask = 3 << option;

-      int setting = (2 + (onoff != 0)) << option;

-      int current = png_ptr->options;

-

-      png_ptr->options = (png_byte)((current & ~mask) | setting);

-

-      return (current & mask) >> option;

-   }

-

-   return PNG_OPTION_INVALID;

-}

-#endif

-

-/* sRGB support */

-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\

-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)

-/* sRGB conversion tables; these are machine generated with the code in

- * contrib/tools/makesRGB.c.  The actual sRGB transfer curve defined in the

- * specification (see the article at http://en.wikipedia.org/wiki/SRGB)

- * is used, not the gamma=1/2.2 approximation use elsewhere in libpng.

- * The sRGB to linear table is exact (to the nearest 16 bit linear fraction).

- * The inverse (linear to sRGB) table has accuracies as follows:

- *

- * For all possible (255*65535+1) input values:

- *

- *    error: -0.515566 - 0.625971, 79441 (0.475369%) of readings inexact

- *

- * For the input values corresponding to the 65536 16-bit values:

- *

- *    error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact

- *

- * In all cases the inexact readings are off by one.

- */

-

-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED

-/* The convert-to-sRGB table is only currently required for read. */

-const png_uint_16 png_sRGB_table[256] =

-{

-   0,20,40,60,80,99,119,139,

-   159,179,199,219,241,264,288,313,

-   340,367,396,427,458,491,526,562,

-   599,637,677,718,761,805,851,898,

-   947,997,1048,1101,1156,1212,1270,1330,

-   1391,1453,1517,1583,1651,1720,1790,1863,

-   1937,2013,2090,2170,2250,2333,2418,2504,

-   2592,2681,2773,2866,2961,3058,3157,3258,

-   3360,3464,3570,3678,3788,3900,4014,4129,

-   4247,4366,4488,4611,4736,4864,4993,5124,

-   5257,5392,5530,5669,5810,5953,6099,6246,

-   6395,6547,6700,6856,7014,7174,7335,7500,

-   7666,7834,8004,8177,8352,8528,8708,8889,

-   9072,9258,9445,9635,9828,10022,10219,10417,

-   10619,10822,11028,11235,11446,11658,11873,12090,

-   12309,12530,12754,12980,13209,13440,13673,13909,

-   14146,14387,14629,14874,15122,15371,15623,15878,

-   16135,16394,16656,16920,17187,17456,17727,18001,

-   18277,18556,18837,19121,19407,19696,19987,20281,

-   20577,20876,21177,21481,21787,22096,22407,22721,

-   23038,23357,23678,24002,24329,24658,24990,25325,

-   25662,26001,26344,26688,27036,27386,27739,28094,

-   28452,28813,29176,29542,29911,30282,30656,31033,

-   31412,31794,32179,32567,32957,33350,33745,34143,

-   34544,34948,35355,35764,36176,36591,37008,37429,

-   37852,38278,38706,39138,39572,40009,40449,40891,

-   41337,41785,42236,42690,43147,43606,44069,44534,

-   45002,45473,45947,46423,46903,47385,47871,48359,

-   48850,49344,49841,50341,50844,51349,51858,52369,

-   52884,53401,53921,54445,54971,55500,56032,56567,

-   57105,57646,58190,58737,59287,59840,60396,60955,

-   61517,62082,62650,63221,63795,64372,64952,65535

-};

-

-#endif /* simplified read only */

-

-/* The base/delta tables are required for both read and write (but currently

- * only the simplified versions.)

- */

-const png_uint_16 png_sRGB_base[512] =

-{

-   128,1782,3383,4644,5675,6564,7357,8074,

-   8732,9346,9921,10463,10977,11466,11935,12384,

-   12816,13233,13634,14024,14402,14769,15125,15473,

-   15812,16142,16466,16781,17090,17393,17690,17981,

-   18266,18546,18822,19093,19359,19621,19879,20133,

-   20383,20630,20873,21113,21349,21583,21813,22041,

-   22265,22487,22707,22923,23138,23350,23559,23767,

-   23972,24175,24376,24575,24772,24967,25160,25352,

-   25542,25730,25916,26101,26284,26465,26645,26823,

-   27000,27176,27350,27523,27695,27865,28034,28201,

-   28368,28533,28697,28860,29021,29182,29341,29500,

-   29657,29813,29969,30123,30276,30429,30580,30730,

-   30880,31028,31176,31323,31469,31614,31758,31902,

-   32045,32186,32327,32468,32607,32746,32884,33021,

-   33158,33294,33429,33564,33697,33831,33963,34095,

-   34226,34357,34486,34616,34744,34873,35000,35127,

-   35253,35379,35504,35629,35753,35876,35999,36122,

-   36244,36365,36486,36606,36726,36845,36964,37083,

-   37201,37318,37435,37551,37668,37783,37898,38013,

-   38127,38241,38354,38467,38580,38692,38803,38915,

-   39026,39136,39246,39356,39465,39574,39682,39790,

-   39898,40005,40112,40219,40325,40431,40537,40642,

-   40747,40851,40955,41059,41163,41266,41369,41471,

-   41573,41675,41777,41878,41979,42079,42179,42279,

-   42379,42478,42577,42676,42775,42873,42971,43068,

-   43165,43262,43359,43456,43552,43648,43743,43839,

-   43934,44028,44123,44217,44311,44405,44499,44592,

-   44685,44778,44870,44962,45054,45146,45238,45329,

-   45420,45511,45601,45692,45782,45872,45961,46051,

-   46140,46229,46318,46406,46494,46583,46670,46758,

-   46846,46933,47020,47107,47193,47280,47366,47452,

-   47538,47623,47709,47794,47879,47964,48048,48133,

-   48217,48301,48385,48468,48552,48635,48718,48801,

-   48884,48966,49048,49131,49213,49294,49376,49458,

-   49539,49620,49701,49782,49862,49943,50023,50103,

-   50183,50263,50342,50422,50501,50580,50659,50738,

-   50816,50895,50973,51051,51129,51207,51285,51362,

-   51439,51517,51594,51671,51747,51824,51900,51977,

-   52053,52129,52205,52280,52356,52432,52507,52582,

-   52657,52732,52807,52881,52956,53030,53104,53178,

-   53252,53326,53400,53473,53546,53620,53693,53766,

-   53839,53911,53984,54056,54129,54201,54273,54345,

-   54417,54489,54560,54632,54703,54774,54845,54916,

-   54987,55058,55129,55199,55269,55340,55410,55480,

-   55550,55620,55689,55759,55828,55898,55967,56036,

-   56105,56174,56243,56311,56380,56448,56517,56585,

-   56653,56721,56789,56857,56924,56992,57059,57127,

-   57194,57261,57328,57395,57462,57529,57595,57662,

-   57728,57795,57861,57927,57993,58059,58125,58191,

-   58256,58322,58387,58453,58518,58583,58648,58713,

-   58778,58843,58908,58972,59037,59101,59165,59230,

-   59294,59358,59422,59486,59549,59613,59677,59740,

-   59804,59867,59930,59993,60056,60119,60182,60245,

-   60308,60370,60433,60495,60558,60620,60682,60744,

-   60806,60868,60930,60992,61054,61115,61177,61238,

-   61300,61361,61422,61483,61544,61605,61666,61727,

-   61788,61848,61909,61969,62030,62090,62150,62211,

-   62271,62331,62391,62450,62510,62570,62630,62689,

-   62749,62808,62867,62927,62986,63045,63104,63163,

-   63222,63281,63340,63398,63457,63515,63574,63632,

-   63691,63749,63807,63865,63923,63981,64039,64097,

-   64155,64212,64270,64328,64385,64443,64500,64557,

-   64614,64672,64729,64786,64843,64900,64956,65013,

-   65070,65126,65183,65239,65296,65352,65409,65465

-};

-

-const png_byte png_sRGB_delta[512] =

-{

-   207,201,158,129,113,100,90,82,77,72,68,64,61,59,56,54,

-   52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36,

-   35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28,

-   28,27,27,27,27,26,26,26,25,25,25,25,24,24,24,24,

-   23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21,

-   21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19,

-   19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17,

-   17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16,

-   16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15,

-   15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14,

-   14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13,

-   13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12,

-   12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,

-   12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11,

-   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,

-   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,

-   11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,

-   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,

-   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,

-   10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,

-   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,

-   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,

-   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,

-   9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,

-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,

-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,

-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,

-   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,

-   8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,

-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,

-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7

-};

-#endif /* SIMPLIFIED READ/WRITE sRGB support */

-

-/* SIMPLIFIED READ/WRITE SUPPORT */

-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\

-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)

-static int

-png_image_free_function(png_voidp argument)

-{

-   png_imagep image = png_voidcast(png_imagep, argument);

-   png_controlp cp = image->opaque;

-   png_control c;

-

-   /* Double check that we have a png_ptr - it should be impossible to get here

-    * without one.

-    */

-   if (cp->png_ptr == NULL)

-      return 0;

-

-   /* First free any data held in the control structure. */

-#  ifdef PNG_STDIO_SUPPORTED

-      if (cp->owned_file)

-      {

-         FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr);

-         cp->owned_file = 0;

-

-         /* Ignore errors here. */

-         if (fp != NULL)

-         {

-            cp->png_ptr->io_ptr = NULL;

-            (void)fclose(fp);

-         }

-      }

-#  endif

-

-   /* Copy the control structure so that the original, allocated, version can be

-    * safely freed.  Notice that a png_error here stops the remainder of the

-    * cleanup, but this is probably fine because that would indicate bad memory

-    * problems anyway.

-    */

-   c = *cp;

-   image->opaque = &c;

-   png_free(c.png_ptr, cp);

-

-   /* Then the structures, calling the correct API. */

-   if (c.for_write)

-   {

-#     ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED

-         png_destroy_write_struct(&c.png_ptr, &c.info_ptr);

-#     else

-         png_error(c.png_ptr, "simplified write not supported");

-#     endif

-   }

-   else

-   {

-#     ifdef PNG_SIMPLIFIED_READ_SUPPORTED

-         png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL);

-#     else

-         png_error(c.png_ptr, "simplified read not supported");

-#     endif

-   }

-

-   /* Success. */

-   return 1;

-}

-

-void PNGAPI

-png_image_free(png_imagep image)

-{

-   /* Safely call the real function, but only if doing so is safe at this point

-    * (if not inside an error handling context).  Otherwise assume

-    * png_safe_execute will call this API after the return.

-    */

-   if (image != NULL && image->opaque != NULL &&

-      image->opaque->error_buf == NULL)

-   {

-      /* Ignore errors here: */

-      (void)png_safe_execute(image, png_image_free_function, image);

-      image->opaque = NULL;

-   }

-}

-

-int /* PRIVATE */

-png_image_error(png_imagep image, png_const_charp error_message)

-{

-   /* Utility to log an error. */

-   png_safecat(image->message, (sizeof image->message), 0, error_message);

-   image->warning_or_error |= PNG_IMAGE_ERROR;

-   png_image_free(image);

-   return 0;

-}

-

-#endif /* SIMPLIFIED READ/WRITE */

-#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */

+
+/* png.c - location for general purpose libpng functions
+ *
+ * Last changed in libpng 1.6.19 [November 12, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+/* Generate a compiler error if there is an old png.h in the search path. */
+typedef png_libpng_version_1_6_20 Your_png_h_is_not_version_1_6_20;
+
+/* Tells libpng that we have already handled the first "num_bytes" bytes
+ * of the PNG file signature.  If the PNG data is embedded into another
+ * stream we can set num_bytes = 8 so that libpng will not attempt to read
+ * or write any of the magic bytes before it starts on the IHDR.
+ */
+
+#ifdef PNG_READ_SUPPORTED
+void PNGAPI
+png_set_sig_bytes(png_structrp png_ptr, int num_bytes)
+{
+   unsigned int nb = (unsigned int)num_bytes;
+
+   png_debug(1, "in png_set_sig_bytes");
+
+   if (png_ptr == NULL)
+      return;
+
+   if (num_bytes < 0)
+      nb = 0;
+
+   if (nb > 8)
+      png_error(png_ptr, "Too many bytes for PNG signature");
+
+   png_ptr->sig_bytes = (png_byte)nb;
+}
+
+/* Checks whether the supplied bytes match the PNG signature.  We allow
+ * checking less than the full 8-byte signature so that those apps that
+ * already read the first few bytes of a file to determine the file type
+ * can simply check the remaining bytes for extra assurance.  Returns
+ * an integer less than, equal to, or greater than zero if sig is found,
+ * respectively, to be less than, to match, or be greater than the correct
+ * PNG signature (this is the same behavior as strcmp, memcmp, etc).
+ */
+int PNGAPI
+png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
+{
+   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+   if (num_to_check > 8)
+      num_to_check = 8;
+
+   else if (num_to_check < 1)
+      return (-1);
+
+   if (start > 7)
+      return (-1);
+
+   if (start + num_to_check > 8)
+      num_to_check = 8 - start;
+
+   return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));
+}
+
+#endif /* READ */
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Function to allocate memory for zlib */
+PNG_FUNCTION(voidpf /* PRIVATE */,
+png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
+{
+   png_alloc_size_t num_bytes = size;
+
+   if (png_ptr == NULL)
+      return NULL;
+
+   if (items >= (~(png_alloc_size_t)0)/size)
+   {
+      png_warning (png_voidcast(png_structrp, png_ptr),
+         "Potential overflow in png_zalloc()");
+      return NULL;
+   }
+
+   num_bytes *= items;
+   return png_malloc_warn(png_voidcast(png_structrp, png_ptr), num_bytes);
+}
+
+/* Function to free memory for zlib */
+void /* PRIVATE */
+png_zfree(voidpf png_ptr, voidpf ptr)
+{
+   png_free(png_voidcast(png_const_structrp,png_ptr), ptr);
+}
+
+/* Reset the CRC variable to 32 bits of 1's.  Care must be taken
+ * in case CRC is > 32 bits to leave the top bits 0.
+ */
+void /* PRIVATE */
+png_reset_crc(png_structrp png_ptr)
+{
+   /* The cast is safe because the crc is a 32-bit value. */
+   png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0);
+}
+
+/* Calculate the CRC over a section of data.  We can only pass as
+ * much data to this routine as the largest single buffer size.  We
+ * also check that this data will actually be used before going to the
+ * trouble of calculating it.
+ */
+void /* PRIVATE */
+png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length)
+{
+   int need_crc = 1;
+
+   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+
+   else /* critical */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
+         need_crc = 0;
+   }
+
+   /* 'uLong' is defined in zlib.h as unsigned long; this means that on some
+    * systems it is a 64-bit value.  crc32, however, returns 32 bits so the
+    * following cast is safe.  'uInt' may be no more than 16 bits, so it is
+    * necessary to perform a loop here.
+    */
+   if (need_crc != 0 && length > 0)
+   {
+      uLong crc = png_ptr->crc; /* Should never issue a warning */
+
+      do
+      {
+         uInt safe_length = (uInt)length;
+#ifndef __COVERITY__
+         if (safe_length == 0)
+            safe_length = (uInt)-1; /* evil, but safe */
+#endif
+
+         crc = crc32(crc, ptr, safe_length);
+
+         /* The following should never issue compiler warnings; if they do the
+          * target system has characteristics that will probably violate other
+          * assumptions within the libpng code.
+          */
+         ptr += safe_length;
+         length -= safe_length;
+      }
+      while (length > 0);
+
+      /* And the following is always safe because the crc is only 32 bits. */
+      png_ptr->crc = (png_uint_32)crc;
+   }
+}
+
+/* Check a user supplied version number, called from both read and write
+ * functions that create a png_struct.
+ */
+int
+png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
+{
+     /* Libpng versions 1.0.0 and later are binary compatible if the version
+      * string matches through the second '.'; we must recompile any
+      * applications that use any older library version.
+      */
+
+   if (user_png_ver != NULL)
+   {
+      int i = -1;
+      int found_dots = 0;
+
+      do
+      {
+         i++;
+         if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i])
+            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+         if (user_png_ver[i] == '.')
+            found_dots++;
+      } while (found_dots < 2 && user_png_ver[i] != 0 &&
+            PNG_LIBPNG_VER_STRING[i] != 0);
+   }
+
+   else
+      png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+
+   if ((png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) != 0)
+   {
+#ifdef PNG_WARNINGS_SUPPORTED
+      size_t pos = 0;
+      char m[128];
+
+      pos = png_safecat(m, (sizeof m), pos,
+          "Application built with libpng-");
+      pos = png_safecat(m, (sizeof m), pos, user_png_ver);
+      pos = png_safecat(m, (sizeof m), pos, " but running with ");
+      pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING);
+      PNG_UNUSED(pos)
+
+      png_warning(png_ptr, m);
+#endif
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+      png_ptr->flags = 0;
+#endif
+
+      return 0;
+   }
+
+   /* Success return. */
+   return 1;
+}
+
+/* Generic function to create a png_struct for either read or write - this
+ * contains the common initialization.
+ */
+PNG_FUNCTION(png_structp /* PRIVATE */,
+png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+{
+   png_struct create_struct;
+#  ifdef PNG_SETJMP_SUPPORTED
+      jmp_buf create_jmp_buf;
+#  endif
+
+   /* This temporary stack-allocated structure is used to provide a place to
+    * build enough context to allow the user provided memory allocator (if any)
+    * to be called.
+    */
+   memset(&create_struct, 0, (sizeof create_struct));
+
+   /* Added at libpng-1.2.6 */
+#  ifdef PNG_USER_LIMITS_SUPPORTED
+      create_struct.user_width_max = PNG_USER_WIDTH_MAX;
+      create_struct.user_height_max = PNG_USER_HEIGHT_MAX;
+
+#     ifdef PNG_USER_CHUNK_CACHE_MAX
+      /* Added at libpng-1.2.43 and 1.4.0 */
+      create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX;
+#     endif
+
+#     ifdef PNG_USER_CHUNK_MALLOC_MAX
+      /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists
+       * in png_struct regardless.
+       */
+      create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX;
+#     endif
+#  endif
+
+   /* The following two API calls simply set fields in png_struct, so it is safe
+    * to do them now even though error handling is not yet set up.
+    */
+#  ifdef PNG_USER_MEM_SUPPORTED
+      png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn);
+#  else
+      PNG_UNUSED(mem_ptr)
+      PNG_UNUSED(malloc_fn)
+      PNG_UNUSED(free_fn)
+#  endif
+
+   /* (*error_fn) can return control to the caller after the error_ptr is set,
+    * this will result in a memory leak unless the error_fn does something
+    * extremely sophisticated.  The design lacks merit but is implicit in the
+    * API.
+    */
+   png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn);
+
+#  ifdef PNG_SETJMP_SUPPORTED
+      if (!setjmp(create_jmp_buf))
+#  endif
+      {
+#  ifdef PNG_SETJMP_SUPPORTED
+         /* Temporarily fake out the longjmp information until we have
+          * successfully completed this function.  This only works if we have
+          * setjmp() support compiled in, but it is safe - this stuff should
+          * never happen.
+          */
+         create_struct.jmp_buf_ptr = &create_jmp_buf;
+         create_struct.jmp_buf_size = 0; /*stack allocation*/
+         create_struct.longjmp_fn = longjmp;
+#  endif
+         /* Call the general version checker (shared with read and write code):
+          */
+         if (png_user_version_check(&create_struct, user_png_ver) != 0)
+         {
+            png_structrp png_ptr = png_voidcast(png_structrp,
+               png_malloc_warn(&create_struct, (sizeof *png_ptr)));
+
+            if (png_ptr != NULL)
+            {
+               /* png_ptr->zstream holds a back-pointer to the png_struct, so
+                * this can only be done now:
+                */
+               create_struct.zstream.zalloc = png_zalloc;
+               create_struct.zstream.zfree = png_zfree;
+               create_struct.zstream.opaque = png_ptr;
+
+#              ifdef PNG_SETJMP_SUPPORTED
+               /* Eliminate the local error handling: */
+               create_struct.jmp_buf_ptr = NULL;
+               create_struct.jmp_buf_size = 0;
+               create_struct.longjmp_fn = 0;
+#              endif
+
+               *png_ptr = create_struct;
+
+               /* This is the successful return point */
+               return png_ptr;
+            }
+         }
+      }
+
+   /* A longjmp because of a bug in the application storage allocator or a
+    * simple failure to allocate the png_struct.
+    */
+   return NULL;
+}
+
+/* Allocate the memory for an info_struct for the application. */
+PNG_FUNCTION(png_infop,PNGAPI
+png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
+{
+   png_inforp info_ptr;
+
+   png_debug(1, "in png_create_info_struct");
+
+   if (png_ptr == NULL)
+      return NULL;
+
+   /* Use the internal API that does not (or at least should not) error out, so
+    * that this call always returns ok.  The application typically sets up the
+    * error handling *after* creating the info_struct because this is the way it
+    * has always been done in 'example.c'.
+    */
+   info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,
+      (sizeof *info_ptr)));
+
+   if (info_ptr != NULL)
+      memset(info_ptr, 0, (sizeof *info_ptr));
+
+   return info_ptr;
+}
+
+/* This function frees the memory associated with a single info struct.
+ * Normally, one would use either png_destroy_read_struct() or
+ * png_destroy_write_struct() to free an info struct, but this may be
+ * useful for some applications.  From libpng 1.6.0 this function is also used
+ * internally to implement the png_info release part of the 'struct' destroy
+ * APIs.  This ensures that all possible approaches free the same data (all of
+ * it).
+ */
+void PNGAPI
+png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr)
+{
+   png_inforp info_ptr = NULL;
+
+   png_debug(1, "in png_destroy_info_struct");
+
+   if (png_ptr == NULL)
+      return;
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+      /* Do this first in case of an error below; if the app implements its own
+       * memory management this can lead to png_free calling png_error, which
+       * will abort this routine and return control to the app error handler.
+       * An infinite loop may result if it then tries to free the same info
+       * ptr.
+       */
+      *info_ptr_ptr = NULL;
+
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+      memset(info_ptr, 0, (sizeof *info_ptr));
+      png_free(png_ptr, info_ptr);
+   }
+}
+
+/* Initialize the info structure.  This is now an internal function (0.89)
+ * and applications using it are urged to use png_create_info_struct()
+ * instead.  Use deprecated in 1.6.0, internal use removed (used internally it
+ * is just a memset).
+ *
+ * NOTE: it is almost inconceivable that this API is used because it bypasses
+ * the user-memory mechanism and the user error handling/warning mechanisms in
+ * those cases where it does anything other than a memset.
+ */
+PNG_FUNCTION(void,PNGAPI
+png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size),
+   PNG_DEPRECATED)
+{
+   png_inforp info_ptr = *ptr_ptr;
+
+   png_debug(1, "in png_info_init_3");
+
+   if (info_ptr == NULL)
+      return;
+
+   if ((sizeof (png_info)) > png_info_struct_size)
+   {
+      *ptr_ptr = NULL;
+      /* The following line is why this API should not be used: */
+      free(info_ptr);
+      info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL,
+         (sizeof *info_ptr)));
+      if (info_ptr == NULL)
+         return;
+      *ptr_ptr = info_ptr;
+   }
+
+   /* Set everything to 0 */
+   memset(info_ptr, 0, (sizeof *info_ptr));
+}
+
+/* The following API is not called internally */
+void PNGAPI
+png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr,
+   int freer, png_uint_32 mask)
+{
+   png_debug(1, "in png_data_freer");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (freer == PNG_DESTROY_WILL_FREE_DATA)
+      info_ptr->free_me |= mask;
+
+   else if (freer == PNG_USER_WILL_FREE_DATA)
+      info_ptr->free_me &= ~mask;
+
+   else
+      png_error(png_ptr, "Unknown freer parameter in png_data_freer");
+}
+
+void PNGAPI
+png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask,
+   int num)
+{
+   png_debug(1, "in png_free_data");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+#ifdef PNG_TEXT_SUPPORTED
+   /* Free text item num or (if num == -1) all text items */
+   if (info_ptr->text != 0 &&
+       ((mask & PNG_FREE_TEXT) & info_ptr->free_me) != 0)
+   {
+      if (num != -1)
+      {
+         png_free(png_ptr, info_ptr->text[num].key);
+         info_ptr->text[num].key = NULL;
+      }
+
+      else
+      {
+         int i;
+
+         for (i = 0; i < info_ptr->num_text; i++)
+            png_free(png_ptr, info_ptr->text[i].key);
+
+         png_free(png_ptr, info_ptr->text);
+         info_ptr->text = NULL;
+         info_ptr->num_text = 0;
+      }
+   }
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+   /* Free any tRNS entry */
+   if (((mask & PNG_FREE_TRNS) & info_ptr->free_me) != 0)
+   {
+      info_ptr->valid &= ~PNG_INFO_tRNS;
+      png_free(png_ptr, info_ptr->trans_alpha);
+      info_ptr->trans_alpha = NULL;
+      info_ptr->num_trans = 0;
+   }
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+   /* Free any sCAL entry */
+   if (((mask & PNG_FREE_SCAL) & info_ptr->free_me) != 0)
+   {
+      png_free(png_ptr, info_ptr->scal_s_width);
+      png_free(png_ptr, info_ptr->scal_s_height);
+      info_ptr->scal_s_width = NULL;
+      info_ptr->scal_s_height = NULL;
+      info_ptr->valid &= ~PNG_INFO_sCAL;
+   }
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+   /* Free any pCAL entry */
+   if (((mask & PNG_FREE_PCAL) & info_ptr->free_me) != 0)
+   {
+      png_free(png_ptr, info_ptr->pcal_purpose);
+      png_free(png_ptr, info_ptr->pcal_units);
+      info_ptr->pcal_purpose = NULL;
+      info_ptr->pcal_units = NULL;
+
+      if (info_ptr->pcal_params != NULL)
+         {
+            int i;
+
+            for (i = 0; i < info_ptr->pcal_nparams; i++)
+               png_free(png_ptr, info_ptr->pcal_params[i]);
+
+            png_free(png_ptr, info_ptr->pcal_params);
+            info_ptr->pcal_params = NULL;
+         }
+      info_ptr->valid &= ~PNG_INFO_pCAL;
+   }
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+   /* Free any profile entry */
+   if (((mask & PNG_FREE_ICCP) & info_ptr->free_me) != 0)
+   {
+      png_free(png_ptr, info_ptr->iccp_name);
+      png_free(png_ptr, info_ptr->iccp_profile);
+      info_ptr->iccp_name = NULL;
+      info_ptr->iccp_profile = NULL;
+      info_ptr->valid &= ~PNG_INFO_iCCP;
+   }
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+   /* Free a given sPLT entry, or (if num == -1) all sPLT entries */
+   if (info_ptr->splt_palettes != 0 &&
+       ((mask & PNG_FREE_SPLT) & info_ptr->free_me) != 0)
+   {
+      if (num != -1)
+      {
+         png_free(png_ptr, info_ptr->splt_palettes[num].name);
+         png_free(png_ptr, info_ptr->splt_palettes[num].entries);
+         info_ptr->splt_palettes[num].name = NULL;
+         info_ptr->splt_palettes[num].entries = NULL;
+      }
+
+      else
+      {
+         int i;
+
+         for (i = 0; i < info_ptr->splt_palettes_num; i++)
+         {
+            png_free(png_ptr, info_ptr->splt_palettes[i].name);
+            png_free(png_ptr, info_ptr->splt_palettes[i].entries);
+         }
+
+         png_free(png_ptr, info_ptr->splt_palettes);
+         info_ptr->splt_palettes = NULL;
+         info_ptr->splt_palettes_num = 0;
+         info_ptr->valid &= ~PNG_INFO_sPLT;
+      }
+   }
+#endif
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+   if (info_ptr->unknown_chunks != 0 &&
+       ((mask & PNG_FREE_UNKN) & info_ptr->free_me) != 0)
+   {
+      if (num != -1)
+      {
+          png_free(png_ptr, info_ptr->unknown_chunks[num].data);
+          info_ptr->unknown_chunks[num].data = NULL;
+      }
+
+      else
+      {
+         int i;
+
+         for (i = 0; i < info_ptr->unknown_chunks_num; i++)
+            png_free(png_ptr, info_ptr->unknown_chunks[i].data);
+
+         png_free(png_ptr, info_ptr->unknown_chunks);
+         info_ptr->unknown_chunks = NULL;
+         info_ptr->unknown_chunks_num = 0;
+      }
+   }
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+   /* Free any hIST entry */
+   if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0)
+   {
+      png_free(png_ptr, info_ptr->hist);
+      info_ptr->hist = NULL;
+      info_ptr->valid &= ~PNG_INFO_hIST;
+   }
+#endif
+
+   /* Free any PLTE entry that was internally allocated */
+   if (((mask & PNG_FREE_PLTE) & info_ptr->free_me) != 0)
+   {
+      png_free(png_ptr, info_ptr->palette);
+      info_ptr->palette = NULL;
+      info_ptr->valid &= ~PNG_INFO_PLTE;
+      info_ptr->num_palette = 0;
+   }
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+   /* Free any image bits attached to the info structure */
+   if (((mask & PNG_FREE_ROWS) & info_ptr->free_me) != 0)
+   {
+      if (info_ptr->row_pointers != 0)
+      {
+         png_uint_32 row;
+         for (row = 0; row < info_ptr->height; row++)
+            png_free(png_ptr, info_ptr->row_pointers[row]);
+
+         png_free(png_ptr, info_ptr->row_pointers);
+         info_ptr->row_pointers = NULL;
+      }
+      info_ptr->valid &= ~PNG_INFO_IDAT;
+   }
+#endif
+
+   if (num != -1)
+      mask &= ~PNG_FREE_MUL;
+
+   info_ptr->free_me &= ~mask;
+}
+#endif /* READ || WRITE */
+
+/* This function returns a pointer to the io_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy() or png_read_destroy() are called.
+ */
+png_voidp PNGAPI
+png_get_io_ptr(png_const_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return (NULL);
+
+   return (png_ptr->io_ptr);
+}
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+#  ifdef PNG_STDIO_SUPPORTED
+/* Initialize the default input/output functions for the PNG file.  If you
+ * use your own read or write routines, you can call either png_set_read_fn()
+ * or png_set_write_fn() instead of png_init_io().  If you have defined
+ * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a
+ * function of your own because "FILE *" isn't necessarily available.
+ */
+void PNGAPI
+png_init_io(png_structrp png_ptr, png_FILE_p fp)
+{
+   png_debug(1, "in png_init_io");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->io_ptr = (png_voidp)fp;
+}
+#  endif
+
+#  ifdef PNG_SAVE_INT_32_SUPPORTED
+/* PNG signed integers are saved in 32-bit 2's complement format.  ANSI C-90
+ * defines a cast of a signed integer to an unsigned integer either to preserve
+ * the value, if it is positive, or to calculate:
+ *
+ *     (UNSIGNED_MAX+1) + integer
+ *
+ * Where UNSIGNED_MAX is the appropriate maximum unsigned value, so when the
+ * negative integral value is added the result will be an unsigned value
+ * correspnding to the 2's complement representation.
+ */
+void PNGAPI
+png_save_int_32(png_bytep buf, png_int_32 i)
+{
+   png_save_uint_32(buf, i);
+}
+#  endif
+
+#  ifdef PNG_TIME_RFC1123_SUPPORTED
+/* Convert the supplied time into an RFC 1123 string suitable for use in
+ * a "Creation Time" or other text-based time string.
+ */
+int PNGAPI
+png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)
+{
+   static PNG_CONST char short_months[12][4] =
+        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+   if (out == NULL)
+      return 0;
+
+   if (ptime->year > 9999 /* RFC1123 limitation */ ||
+       ptime->month == 0    ||  ptime->month > 12  ||
+       ptime->day   == 0    ||  ptime->day   > 31  ||
+       ptime->hour  > 23    ||  ptime->minute > 59 ||
+       ptime->second > 60)
+      return 0;
+
+   {
+      size_t pos = 0;
+      char number_buf[5]; /* enough for a four-digit year */
+
+#     define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string))
+#     define APPEND_NUMBER(format, value)\
+         APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value)))
+#     define APPEND(ch) if (pos < 28) out[pos++] = (ch)
+
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day);
+      APPEND(' ');
+      APPEND_STRING(short_months[(ptime->month - 1)]);
+      APPEND(' ');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year);
+      APPEND(' ');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour);
+      APPEND(':');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute);
+      APPEND(':');
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second);
+      APPEND_STRING(" +0000"); /* This reliably terminates the buffer */
+      PNG_UNUSED (pos)
+
+#     undef APPEND
+#     undef APPEND_NUMBER
+#     undef APPEND_STRING
+   }
+
+   return 1;
+}
+
+#    if PNG_LIBPNG_VER < 10700
+/* To do: remove the following from libpng-1.7 */
+/* Original API that uses a private buffer in png_struct.
+ * Deprecated because it causes png_struct to carry a spurious temporary
+ * buffer (png_struct::time_buffer), better to have the caller pass this in.
+ */
+png_const_charp PNGAPI
+png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime)
+{
+   if (png_ptr != NULL)
+   {
+      /* The only failure above if png_ptr != NULL is from an invalid ptime */
+      if (png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime) == 0)
+         png_warning(png_ptr, "Ignoring invalid time value");
+
+      else
+         return png_ptr->time_buffer;
+   }
+
+   return NULL;
+}
+#    endif /* LIBPNG_VER < 10700 */
+#  endif /* TIME_RFC1123 */
+
+#endif /* READ || WRITE */
+
+png_const_charp PNGAPI
+png_get_copyright(png_const_structrp png_ptr)
+{
+   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
+#ifdef PNG_STRING_COPYRIGHT
+   return PNG_STRING_COPYRIGHT
+#else
+#  ifdef __STDC__
+   return PNG_STRING_NEWLINE \
+      "libpng version 1.6.20 - December 3, 2015" PNG_STRING_NEWLINE \
+      "Copyright (c) 1998-2015 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
+      "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
+      "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
+      PNG_STRING_NEWLINE;
+#  else
+   return "libpng version 1.6.20 - December 3, 2015\
+      Copyright (c) 1998-2015 Glenn Randers-Pehrson\
+      Copyright (c) 1996-1997 Andreas Dilger\
+      Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
+#  endif
+#endif
+}
+
+/* The following return the library version as a short string in the
+ * format 1.0.0 through 99.99.99zz.  To get the version of *.h files
+ * used with your application, print out PNG_LIBPNG_VER_STRING, which
+ * is defined in png.h.
+ * Note: now there is no difference between png_get_libpng_ver() and
+ * png_get_header_ver().  Due to the version_nn_nn_nn typedef guard,
+ * it is guaranteed that png.c uses the correct version of png.h.
+ */
+png_const_charp PNGAPI
+png_get_libpng_ver(png_const_structrp png_ptr)
+{
+   /* Version of *.c files used when building libpng */
+   return png_get_header_ver(png_ptr);
+}
+
+png_const_charp PNGAPI
+png_get_header_ver(png_const_structrp png_ptr)
+{
+   /* Version of *.h files used when building libpng */
+   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
+   return PNG_LIBPNG_VER_STRING;
+}
+
+png_const_charp PNGAPI
+png_get_header_version(png_const_structrp png_ptr)
+{
+   /* Returns longer string containing both version and date */
+   PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
+#ifdef __STDC__
+   return PNG_HEADER_VERSION_STRING
+#  ifndef PNG_READ_SUPPORTED
+      " (NO READ SUPPORT)"
+#  endif
+      PNG_STRING_NEWLINE;
+#else
+   return PNG_HEADER_VERSION_STRING;
+#endif
+}
+
+#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
+/* NOTE: this routine is not used internally! */
+/* Build a grayscale palette.  Palette is assumed to be 1 << bit_depth
+ * large of png_color.  This lets grayscale images be treated as
+ * paletted.  Most useful for gamma correction and simplification
+ * of code.  This API is not used internally.
+ */
+void PNGAPI
+png_build_grayscale_palette(int bit_depth, png_colorp palette)
+{
+   int num_palette;
+   int color_inc;
+   int i;
+   int v;
+
+   png_debug(1, "in png_do_build_grayscale_palette");
+
+   if (palette == NULL)
+      return;
+
+   switch (bit_depth)
+   {
+      case 1:
+         num_palette = 2;
+         color_inc = 0xff;
+         break;
+
+      case 2:
+         num_palette = 4;
+         color_inc = 0x55;
+         break;
+
+      case 4:
+         num_palette = 16;
+         color_inc = 0x11;
+         break;
+
+      case 8:
+         num_palette = 256;
+         color_inc = 1;
+         break;
+
+      default:
+         num_palette = 0;
+         color_inc = 0;
+         break;
+   }
+
+   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
+   {
+      palette[i].red = (png_byte)(v & 0xff);
+      palette[i].green = (png_byte)(v & 0xff);
+      palette[i].blue = (png_byte)(v & 0xff);
+   }
+}
+#endif
+
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+int PNGAPI
+png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name)
+{
+   /* Check chunk_name and return "keep" value if it's on the list, else 0 */
+   png_const_bytep p, p_end;
+
+   if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list == 0)
+      return PNG_HANDLE_CHUNK_AS_DEFAULT;
+
+   p_end = png_ptr->chunk_list;
+   p = p_end + png_ptr->num_chunk_list*5; /* beyond end */
+
+   /* The code is the fifth byte after each four byte string.  Historically this
+    * code was always searched from the end of the list, this is no longer
+    * necessary because the 'set' routine handles duplicate entries correcty.
+    */
+   do /* num_chunk_list > 0, so at least one */
+   {
+      p -= 5;
+
+      if (memcmp(chunk_name, p, 4) == 0)
+         return p[4];
+   }
+   while (p > p_end);
+
+   /* This means that known chunks should be processed and unknown chunks should
+    * be handled according to the value of png_ptr->unknown_default; this can be
+    * confusing because, as a result, there are two levels of defaulting for
+    * unknown chunks.
+    */
+   return PNG_HANDLE_CHUNK_AS_DEFAULT;
+}
+
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
+   defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
+int /* PRIVATE */
+png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name)
+{
+   png_byte chunk_string[5];
+
+   PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name);
+   return png_handle_as_unknown(png_ptr, chunk_string);
+}
+#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */
+#endif /* SET_UNKNOWN_CHUNKS */
+
+#ifdef PNG_READ_SUPPORTED
+/* This function, added to libpng-1.0.6g, is untested. */
+int PNGAPI
+png_reset_zstream(png_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return Z_STREAM_ERROR;
+
+   /* WARNING: this resets the window bits to the maximum! */
+   return (inflateReset(&png_ptr->zstream));
+}
+#endif /* READ */
+
+/* This function was added to libpng-1.0.7 */
+png_uint_32 PNGAPI
+png_access_version_number(void)
+{
+   /* Version of *.c files used when building libpng */
+   return((png_uint_32)PNG_LIBPNG_VER);
+}
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Ensure that png_ptr->zstream.msg holds some appropriate error message string.
+ * If it doesn't 'ret' is used to set it to something appropriate, even in cases
+ * like Z_OK or Z_STREAM_END where the error code is apparently a success code.
+ */
+void /* PRIVATE */
+png_zstream_error(png_structrp png_ptr, int ret)
+{
+   /* Translate 'ret' into an appropriate error string, priority is given to the
+    * one in zstream if set.  This always returns a string, even in cases like
+    * Z_OK or Z_STREAM_END where the error code is a success code.
+    */
+   if (png_ptr->zstream.msg == NULL) switch (ret)
+   {
+      default:
+      case Z_OK:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code");
+         break;
+
+      case Z_STREAM_END:
+         /* Normal exit */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream");
+         break;
+
+      case Z_NEED_DICT:
+         /* This means the deflate stream did not have a dictionary; this
+          * indicates a bogus PNG.
+          */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary");
+         break;
+
+      case Z_ERRNO:
+         /* gz APIs only: should not happen */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error");
+         break;
+
+      case Z_STREAM_ERROR:
+         /* internal libpng error */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib");
+         break;
+
+      case Z_DATA_ERROR:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream");
+         break;
+
+      case Z_MEM_ERROR:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory");
+         break;
+
+      case Z_BUF_ERROR:
+         /* End of input or output; not a problem if the caller is doing
+          * incremental read or write.
+          */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated");
+         break;
+
+      case Z_VERSION_ERROR:
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version");
+         break;
+
+      case PNG_UNEXPECTED_ZLIB_RETURN:
+         /* Compile errors here mean that zlib now uses the value co-opted in
+          * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above
+          * and change pngpriv.h.  Note that this message is "... return",
+          * whereas the default/Z_OK one is "... return code".
+          */
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return");
+         break;
+   }
+}
+
+/* png_convert_size: a PNGAPI but no longer in png.h, so deleted
+ * at libpng 1.5.5!
+ */
+
+/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */
+#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */
+static int
+png_colorspace_check_gamma(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_fixed_point gAMA, int from)
+   /* This is called to check a new gamma value against an existing one.  The
+    * routine returns false if the new gamma value should not be written.
+    *
+    * 'from' says where the new gamma value comes from:
+    *
+    *    0: the new gamma value is the libpng estimate for an ICC profile
+    *    1: the new gamma value comes from a gAMA chunk
+    *    2: the new gamma value comes from an sRGB chunk
+    */
+{
+   png_fixed_point gtest;
+
+   if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
+      (png_muldiv(&gtest, colorspace->gamma, PNG_FP_1, gAMA) == 0  ||
+      png_gamma_significant(gtest) != 0))
+   {
+      /* Either this is an sRGB image, in which case the calculated gamma
+       * approximation should match, or this is an image with a profile and the
+       * value libpng calculates for the gamma of the profile does not match the
+       * value recorded in the file.  The former, sRGB, case is an error, the
+       * latter is just a warning.
+       */
+      if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2)
+      {
+         png_chunk_report(png_ptr, "gamma value does not match sRGB",
+            PNG_CHUNK_ERROR);
+         /* Do not overwrite an sRGB value */
+         return from == 2;
+      }
+
+      else /* sRGB tag not involved */
+      {
+         png_chunk_report(png_ptr, "gamma value does not match libpng estimate",
+            PNG_CHUNK_WARNING);
+         return from == 1;
+      }
+   }
+
+   return 1;
+}
+
+void /* PRIVATE */
+png_colorspace_set_gamma(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_fixed_point gAMA)
+{
+   /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't
+    * occur.  Since the fixed point representation is asymetrical it is
+    * possible for 1/gamma to overflow the limit of 21474 and this means the
+    * gamma value must be at least 5/100000 and hence at most 20000.0.  For
+    * safety the limits here are a little narrower.  The values are 0.00016 to
+    * 6250.0, which are truly ridiculous gamma values (and will produce
+    * displays that are all black or all white.)
+    *
+    * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk
+    * handling code, which only required the value to be >0.
+    */
+   png_const_charp errmsg;
+
+   if (gAMA < 16 || gAMA > 625000000)
+      errmsg = "gamma value out of range";
+
+#  ifdef PNG_READ_gAMA_SUPPORTED
+   /* Allow the application to set the gamma value more than once */
+   else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
+      (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0)
+      errmsg = "duplicate";
+#  endif
+
+   /* Do nothing if the colorspace is already invalid */
+   else if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
+      return;
+
+   else
+   {
+      if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA,
+          1/*from gAMA*/) != 0)
+      {
+         /* Store this gamma value. */
+         colorspace->gamma = gAMA;
+         colorspace->flags |=
+            (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA);
+      }
+
+      /* At present if the check_gamma test fails the gamma of the colorspace is
+       * not updated however the colorspace is not invalidated.  This
+       * corresponds to the case where the existing gamma comes from an sRGB
+       * chunk or profile.  An error message has already been output.
+       */
+      return;
+   }
+
+   /* Error exit - errmsg has been set. */
+   colorspace->flags |= PNG_COLORSPACE_INVALID;
+   png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR);
+}
+
+void /* PRIVATE */
+png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr)
+{
+   if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
+   {
+      /* Everything is invalid */
+      info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB|
+         PNG_INFO_iCCP);
+
+#     ifdef PNG_COLORSPACE_SUPPORTED
+      /* Clean up the iCCP profile now if it won't be used. */
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/);
+#     else
+      PNG_UNUSED(png_ptr)
+#     endif
+   }
+
+   else
+   {
+#     ifdef PNG_COLORSPACE_SUPPORTED
+      /* Leave the INFO_iCCP flag set if the pngset.c code has already set
+       * it; this allows a PNG to contain a profile which matches sRGB and
+       * yet still have that profile retrievable by the application.
+       */
+      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) != 0)
+         info_ptr->valid |= PNG_INFO_sRGB;
+
+      else
+         info_ptr->valid &= ~PNG_INFO_sRGB;
+
+      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+         info_ptr->valid |= PNG_INFO_cHRM;
+
+      else
+         info_ptr->valid &= ~PNG_INFO_cHRM;
+#     endif
+
+      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0)
+         info_ptr->valid |= PNG_INFO_gAMA;
+
+      else
+         info_ptr->valid &= ~PNG_INFO_gAMA;
+   }
+}
+
+#ifdef PNG_READ_SUPPORTED
+void /* PRIVATE */
+png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr)
+{
+   if (info_ptr == NULL) /* reduce code size; check here not in the caller */
+      return;
+
+   info_ptr->colorspace = png_ptr->colorspace;
+   png_colorspace_sync_info(png_ptr, info_ptr);
+}
+#endif
+#endif /* GAMMA */
+
+#ifdef PNG_COLORSPACE_SUPPORTED
+/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for
+ * cHRM, as opposed to using chromaticities.  These internal APIs return
+ * non-zero on a parameter error.  The X, Y and Z values are required to be
+ * positive and less than 1.0.
+ */
+static int
+png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
+{
+   png_int_32 d, dwhite, whiteX, whiteY;
+
+   d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z;
+   if (png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d) == 0)
+      return 1;
+   if (png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d) == 0)
+      return 1;
+   dwhite = d;
+   whiteX = XYZ->red_X;
+   whiteY = XYZ->red_Y;
+
+   d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z;
+   if (png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d) == 0)
+      return 1;
+   if (png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d) == 0)
+      return 1;
+   dwhite += d;
+   whiteX += XYZ->green_X;
+   whiteY += XYZ->green_Y;
+
+   d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z;
+   if (png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d) == 0)
+      return 1;
+   if (png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d) == 0)
+      return 1;
+   dwhite += d;
+   whiteX += XYZ->blue_X;
+   whiteY += XYZ->blue_Y;
+
+   /* The reference white is simply the sum of the end-point (X,Y,Z) vectors,
+    * thus:
+    */
+   if (png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite) == 0)
+      return 1;
+   if (png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite) == 0)
+      return 1;
+
+   return 0;
+}
+
+static int
+png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy)
+{
+   png_fixed_point red_inverse, green_inverse, blue_scale;
+   png_fixed_point left, right, denominator;
+
+   /* Check xy and, implicitly, z.  Note that wide gamut color spaces typically
+    * have end points with 0 tristimulus values (these are impossible end
+    * points, but they are used to cover the possible colors).  We check
+    * xy->whitey against 5, not 0, to avoid a possible integer overflow.
+    */
+   if (xy->redx   < 0 || xy->redx > PNG_FP_1) return 1;
+   if (xy->redy   < 0 || xy->redy > PNG_FP_1-xy->redx) return 1;
+   if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1;
+   if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1;
+   if (xy->bluex  < 0 || xy->bluex > PNG_FP_1) return 1;
+   if (xy->bluey  < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1;
+   if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1;
+   if (xy->whitey < 5 || xy->whitey > PNG_FP_1-xy->whitex) return 1;
+
+   /* The reverse calculation is more difficult because the original tristimulus
+    * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8
+    * derived values were recorded in the cHRM chunk;
+    * (red,green,blue,white)x(x,y).  This loses one degree of freedom and
+    * therefore an arbitrary ninth value has to be introduced to undo the
+    * original transformations.
+    *
+    * Think of the original end-points as points in (X,Y,Z) space.  The
+    * chromaticity values (c) have the property:
+    *
+    *           C
+    *   c = ---------
+    *       X + Y + Z
+    *
+    * For each c (x,y,z) from the corresponding original C (X,Y,Z).  Thus the
+    * three chromaticity values (x,y,z) for each end-point obey the
+    * relationship:
+    *
+    *   x + y + z = 1
+    *
+    * This describes the plane in (X,Y,Z) space that intersects each axis at the
+    * value 1.0; call this the chromaticity plane.  Thus the chromaticity
+    * calculation has scaled each end-point so that it is on the x+y+z=1 plane
+    * and chromaticity is the intersection of the vector from the origin to the
+    * (X,Y,Z) value with the chromaticity plane.
+    *
+    * To fully invert the chromaticity calculation we would need the three
+    * end-point scale factors, (red-scale, green-scale, blue-scale), but these
+    * were not recorded.  Instead we calculated the reference white (X,Y,Z) and
+    * recorded the chromaticity of this.  The reference white (X,Y,Z) would have
+    * given all three of the scale factors since:
+    *
+    *    color-C = color-c * color-scale
+    *    white-C = red-C + green-C + blue-C
+    *            = red-c*red-scale + green-c*green-scale + blue-c*blue-scale
+    *
+    * But cHRM records only white-x and white-y, so we have lost the white scale
+    * factor:
+    *
+    *    white-C = white-c*white-scale
+    *
+    * To handle this the inverse transformation makes an arbitrary assumption
+    * about white-scale:
+    *
+    *    Assume: white-Y = 1.0
+    *    Hence:  white-scale = 1/white-y
+    *    Or:     red-Y + green-Y + blue-Y = 1.0
+    *
+    * Notice the last statement of the assumption gives an equation in three of
+    * the nine values we want to calculate.  8 more equations come from the
+    * above routine as summarised at the top above (the chromaticity
+    * calculation):
+    *
+    *    Given: color-x = color-X / (color-X + color-Y + color-Z)
+    *    Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0
+    *
+    * This is 9 simultaneous equations in the 9 variables "color-C" and can be
+    * solved by Cramer's rule.  Cramer's rule requires calculating 10 9x9 matrix
+    * determinants, however this is not as bad as it seems because only 28 of
+    * the total of 90 terms in the various matrices are non-zero.  Nevertheless
+    * Cramer's rule is notoriously numerically unstable because the determinant
+    * calculation involves the difference of large, but similar, numbers.  It is
+    * difficult to be sure that the calculation is stable for real world values
+    * and it is certain that it becomes unstable where the end points are close
+    * together.
+    *
+    * So this code uses the perhaps slightly less optimal but more
+    * understandable and totally obvious approach of calculating color-scale.
+    *
+    * This algorithm depends on the precision in white-scale and that is
+    * (1/white-y), so we can immediately see that as white-y approaches 0 the
+    * accuracy inherent in the cHRM chunk drops off substantially.
+    *
+    * libpng arithmetic: a simple inversion of the above equations
+    * ------------------------------------------------------------
+    *
+    *    white_scale = 1/white-y
+    *    white-X = white-x * white-scale
+    *    white-Y = 1.0
+    *    white-Z = (1 - white-x - white-y) * white_scale
+    *
+    *    white-C = red-C + green-C + blue-C
+    *            = red-c*red-scale + green-c*green-scale + blue-c*blue-scale
+    *
+    * This gives us three equations in (red-scale,green-scale,blue-scale) where
+    * all the coefficients are now known:
+    *
+    *    red-x*red-scale + green-x*green-scale + blue-x*blue-scale
+    *       = white-x/white-y
+    *    red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1
+    *    red-z*red-scale + green-z*green-scale + blue-z*blue-scale
+    *       = (1 - white-x - white-y)/white-y
+    *
+    * In the last equation color-z is (1 - color-x - color-y) so we can add all
+    * three equations together to get an alternative third:
+    *
+    *    red-scale + green-scale + blue-scale = 1/white-y = white-scale
+    *
+    * So now we have a Cramer's rule solution where the determinants are just
+    * 3x3 - far more tractible.  Unfortunately 3x3 determinants still involve
+    * multiplication of three coefficients so we can't guarantee to avoid
+    * overflow in the libpng fixed point representation.  Using Cramer's rule in
+    * floating point is probably a good choice here, but it's not an option for
+    * fixed point.  Instead proceed to simplify the first two equations by
+    * eliminating what is likely to be the largest value, blue-scale:
+    *
+    *    blue-scale = white-scale - red-scale - green-scale
+    *
+    * Hence:
+    *
+    *    (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale =
+    *                (white-x - blue-x)*white-scale
+    *
+    *    (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale =
+    *                1 - blue-y*white-scale
+    *
+    * And now we can trivially solve for (red-scale,green-scale):
+    *
+    *    green-scale =
+    *                (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale
+    *                -----------------------------------------------------------
+    *                                  green-x - blue-x
+    *
+    *    red-scale =
+    *                1 - blue-y*white-scale - (green-y - blue-y) * green-scale
+    *                ---------------------------------------------------------
+    *                                  red-y - blue-y
+    *
+    * Hence:
+    *
+    *    red-scale =
+    *          ( (green-x - blue-x) * (white-y - blue-y) -
+    *            (green-y - blue-y) * (white-x - blue-x) ) / white-y
+    * -------------------------------------------------------------------------
+    *  (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)
+    *
+    *    green-scale =
+    *          ( (red-y - blue-y) * (white-x - blue-x) -
+    *            (red-x - blue-x) * (white-y - blue-y) ) / white-y
+    * -------------------------------------------------------------------------
+    *  (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)
+    *
+    * Accuracy:
+    * The input values have 5 decimal digits of accuracy.  The values are all in
+    * the range 0 < value < 1, so simple products are in the same range but may
+    * need up to 10 decimal digits to preserve the original precision and avoid
+    * underflow.  Because we are using a 32-bit signed representation we cannot
+    * match this; the best is a little over 9 decimal digits, less than 10.
+    *
+    * The approach used here is to preserve the maximum precision within the
+    * signed representation.  Because the red-scale calculation above uses the
+    * difference between two products of values that must be in the range -1..+1
+    * it is sufficient to divide the product by 7; ceil(100,000/32767*2).  The
+    * factor is irrelevant in the calculation because it is applied to both
+    * numerator and denominator.
+    *
+    * Note that the values of the differences of the products of the
+    * chromaticities in the above equations tend to be small, for example for
+    * the sRGB chromaticities they are:
+    *
+    * red numerator:    -0.04751
+    * green numerator:  -0.08788
+    * denominator:      -0.2241 (without white-y multiplication)
+    *
+    *  The resultant Y coefficients from the chromaticities of some widely used
+    *  color space definitions are (to 15 decimal places):
+    *
+    *  sRGB
+    *    0.212639005871510 0.715168678767756 0.072192315360734
+    *  Kodak ProPhoto
+    *    0.288071128229293 0.711843217810102 0.000085653960605
+    *  Adobe RGB
+    *    0.297344975250536 0.627363566255466 0.075291458493998
+    *  Adobe Wide Gamut RGB
+    *    0.258728243040113 0.724682314948566 0.016589442011321
+    */
+   /* By the argument, above overflow should be impossible here. The return
+    * value of 2 indicates an internal error to the caller.
+    */
+   if (png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7) == 0)
+      return 2;
+   if (png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7) == 0)
+      return 2;
+   denominator = left - right;
+
+   /* Now find the red numerator. */
+   if (png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7) == 0)
+      return 2;
+   if (png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7) == 0)
+      return 2;
+
+   /* Overflow is possible here and it indicates an extreme set of PNG cHRM
+    * chunk values.  This calculation actually returns the reciprocal of the
+    * scale value because this allows us to delay the multiplication of white-y
+    * into the denominator, which tends to produce a small number.
+    */
+   if (png_muldiv(&red_inverse, xy->whitey, denominator, left-right) == 0 ||
+       red_inverse <= xy->whitey /* r+g+b scales = white scale */)
+      return 1;
+
+   /* Similarly for green_inverse: */
+   if (png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7) == 0)
+      return 2;
+   if (png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7) == 0)
+      return 2;
+   if (png_muldiv(&green_inverse, xy->whitey, denominator, left-right) == 0 ||
+       green_inverse <= xy->whitey)
+      return 1;
+
+   /* And the blue scale, the checks above guarantee this can't overflow but it
+    * can still produce 0 for extreme cHRM values.
+    */
+   blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) -
+       png_reciprocal(green_inverse);
+   if (blue_scale <= 0)
+      return 1;
+
+
+   /* And fill in the png_XYZ: */
+   if (png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse) == 0)
+      return 1;
+   if (png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse) == 0)
+      return 1;
+   if (png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1,
+       red_inverse) == 0)
+      return 1;
+
+   if (png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse) == 0)
+      return 1;
+   if (png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse) == 0)
+      return 1;
+   if (png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1,
+       green_inverse) == 0)
+      return 1;
+
+   if (png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1) == 0)
+      return 1;
+   if (png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1) == 0)
+      return 1;
+   if (png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale,
+       PNG_FP_1) == 0)
+      return 1;
+
+   return 0; /*success*/
+}
+
+static int
+png_XYZ_normalize(png_XYZ *XYZ)
+{
+   png_int_32 Y;
+
+   if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 ||
+      XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 ||
+      XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0)
+      return 1;
+
+   /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1.
+    * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore
+    * relying on addition of two positive values producing a negative one is not
+    * safe.
+    */
+   Y = XYZ->red_Y;
+   if (0x7fffffff - Y < XYZ->green_X)
+      return 1;
+   Y += XYZ->green_Y;
+   if (0x7fffffff - Y < XYZ->blue_X)
+      return 1;
+   Y += XYZ->blue_Y;
+
+   if (Y != PNG_FP_1)
+   {
+      if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0)
+         return 1;
+      if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0)
+         return 1;
+      if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0)
+         return 1;
+
+      if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0)
+         return 1;
+      if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0)
+         return 1;
+      if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0)
+         return 1;
+
+      if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0)
+         return 1;
+      if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0)
+         return 1;
+      if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0)
+         return 1;
+   }
+
+   return 0;
+}
+
+static int
+png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta)
+{
+   /* Allow an error of +/-0.01 (absolute value) on each chromaticity */
+   if (PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) ||
+       PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) ||
+       PNG_OUT_OF_RANGE(xy1->redx,   xy2->redx,  delta) ||
+       PNG_OUT_OF_RANGE(xy1->redy,   xy2->redy,  delta) ||
+       PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) ||
+       PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) ||
+       PNG_OUT_OF_RANGE(xy1->bluex,  xy2->bluex, delta) ||
+       PNG_OUT_OF_RANGE(xy1->bluey,  xy2->bluey, delta))
+      return 0;
+   return 1;
+}
+
+/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM
+ * chunk chromaticities.  Earlier checks used to simply look for the overflow
+ * condition (where the determinant of the matrix to solve for XYZ ends up zero
+ * because the chromaticity values are not all distinct.)  Despite this it is
+ * theoretically possible to produce chromaticities that are apparently valid
+ * but that rapidly degrade to invalid, potentially crashing, sets because of
+ * arithmetic inaccuracies when calculations are performed on them.  The new
+ * check is to round-trip xy -> XYZ -> xy and then check that the result is
+ * within a small percentage of the original.
+ */
+static int
+png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy)
+{
+   int result;
+   png_xy xy_test;
+
+   /* As a side-effect this routine also returns the XYZ endpoints. */
+   result = png_XYZ_from_xy(XYZ, xy);
+   if (result != 0)
+      return result;
+
+   result = png_xy_from_XYZ(&xy_test, XYZ);
+   if (result != 0)
+      return result;
+
+   if (png_colorspace_endpoints_match(xy, &xy_test,
+       5/*actually, the math is pretty accurate*/) != 0)
+      return 0;
+
+   /* Too much slip */
+   return 1;
+}
+
+/* This is the check going the other way.  The XYZ is modified to normalize it
+ * (another side-effect) and the xy chromaticities are returned.
+ */
+static int
+png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ)
+{
+   int result;
+   png_XYZ XYZtemp;
+
+   result = png_XYZ_normalize(XYZ);
+   if (result != 0)
+      return result;
+
+   result = png_xy_from_XYZ(xy, XYZ);
+   if (result != 0)
+      return result;
+
+   XYZtemp = *XYZ;
+   return png_colorspace_check_xy(&XYZtemp, xy);
+}
+
+/* Used to check for an endpoint match against sRGB */
+static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */
+{
+   /* color      x       y */
+   /* red   */ 64000, 33000,
+   /* green */ 30000, 60000,
+   /* blue  */ 15000,  6000,
+   /* white */ 31270, 32900
+};
+
+static int
+png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ,
+   int preferred)
+{
+   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
+      return 0;
+
+   /* The consistency check is performed on the chromaticities; this factors out
+    * variations because of the normalization (or not) of the end point Y
+    * values.
+    */
+   if (preferred < 2 &&
+       (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+   {
+      /* The end points must be reasonably close to any we already have.  The
+       * following allows an error of up to +/-.001
+       */
+      if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy,
+          100) == 0)
+      {
+         colorspace->flags |= PNG_COLORSPACE_INVALID;
+         png_benign_error(png_ptr, "inconsistent chromaticities");
+         return 0; /* failed */
+      }
+
+      /* Only overwrite with preferred values */
+      if (preferred == 0)
+         return 1; /* ok, but no change */
+   }
+
+   colorspace->end_points_xy = *xy;
+   colorspace->end_points_XYZ = *XYZ;
+   colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS;
+
+   /* The end points are normally quoted to two decimal digits, so allow +/-0.01
+    * on this test.
+    */
+   if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0)
+      colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB;
+
+   else
+      colorspace->flags &= PNG_COLORSPACE_CANCEL(
+         PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
+
+   return 2; /* ok and changed */
+}
+
+int /* PRIVATE */
+png_colorspace_set_chromaticities(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, const png_xy *xy, int preferred)
+{
+   /* We must check the end points to ensure they are reasonable - in the past
+    * color management systems have crashed as a result of getting bogus
+    * colorant values, while this isn't the fault of libpng it is the
+    * responsibility of libpng because PNG carries the bomb and libpng is in a
+    * position to protect against it.
+    */
+   png_XYZ XYZ;
+
+   switch (png_colorspace_check_xy(&XYZ, xy))
+   {
+      case 0: /* success */
+         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,
+            preferred);
+
+      case 1:
+         /* We can't invert the chromaticities so we can't produce value XYZ
+          * values.  Likely as not a color management system will fail too.
+          */
+         colorspace->flags |= PNG_COLORSPACE_INVALID;
+         png_benign_error(png_ptr, "invalid chromaticities");
+         break;
+
+      default:
+         /* libpng is broken; this should be a warning but if it happens we
+          * want error reports so for the moment it is an error.
+          */
+         colorspace->flags |= PNG_COLORSPACE_INVALID;
+         png_error(png_ptr, "internal error checking chromaticities");
+   }
+
+   return 0; /* failed */
+}
+
+int /* PRIVATE */
+png_colorspace_set_endpoints(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
+{
+   png_XYZ XYZ = *XYZ_in;
+   png_xy xy;
+
+   switch (png_colorspace_check_XYZ(&xy, &XYZ))
+   {
+      case 0:
+         return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ,
+            preferred);
+
+      case 1:
+         /* End points are invalid. */
+         colorspace->flags |= PNG_COLORSPACE_INVALID;
+         png_benign_error(png_ptr, "invalid end points");
+         break;
+
+      default:
+         colorspace->flags |= PNG_COLORSPACE_INVALID;
+         png_error(png_ptr, "internal error checking chromaticities");
+   }
+
+   return 0; /* failed */
+}
+
+#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED)
+/* Error message generation */
+static char
+png_icc_tag_char(png_uint_32 byte)
+{
+   byte &= 0xff;
+   if (byte >= 32 && byte <= 126)
+      return (char)byte;
+   else
+      return '?';
+}
+
+static void
+png_icc_tag_name(char *name, png_uint_32 tag)
+{
+   name[0] = '\'';
+   name[1] = png_icc_tag_char(tag >> 24);
+   name[2] = png_icc_tag_char(tag >> 16);
+   name[3] = png_icc_tag_char(tag >>  8);
+   name[4] = png_icc_tag_char(tag      );
+   name[5] = '\'';
+}
+
+static int
+is_ICC_signature_char(png_alloc_size_t it)
+{
+   return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) ||
+      (it >= 97 && it <= 122);
+}
+
+static int
+is_ICC_signature(png_alloc_size_t it)
+{
+   return is_ICC_signature_char(it >> 24) /* checks all the top bits */ &&
+      is_ICC_signature_char((it >> 16) & 0xff) &&
+      is_ICC_signature_char((it >> 8) & 0xff) &&
+      is_ICC_signature_char(it & 0xff);
+}
+
+static int
+png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
+   png_const_charp name, png_alloc_size_t value, png_const_charp reason)
+{
+   size_t pos;
+   char message[196]; /* see below for calculation */
+
+   if (colorspace != NULL)
+      colorspace->flags |= PNG_COLORSPACE_INVALID;
+
+   pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */
+   pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */
+   pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */
+   if (is_ICC_signature(value) != 0)
+   {
+      /* So 'value' is at most 4 bytes and the following cast is safe */
+      png_icc_tag_name(message+pos, (png_uint_32)value);
+      pos += 6; /* total +8; less than the else clause */
+      message[pos++] = ':';
+      message[pos++] = ' ';
+   }
+#  ifdef PNG_WARNINGS_SUPPORTED
+   else
+      {
+         char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/
+
+         pos = png_safecat(message, (sizeof message), pos,
+            png_format_number(number, number+(sizeof number),
+               PNG_NUMBER_FORMAT_x, value));
+         pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/
+      }
+#  endif
+   /* The 'reason' is an arbitrary message, allow +79 maximum 195 */
+   pos = png_safecat(message, (sizeof message), pos, reason);
+   PNG_UNUSED(pos)
+
+   /* This is recoverable, but make it unconditionally an app_error on write to
+    * avoid writing invalid ICC profiles into PNG files (i.e., we handle them
+    * on read, with a warning, but on write unless the app turns off
+    * application errors the PNG won't be written.)
+    */
+   png_chunk_report(png_ptr, message,
+      (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);
+
+   return 0;
+}
+#endif /* sRGB || iCCP */
+
+#ifdef PNG_sRGB_SUPPORTED
+int /* PRIVATE */
+png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
+   int intent)
+{
+   /* sRGB sets known gamma, end points and (from the chunk) intent. */
+   /* IMPORTANT: these are not necessarily the values found in an ICC profile
+    * because ICC profiles store values adapted to a D50 environment; it is
+    * expected that the ICC profile mediaWhitePointTag will be D50; see the
+    * checks and code elsewhere to understand this better.
+    *
+    * These XYZ values, which are accurate to 5dp, produce rgb to gray
+    * coefficients of (6968,23435,2366), which are reduced (because they add up
+    * to 32769 not 32768) to (6968,23434,2366).  These are the values that
+    * libpng has traditionally used (and are the best values given the 15bit
+    * algorithm used by the rgb to gray code.)
+    */
+   static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */
+   {
+      /* color      X      Y      Z */
+      /* red   */ 41239, 21264,  1933,
+      /* green */ 35758, 71517, 11919,
+      /* blue  */ 18048,  7219, 95053
+   };
+
+   /* Do nothing if the colorspace is already invalidated. */
+   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
+      return 0;
+
+   /* Check the intent, then check for existing settings.  It is valid for the
+    * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must
+    * be consistent with the correct values.  If, however, this function is
+    * called below because an iCCP chunk matches sRGB then it is quite
+    * conceivable that an older app recorded incorrect gAMA and cHRM because of
+    * an incorrect calculation based on the values in the profile - this does
+    * *not* invalidate the profile (though it still produces an error, which can
+    * be ignored.)
+    */
+   if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)
+      return png_icc_profile_error(png_ptr, colorspace, "sRGB",
+         (unsigned)intent, "invalid sRGB rendering intent");
+
+   if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&
+      colorspace->rendering_intent != intent)
+      return png_icc_profile_error(png_ptr, colorspace, "sRGB",
+         (unsigned)intent, "inconsistent rendering intents");
+
+   if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0)
+   {
+      png_benign_error(png_ptr, "duplicate sRGB information ignored");
+      return 0;
+   }
+
+   /* If the standard sRGB cHRM chunk does not match the one from the PNG file
+    * warn but overwrite the value with the correct one.
+    */
+   if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&
+      !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
+         100))
+      png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",
+         PNG_CHUNK_ERROR);
+
+   /* This check is just done for the error reporting - the routine always
+    * returns true when the 'from' argument corresponds to sRGB (2).
+    */
+   (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,
+      2/*from sRGB*/);
+
+   /* intent: bugs in GCC force 'int' to be used as the parameter type. */
+   colorspace->rendering_intent = (png_uint_16)intent;
+   colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT;
+
+   /* endpoints */
+   colorspace->end_points_xy = sRGB_xy;
+   colorspace->end_points_XYZ = sRGB_XYZ;
+   colorspace->flags |=
+      (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
+
+   /* gamma */
+   colorspace->gamma = PNG_GAMMA_sRGB_INVERSE;
+   colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA;
+
+   /* Finally record that we have an sRGB profile */
+   colorspace->flags |=
+      (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB);
+
+   return 1; /* set */
+}
+#endif /* sRGB */
+
+#ifdef PNG_iCCP_SUPPORTED
+/* Encoded value of D50 as an ICC XYZNumber.  From the ICC 2010 spec the value
+ * is XYZ(0.9642,1.0,0.8249), which scales to:
+ *
+ *    (63189.8112, 65536, 54060.6464)
+ */
+static const png_byte D50_nCIEXYZ[12] =
+   { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d };
+
+int /* PRIVATE */
+png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace,
+   png_const_charp name, png_uint_32 profile_length)
+{
+   if (profile_length < 132)
+      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
+         "too short");
+
+   return 1;
+}
+
+int /* PRIVATE */
+png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace,
+   png_const_charp name, png_uint_32 profile_length,
+   png_const_bytep profile/* first 132 bytes only */, int color_type)
+{
+   png_uint_32 temp;
+
+   /* Length check; this cannot be ignored in this code because profile_length
+    * is used later to check the tag table, so even if the profile seems over
+    * long profile_length from the caller must be correct.  The caller can fix
+    * this up on read or write by just passing in the profile header length.
+    */
+   temp = png_get_uint_32(profile);
+   if (temp != profile_length)
+      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         "length does not match profile");
+
+   temp = (png_uint_32) (*(profile+8));
+   if (temp > 3 && (profile_length & 3))
+      return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
+         "invalid length");
+
+   temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */
+   if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */
+      profile_length < 132+12*temp) /* truncated tag table */
+      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         "tag count too large");
+
+   /* The 'intent' must be valid or we can't store it, ICC limits the intent to
+    * 16 bits.
+    */
+   temp = png_get_uint_32(profile+64);
+   if (temp >= 0xffff) /* The ICC limit */
+      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         "invalid rendering intent");
+
+   /* This is just a warning because the profile may be valid in future
+    * versions.
+    */
+   if (temp >= PNG_sRGB_INTENT_LAST)
+      (void)png_icc_profile_error(png_ptr, NULL, name, temp,
+         "intent outside defined range");
+
+   /* At this point the tag table can't be checked because it hasn't necessarily
+    * been loaded; however, various header fields can be checked.  These checks
+    * are for values permitted by the PNG spec in an ICC profile; the PNG spec
+    * restricts the profiles that can be passed in an iCCP chunk (they must be
+    * appropriate to processing PNG data!)
+    */
+
+   /* Data checks (could be skipped).  These checks must be independent of the
+    * version number; however, the version number doesn't accomodate changes in
+    * the header fields (just the known tags and the interpretation of the
+    * data.)
+    */
+   temp = png_get_uint_32(profile+36); /* signature 'ascp' */
+   if (temp != 0x61637370)
+      return png_icc_profile_error(png_ptr, colorspace, name, temp,
+         "invalid signature");
+
+   /* Currently the PCS illuminant/adopted white point (the computational
+    * white point) are required to be D50,
+    * however the profile contains a record of the illuminant so perhaps ICC
+    * expects to be able to change this in the future (despite the rationale in
+    * the introduction for using a fixed PCS adopted white.)  Consequently the
+    * following is just a warning.
+    */
+   if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0)
+      (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/,
+         "PCS illuminant is not D50");
+
+   /* The PNG spec requires this:
+    * "If the iCCP chunk is present, the image samples conform to the colour
+    * space represented by the embedded ICC profile as defined by the
+    * International Color Consortium [ICC]. The colour space of the ICC profile
+    * shall be an RGB colour space for colour images (PNG colour types 2, 3, and
+    * 6), or a greyscale colour space for greyscale images (PNG colour types 0
+    * and 4)."
+    *
+    * This checking code ensures the embedded profile (on either read or write)
+    * conforms to the specification requirements.  Notice that an ICC 'gray'
+    * color-space profile contains the information to transform the monochrome
+    * data to XYZ or L*a*b (according to which PCS the profile uses) and this
+    * should be used in preference to the standard libpng K channel replication
+    * into R, G and B channels.
+    *
+    * Previously it was suggested that an RGB profile on grayscale data could be
+    * handled.  However it it is clear that using an RGB profile in this context
+    * must be an error - there is no specification of what it means.  Thus it is
+    * almost certainly more correct to ignore the profile.
+    */
+   temp = png_get_uint_32(profile+16); /* data colour space field */
+   switch (temp)
+   {
+      case 0x52474220: /* 'RGB ' */
+         if ((color_type & PNG_COLOR_MASK_COLOR) == 0)
+            return png_icc_profile_error(png_ptr, colorspace, name, temp,
+               "RGB color space not permitted on grayscale PNG");
+         break;
+
+      case 0x47524159: /* 'GRAY' */
+         if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
+            return png_icc_profile_error(png_ptr, colorspace, name, temp,
+               "Gray color space not permitted on RGB PNG");
+         break;
+
+      default:
+         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            "invalid ICC profile color space");
+   }
+
+   /* It is up to the application to check that the profile class matches the
+    * application requirements; the spec provides no guidance, but it's pretty
+    * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer
+    * ('prtr') or 'spac' (for generic color spaces).  Issue a warning in these
+    * cases.  Issue an error for device link or abstract profiles - these don't
+    * contain the records necessary to transform the color-space to anything
+    * other than the target device (and not even that for an abstract profile).
+    * Profiles of these classes may not be embedded in images.
+    */
+   temp = png_get_uint_32(profile+12); /* profile/device class */
+   switch (temp)
+   {
+      case 0x73636e72: /* 'scnr' */
+      case 0x6d6e7472: /* 'mntr' */
+      case 0x70727472: /* 'prtr' */
+      case 0x73706163: /* 'spac' */
+         /* All supported */
+         break;
+
+      case 0x61627374: /* 'abst' */
+         /* May not be embedded in an image */
+         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            "invalid embedded Abstract ICC profile");
+
+      case 0x6c696e6b: /* 'link' */
+         /* DeviceLink profiles cannot be interpreted in a non-device specific
+          * fashion, if an app uses the AToB0Tag in the profile the results are
+          * undefined unless the result is sent to the intended device,
+          * therefore a DeviceLink profile should not be found embedded in a
+          * PNG.
+          */
+         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            "unexpected DeviceLink ICC profile class");
+
+      case 0x6e6d636c: /* 'nmcl' */
+         /* A NamedColor profile is also device specific, however it doesn't
+          * contain an AToB0 tag that is open to misinterpretation.  Almost
+          * certainly it will fail the tests below.
+          */
+         (void)png_icc_profile_error(png_ptr, NULL, name, temp,
+            "unexpected NamedColor ICC profile class");
+         break;
+
+      default:
+         /* To allow for future enhancements to the profile accept unrecognized
+          * profile classes with a warning, these then hit the test below on the
+          * tag content to ensure they are backward compatible with one of the
+          * understood profiles.
+          */
+         (void)png_icc_profile_error(png_ptr, NULL, name, temp,
+            "unrecognized ICC profile class");
+         break;
+   }
+
+   /* For any profile other than a device link one the PCS must be encoded
+    * either in XYZ or Lab.
+    */
+   temp = png_get_uint_32(profile+20);
+   switch (temp)
+   {
+      case 0x58595a20: /* 'XYZ ' */
+      case 0x4c616220: /* 'Lab ' */
+         break;
+
+      default:
+         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            "unexpected ICC PCS encoding");
+   }
+
+   return 1;
+}
+
+int /* PRIVATE */
+png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace,
+   png_const_charp name, png_uint_32 profile_length,
+   png_const_bytep profile /* header plus whole tag table */)
+{
+   png_uint_32 tag_count = png_get_uint_32(profile+128);
+   png_uint_32 itag;
+   png_const_bytep tag = profile+132; /* The first tag */
+
+   /* First scan all the tags in the table and add bits to the icc_info value
+    * (temporarily in 'tags').
+    */
+   for (itag=0; itag < tag_count; ++itag, tag += 12)
+   {
+      png_uint_32 tag_id = png_get_uint_32(tag+0);
+      png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */
+      png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */
+
+      /* The ICC specification does not exclude zero length tags, therefore the
+       * start might actually be anywhere if there is no data, but this would be
+       * a clear abuse of the intent of the standard so the start is checked for
+       * being in range.  All defined tag types have an 8 byte header - a 4 byte
+       * type signature then 0.
+       */
+      if ((tag_start & 3) != 0)
+      {
+         /* CNHP730S.icc shipped with Microsoft Windows 64 violates this, it is
+          * only a warning here because libpng does not care about the
+          * alignment.
+          */
+         (void)png_icc_profile_error(png_ptr, NULL, name, tag_id,
+            "ICC profile tag start not a multiple of 4");
+      }
+
+      /* This is a hard error; potentially it can cause read outside the
+       * profile.
+       */
+      if (tag_start > profile_length || tag_length > profile_length - tag_start)
+         return png_icc_profile_error(png_ptr, colorspace, name, tag_id,
+            "ICC profile tag outside profile");
+   }
+
+   return 1; /* success, maybe with warnings */
+}
+
+#ifdef PNG_sRGB_SUPPORTED
+#if PNG_sRGB_PROFILE_CHECKS >= 0
+/* Information about the known ICC sRGB profiles */
+static const struct
+{
+   png_uint_32 adler, crc, length;
+   png_uint_32 md5[4];
+   png_byte    have_md5;
+   png_byte    is_broken;
+   png_uint_16 intent;
+
+#  define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0)
+#  define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\
+      { adler, crc, length, md5, broke, intent },
+
+} png_sRGB_checks[] =
+{
+   /* This data comes from contrib/tools/checksum-icc run on downloads of
+    * all four ICC sRGB profiles from www.color.org.
+    */
+   /* adler32, crc32, MD5[4], intent, date, length, file-name */
+   PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9,
+      PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0,
+      "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc")
+
+   /* ICC sRGB v2 perceptual no black-compensation: */
+   PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21,
+      PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0,
+      "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc")
+
+   PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae,
+      PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0,
+      "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc")
+
+   /* ICC sRGB v4 perceptual */
+   PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812,
+      PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0,
+      "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc")
+
+   /* The following profiles have no known MD5 checksum. If there is a match
+    * on the (empty) MD5 the other fields are used to attempt a match and
+    * a warning is produced.  The first two of these profiles have a 'cprt' tag
+    * which suggests that they were also made by Hewlett Packard.
+    */
+   PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce,
+      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0,
+      "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc")
+
+   /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not
+    * match the D50 PCS illuminant in the header (it is in fact the D65 values,
+    * so the white point is recorded as the un-adapted value.)  The profiles
+    * below only differ in one byte - the intent - and are basically the same as
+    * the previous profile except for the mediaWhitePointTag error and a missing
+    * chromaticAdaptationTag.
+    */
+   PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552,
+      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/,
+      "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual")
+
+   PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
+      PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
+      "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
+};
+
+static int
+png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr,
+   png_const_bytep profile, uLong adler)
+{
+   /* The quick check is to verify just the MD5 signature and trust the
+    * rest of the data.  Because the profile has already been verified for
+    * correctness this is safe.  png_colorspace_set_sRGB will check the 'intent'
+    * field too, so if the profile has been edited with an intent not defined
+    * by sRGB (but maybe defined by a later ICC specification) the read of
+    * the profile will fail at that point.
+    */
+
+   png_uint_32 length = 0;
+   png_uint_32 intent = 0x10000; /* invalid */
+#if PNG_sRGB_PROFILE_CHECKS > 1
+   uLong crc = 0; /* the value for 0 length data */
+#endif
+   unsigned int i;
+
+#ifdef PNG_SET_OPTION_SUPPORTED
+   /* First see if PNG_SKIP_sRGB_CHECK_PROFILE has been set to "on" */
+   if (((png_ptr->options >> PNG_SKIP_sRGB_CHECK_PROFILE) & 3) ==
+               PNG_OPTION_ON)
+      return 0;
+#endif
+
+   for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i)
+   {
+      if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] &&
+         png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] &&
+         png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] &&
+         png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3])
+      {
+         /* This may be one of the old HP profiles without an MD5, in that
+          * case we can only use the length and Adler32 (note that these
+          * are not used by default if there is an MD5!)
+          */
+#        if PNG_sRGB_PROFILE_CHECKS == 0
+            if (png_sRGB_checks[i].have_md5 != 0)
+               return 1+png_sRGB_checks[i].is_broken;
+#        endif
+
+         /* Profile is unsigned or more checks have been configured in. */
+         if (length == 0)
+         {
+            length = png_get_uint_32(profile);
+            intent = png_get_uint_32(profile+64);
+         }
+
+         /* Length *and* intent must match */
+         if (length == (png_uint_32) png_sRGB_checks[i].length &&
+            intent == (png_uint_32) png_sRGB_checks[i].intent)
+         {
+            /* Now calculate the adler32 if not done already. */
+            if (adler == 0)
+            {
+               adler = adler32(0, NULL, 0);
+               adler = adler32(adler, profile, length);
+            }
+
+            if (adler == png_sRGB_checks[i].adler)
+            {
+               /* These basic checks suggest that the data has not been
+                * modified, but if the check level is more than 1 perform
+                * our own crc32 checksum on the data.
+                */
+#              if PNG_sRGB_PROFILE_CHECKS > 1
+                  if (crc == 0)
+                  {
+                     crc = crc32(0, NULL, 0);
+                     crc = crc32(crc, profile, length);
+                  }
+
+                  /* So this check must pass for the 'return' below to happen.
+                   */
+                  if (crc == png_sRGB_checks[i].crc)
+#              endif
+               {
+                  if (png_sRGB_checks[i].is_broken != 0)
+                  {
+                     /* These profiles are known to have bad data that may cause
+                      * problems if they are used, therefore attempt to
+                      * discourage their use, skip the 'have_md5' warning below,
+                      * which is made irrelevant by this error.
+                      */
+                     png_chunk_report(png_ptr, "known incorrect sRGB profile",
+                        PNG_CHUNK_ERROR);
+                  }
+
+                  /* Warn that this being done; this isn't even an error since
+                   * the profile is perfectly valid, but it would be nice if
+                   * people used the up-to-date ones.
+                   */
+                  else if (png_sRGB_checks[i].have_md5 == 0)
+                  {
+                     png_chunk_report(png_ptr,
+                        "out-of-date sRGB profile with no signature",
+                        PNG_CHUNK_WARNING);
+                  }
+
+                  return 1+png_sRGB_checks[i].is_broken;
+               }
+            }
+
+# if PNG_sRGB_PROFILE_CHECKS > 0
+         /* The signature matched, but the profile had been changed in some
+          * way.  This probably indicates a data error or uninformed hacking.
+          * Fall through to "no match".
+          */
+         png_chunk_report(png_ptr,
+             "Not recognizing known sRGB profile that has been edited",
+             PNG_CHUNK_WARNING);
+         break;
+# endif
+         }
+      }
+   }
+
+   return 0; /* no match */
+}
+#endif /* PNG_sRGB_PROFILE_CHECKS >= 0 */
+
+void /* PRIVATE */
+png_icc_set_sRGB(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
+{
+   /* Is this profile one of the known ICC sRGB profiles?  If it is, just set
+    * the sRGB information.
+    */
+#if PNG_sRGB_PROFILE_CHECKS >= 0
+   if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler) != 0)
+#endif
+      (void)png_colorspace_set_sRGB(png_ptr, colorspace,
+         (int)/*already checked*/png_get_uint_32(profile+64));
+}
+#endif /* sRGB */
+
+int /* PRIVATE */
+png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace,
+   png_const_charp name, png_uint_32 profile_length, png_const_bytep profile,
+   int color_type)
+{
+   if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
+      return 0;
+
+   if (png_icc_check_length(png_ptr, colorspace, name, profile_length) != 0 &&
+       png_icc_check_header(png_ptr, colorspace, name, profile_length, profile,
+          color_type) != 0 &&
+       png_icc_check_tag_table(png_ptr, colorspace, name, profile_length,
+          profile) != 0)
+   {
+#     ifdef PNG_sRGB_SUPPORTED
+         /* If no sRGB support, don't try storing sRGB information */
+         png_icc_set_sRGB(png_ptr, colorspace, profile, 0);
+#     endif
+      return 1;
+   }
+
+   /* Failure case */
+   return 0;
+}
+#endif /* iCCP */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+void /* PRIVATE */
+png_colorspace_set_rgb_coefficients(png_structrp png_ptr)
+{
+   /* Set the rgb_to_gray coefficients from the colorspace. */
+   if (png_ptr->rgb_to_gray_coefficients_set == 0 &&
+      (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+   {
+      /* png_set_background has not been called, get the coefficients from the Y
+       * values of the colorspace colorants.
+       */
+      png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y;
+      png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y;
+      png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y;
+      png_fixed_point total = r+g+b;
+
+      if (total > 0 &&
+         r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 &&
+         g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 &&
+         b >= 0 && png_muldiv(&b, b, 32768, total) && 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");
+
+         else
+         {
+            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");
+   }
+}
+#endif /* READ_RGB_TO_GRAY */
+
+#endif /* COLORSPACE */
+
+#ifdef __GNUC__
+/* This exists solely to work round a warning from GNU C. */
+static int /* PRIVATE */
+png_gt(size_t a, size_t b)
+{
+    return a > b;
+}
+#else
+#   define png_gt(a,b) ((a) > (b))
+#endif
+
+void /* PRIVATE */
+png_check_IHDR(png_const_structrp png_ptr,
+   png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_type, int compression_type,
+   int filter_type)
+{
+   int error = 0;
+
+   /* Check for width and height valid values */
+   if (width == 0)
+   {
+      png_warning(png_ptr, "Image width is zero in IHDR");
+      error = 1;
+   }
+
+   if (width > PNG_UINT_31_MAX)
+   {
+      png_warning(png_ptr, "Invalid image width in IHDR");
+      error = 1;
+   }
+
+   if (png_gt(((width + 7) & (~7)),
+       ((PNG_SIZE_MAX
+           - 48        /* big_row_buf hack */
+           - 1)        /* filter byte */
+           / 8)        /* 8-byte RGBA pixels */
+           - 1))       /* extra max_pixel_depth pad */
+   {
+      /* The size of the row must be within the limits of this architecture.
+       * Because the read code can perform arbitrary transformations the
+       * maximum size is checked here.  Because the code in png_read_start_row
+       * adds extra space "for safety's sake" in several places a conservative
+       * limit is used here.
+       *
+       * NOTE: it would be far better to check the size that is actually used,
+       * but the effect in the real world is minor and the changes are more
+       * extensive, therefore much more dangerous and much more difficult to
+       * write in a way that avoids compiler warnings.
+       */
+      png_warning(png_ptr, "Image width is too large for this architecture");
+      error = 1;
+   }
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   if (width > png_ptr->user_width_max)
+#else
+   if (width > PNG_USER_WIDTH_MAX)
+#endif
+   {
+      png_warning(png_ptr, "Image width exceeds user limit in IHDR");
+      error = 1;
+   }
+
+   if (height == 0)
+   {
+      png_warning(png_ptr, "Image height is zero in IHDR");
+      error = 1;
+   }
+
+   if (height > PNG_UINT_31_MAX)
+   {
+      png_warning(png_ptr, "Invalid image height in IHDR");
+      error = 1;
+   }
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   if (height > png_ptr->user_height_max)
+#else
+   if (height > PNG_USER_HEIGHT_MAX)
+#endif
+   {
+      png_warning(png_ptr, "Image height exceeds user limit in IHDR");
+      error = 1;
+   }
+
+   /* Check other values */
+   if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
+       bit_depth != 8 && bit_depth != 16)
+   {
+      png_warning(png_ptr, "Invalid bit depth in IHDR");
+      error = 1;
+   }
+
+   if (color_type < 0 || color_type == 1 ||
+       color_type == 5 || color_type > 6)
+   {
+      png_warning(png_ptr, "Invalid color type in IHDR");
+      error = 1;
+   }
+
+   if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) ||
+       ((color_type == PNG_COLOR_TYPE_RGB ||
+         color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+         color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))
+   {
+      png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR");
+      error = 1;
+   }
+
+   if (interlace_type >= PNG_INTERLACE_LAST)
+   {
+      png_warning(png_ptr, "Unknown interlace method in IHDR");
+      error = 1;
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Unknown compression method in IHDR");
+      error = 1;
+   }
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   /* Accept filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not read a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 &&
+       png_ptr->mng_features_permitted != 0)
+      png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
+
+   if (filter_type != PNG_FILTER_TYPE_BASE)
+   {
+      if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+          (filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&
+          ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
+          (color_type == PNG_COLOR_TYPE_RGB ||
+          color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
+      {
+         png_warning(png_ptr, "Unknown filter method in IHDR");
+         error = 1;
+      }
+
+      if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0)
+      {
+         png_warning(png_ptr, "Invalid filter method in IHDR");
+         error = 1;
+      }
+   }
+
+#else
+   if (filter_type != PNG_FILTER_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Unknown filter method in IHDR");
+      error = 1;
+   }
+#endif
+
+   if (error == 1)
+      png_error(png_ptr, "Invalid IHDR data");
+}
+
+#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
+/* ASCII to fp functions */
+/* Check an ASCII formated floating point value, see the more detailed
+ * comments in pngpriv.h
+ */
+/* The following is used internally to preserve the sticky flags */
+#define png_fp_add(state, flags) ((state) |= (flags))
+#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY))
+
+int /* PRIVATE */
+png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
+   png_size_tp whereami)
+{
+   int state = *statep;
+   png_size_t i = *whereami;
+
+   while (i < size)
+   {
+      int type;
+      /* First find the type of the next character */
+      switch (string[i])
+      {
+      case 43:  type = PNG_FP_SAW_SIGN;                   break;
+      case 45:  type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break;
+      case 46:  type = PNG_FP_SAW_DOT;                    break;
+      case 48:  type = PNG_FP_SAW_DIGIT;                  break;
+      case 49: case 50: case 51: case 52:
+      case 53: case 54: case 55: case 56:
+      case 57:  type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break;
+      case 69:
+      case 101: type = PNG_FP_SAW_E;                      break;
+      default:  goto PNG_FP_End;
+      }
+
+      /* Now deal with this type according to the current
+       * state, the type is arranged to not overlap the
+       * bits of the PNG_FP_STATE.
+       */
+      switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY))
+      {
+      case PNG_FP_INTEGER + PNG_FP_SAW_SIGN:
+         if ((state & PNG_FP_SAW_ANY) != 0)
+            goto PNG_FP_End; /* not a part of the number */
+
+         png_fp_add(state, type);
+         break;
+
+      case PNG_FP_INTEGER + PNG_FP_SAW_DOT:
+         /* Ok as trailer, ok as lead of fraction. */
+         if ((state & PNG_FP_SAW_DOT) != 0) /* two dots */
+            goto PNG_FP_End;
+
+         else if ((state & PNG_FP_SAW_DIGIT) != 0) /* trailing dot? */
+            png_fp_add(state, type);
+
+         else
+            png_fp_set(state, PNG_FP_FRACTION | type);
+
+         break;
+
+      case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT:
+         if ((state & PNG_FP_SAW_DOT) != 0) /* delayed fraction */
+            png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
+
+         png_fp_add(state, type | PNG_FP_WAS_VALID);
+
+         break;
+
+      case PNG_FP_INTEGER + PNG_FP_SAW_E:
+         if ((state & PNG_FP_SAW_DIGIT) == 0)
+            goto PNG_FP_End;
+
+         png_fp_set(state, PNG_FP_EXPONENT);
+
+         break;
+
+   /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN:
+         goto PNG_FP_End; ** no sign in fraction */
+
+   /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT:
+         goto PNG_FP_End; ** Because SAW_DOT is always set */
+
+      case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT:
+         png_fp_add(state, type | PNG_FP_WAS_VALID);
+         break;
+
+      case PNG_FP_FRACTION + PNG_FP_SAW_E:
+         /* This is correct because the trailing '.' on an
+          * integer is handled above - so we can only get here
+          * with the sequence ".E" (with no preceding digits).
+          */
+         if ((state & PNG_FP_SAW_DIGIT) == 0)
+            goto PNG_FP_End;
+
+         png_fp_set(state, PNG_FP_EXPONENT);
+
+         break;
+
+      case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN:
+         if ((state & PNG_FP_SAW_ANY) != 0)
+            goto PNG_FP_End; /* not a part of the number */
+
+         png_fp_add(state, PNG_FP_SAW_SIGN);
+
+         break;
+
+   /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT:
+         goto PNG_FP_End; */
+
+      case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT:
+         png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID);
+
+         break;
+
+   /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E:
+         goto PNG_FP_End; */
+
+      default: goto PNG_FP_End; /* I.e. break 2 */
+      }
+
+      /* The character seems ok, continue. */
+      ++i;
+   }
+
+PNG_FP_End:
+   /* Here at the end, update the state and return the correct
+    * return code.
+    */
+   *statep = state;
+   *whereami = i;
+
+   return (state & PNG_FP_SAW_DIGIT) != 0;
+}
+
+
+/* The same but for a complete string. */
+int
+png_check_fp_string(png_const_charp string, png_size_t size)
+{
+   int        state=0;
+   png_size_t char_index=0;
+
+   if (png_check_fp_number(string, size, &state, &char_index) != 0 &&
+      (char_index == size || string[char_index] == 0))
+      return state /* must be non-zero - see above */;
+
+   return 0; /* i.e. fail */
+}
+#endif /* pCAL || sCAL */
+
+#ifdef PNG_sCAL_SUPPORTED
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+/* Utility used below - a simple accurate power of ten from an integral
+ * exponent.
+ */
+static double
+png_pow10(int power)
+{
+   int recip = 0;
+   double d = 1;
+
+   /* Handle negative exponent with a reciprocal at the end because
+    * 10 is exact whereas .1 is inexact in base 2
+    */
+   if (power < 0)
+   {
+      if (power < DBL_MIN_10_EXP) return 0;
+      recip = 1, power = -power;
+   }
+
+   if (power > 0)
+   {
+      /* Decompose power bitwise. */
+      double mult = 10;
+      do
+      {
+         if (power & 1) d *= mult;
+         mult *= mult;
+         power >>= 1;
+      }
+      while (power > 0);
+
+      if (recip != 0) d = 1/d;
+   }
+   /* else power is 0 and d is 1 */
+
+   return d;
+}
+
+/* Function to format a floating point value in ASCII with a given
+ * precision.
+ */
+void /* PRIVATE */
+png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size,
+    double fp, unsigned int precision)
+{
+   /* We use standard functions from math.h, but not printf because
+    * that would require stdio.  The caller must supply a buffer of
+    * sufficient size or we will png_error.  The tests on size and
+    * the space in ascii[] consumed are indicated below.
+    */
+   if (precision < 1)
+      precision = DBL_DIG;
+
+   /* Enforce the limit of the implementation precision too. */
+   if (precision > DBL_DIG+1)
+      precision = DBL_DIG+1;
+
+   /* Basic sanity checks */
+   if (size >= precision+5) /* See the requirements below. */
+   {
+      if (fp < 0)
+      {
+         fp = -fp;
+         *ascii++ = 45; /* '-'  PLUS 1 TOTAL 1 */
+         --size;
+      }
+
+      if (fp >= DBL_MIN && fp <= DBL_MAX)
+      {
+         int exp_b10;   /* A base 10 exponent */
+         double base;   /* 10^exp_b10 */
+
+         /* First extract a base 10 exponent of the number,
+          * the calculation below rounds down when converting
+          * from base 2 to base 10 (multiply by log10(2) -
+          * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to
+          * be increased.  Note that the arithmetic shift
+          * performs a floor() unlike C arithmetic - using a
+          * C multiply would break the following for negative
+          * exponents.
+          */
+         (void)frexp(fp, &exp_b10); /* exponent to base 2 */
+
+         exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */
+
+         /* Avoid underflow here. */
+         base = png_pow10(exp_b10); /* May underflow */
+
+         while (base < DBL_MIN || base < fp)
+         {
+            /* And this may overflow. */
+            double test = png_pow10(exp_b10+1);
+
+            if (test <= DBL_MAX)
+               ++exp_b10, base = test;
+
+            else
+               break;
+         }
+
+         /* Normalize fp and correct exp_b10, after this fp is in the
+          * range [.1,1) and exp_b10 is both the exponent and the digit
+          * *before* which the decimal point should be inserted
+          * (starting with 0 for the first digit).  Note that this
+          * works even if 10^exp_b10 is out of range because of the
+          * test on DBL_MAX above.
+          */
+         fp /= base;
+         while (fp >= 1) fp /= 10, ++exp_b10;
+
+         /* Because of the code above fp may, at this point, be
+          * less than .1, this is ok because the code below can
+          * handle the leading zeros this generates, so no attempt
+          * is made to correct that here.
+          */
+
+         {
+            unsigned int czero, clead, cdigits;
+            char exponent[10];
+
+            /* Allow up to two leading zeros - this will not lengthen
+             * the number compared to using E-n.
+             */
+            if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */
+            {
+               czero = -exp_b10; /* PLUS 2 digits: TOTAL 3 */
+               exp_b10 = 0;      /* Dot added below before first output. */
+            }
+            else
+               czero = 0;    /* No zeros to add */
+
+            /* Generate the digit list, stripping trailing zeros and
+             * inserting a '.' before a digit if the exponent is 0.
+             */
+            clead = czero; /* Count of leading zeros */
+            cdigits = 0;   /* Count of digits in list. */
+
+            do
+            {
+               double d;
+
+               fp *= 10;
+               /* Use modf here, not floor and subtract, so that
+                * the separation is done in one step.  At the end
+                * of the loop don't break the number into parts so
+                * that the final digit is rounded.
+                */
+               if (cdigits+czero+1 < precision+clead)
+                  fp = modf(fp, &d);
+
+               else
+               {
+                  d = floor(fp + .5);
+
+                  if (d > 9)
+                  {
+                     /* Rounding up to 10, handle that here. */
+                     if (czero > 0)
+                     {
+                        --czero, d = 1;
+                        if (cdigits == 0) --clead;
+                     }
+                     else
+                     {
+                        while (cdigits > 0 && d > 9)
+                        {
+                           int ch = *--ascii;
+
+                           if (exp_b10 != (-1))
+                              ++exp_b10;
+
+                           else if (ch == 46)
+                           {
+                              ch = *--ascii, ++size;
+                              /* Advance exp_b10 to '1', so that the
+                               * decimal point happens after the
+                               * previous digit.
+                               */
+                              exp_b10 = 1;
+                           }
+
+                           --cdigits;
+                           d = ch - 47;  /* I.e. 1+(ch-48) */
+                        }
+
+                        /* Did we reach the beginning? If so adjust the
+                         * exponent but take into account the leading
+                         * decimal point.
+                         */
+                        if (d > 9)  /* cdigits == 0 */
+                        {
+                           if (exp_b10 == (-1))
+                           {
+                              /* Leading decimal point (plus zeros?), if
+                               * we lose the decimal point here it must
+                               * be reentered below.
+                               */
+                              int ch = *--ascii;
+
+                              if (ch == 46)
+                                 ++size, exp_b10 = 1;
+
+                              /* Else lost a leading zero, so 'exp_b10' is
+                               * still ok at (-1)
+                               */
+                           }
+                           else
+                              ++exp_b10;
+
+                           /* In all cases we output a '1' */
+                           d = 1;
+                        }
+                     }
+                  }
+                  fp = 0; /* Guarantees termination below. */
+               }
+
+               if (d == 0)
+               {
+                  ++czero;
+                  if (cdigits == 0) ++clead;
+               }
+               else
+               {
+                  /* Included embedded zeros in the digit count. */
+                  cdigits += czero - clead;
+                  clead = 0;
+
+                  while (czero > 0)
+                  {
+                     /* exp_b10 == (-1) means we just output the decimal
+                      * place - after the DP don't adjust 'exp_b10' any
+                      * more!
+                      */
+                     if (exp_b10 != (-1))
+                     {
+                        if (exp_b10 == 0) *ascii++ = 46, --size;
+                        /* PLUS 1: TOTAL 4 */
+                        --exp_b10;
+                     }
+                     *ascii++ = 48, --czero;
+                  }
+
+                  if (exp_b10 != (-1))
+                  {
+                     if (exp_b10 == 0)
+                        *ascii++ = 46, --size; /* counted above */
+
+                     --exp_b10;
+                  }
+                  *ascii++ = (char)(48 + (int)d), ++cdigits;
+               }
+            }
+            while (cdigits+czero < precision+clead && fp > DBL_MIN);
+
+            /* The total output count (max) is now 4+precision */
+
+            /* Check for an exponent, if we don't need one we are
+             * done and just need to terminate the string.  At
+             * this point exp_b10==(-1) is effectively if flag - it got
+             * to '-1' because of the decrement after outputting
+             * the decimal point above (the exponent required is
+             * *not* -1!)
+             */
+            if (exp_b10 >= (-1) && exp_b10 <= 2)
+            {
+               /* The following only happens if we didn't output the
+                * leading zeros above for negative exponent, so this
+                * doesn't add to the digit requirement.  Note that the
+                * two zeros here can only be output if the two leading
+                * zeros were *not* output, so this doesn't increase
+                * the output count.
+                */
+               while (--exp_b10 >= 0) *ascii++ = 48;
+
+               *ascii = 0;
+
+               /* Total buffer requirement (including the '\0') is
+                * 5+precision - see check at the start.
+                */
+               return;
+            }
+
+            /* Here if an exponent is required, adjust size for
+             * the digits we output but did not count.  The total
+             * digit output here so far is at most 1+precision - no
+             * decimal point and no leading or trailing zeros have
+             * been output.
+             */
+            size -= cdigits;
+
+            *ascii++ = 69, --size;    /* 'E': PLUS 1 TOTAL 2+precision */
+
+            /* The following use of an unsigned temporary avoids ambiguities in
+             * the signed arithmetic on exp_b10 and permits GCC at least to do
+             * better optimization.
+             */
+            {
+               unsigned int uexp_b10;
+
+               if (exp_b10 < 0)
+               {
+                  *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */
+                  uexp_b10 = -exp_b10;
+               }
+
+               else
+                  uexp_b10 = exp_b10;
+
+               cdigits = 0;
+
+               while (uexp_b10 > 0)
+               {
+                  exponent[cdigits++] = (char)(48 + uexp_b10 % 10);
+                  uexp_b10 /= 10;
+               }
+            }
+
+            /* Need another size check here for the exponent digits, so
+             * this need not be considered above.
+             */
+            if (size > cdigits)
+            {
+               while (cdigits > 0) *ascii++ = exponent[--cdigits];
+
+               *ascii = 0;
+
+               return;
+            }
+         }
+      }
+      else if (!(fp >= DBL_MIN))
+      {
+         *ascii++ = 48; /* '0' */
+         *ascii = 0;
+         return;
+      }
+      else
+      {
+         *ascii++ = 105; /* 'i' */
+         *ascii++ = 110; /* 'n' */
+         *ascii++ = 102; /* 'f' */
+         *ascii = 0;
+         return;
+      }
+   }
+
+   /* Here on buffer too small. */
+   png_error(png_ptr, "ASCII conversion buffer too small");
+}
+
+#  endif /* FLOATING_POINT */
+
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+/* Function to format a fixed point value in ASCII.
+ */
+void /* PRIVATE */
+png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,
+    png_size_t size, png_fixed_point fp)
+{
+   /* Require space for 10 decimal digits, a decimal point, a minus sign and a
+    * trailing \0, 13 characters:
+    */
+   if (size > 12)
+   {
+      png_uint_32 num;
+
+      /* Avoid overflow here on the minimum integer. */
+      if (fp < 0)
+         *ascii++ = 45, num = -fp;
+      else
+         num = fp;
+
+      if (num <= 0x80000000) /* else overflowed */
+      {
+         unsigned int ndigits = 0, first = 16 /* flag value */;
+         char digits[10];
+
+         while (num)
+         {
+            /* Split the low digit off num: */
+            unsigned int tmp = num/10;
+            num -= tmp*10;
+            digits[ndigits++] = (char)(48 + num);
+            /* Record the first non-zero digit, note that this is a number
+             * starting at 1, it's not actually the array index.
+             */
+            if (first == 16 && num > 0)
+               first = ndigits;
+            num = tmp;
+         }
+
+         if (ndigits > 0)
+         {
+            while (ndigits > 5) *ascii++ = digits[--ndigits];
+            /* The remaining digits are fractional digits, ndigits is '5' or
+             * smaller at this point.  It is certainly not zero.  Check for a
+             * non-zero fractional digit:
+             */
+            if (first <= 5)
+            {
+               unsigned int i;
+               *ascii++ = 46; /* decimal point */
+               /* ndigits may be <5 for small numbers, output leading zeros
+                * then ndigits digits to first:
+                */
+               i = 5;
+               while (ndigits < i) *ascii++ = 48, --i;
+               while (ndigits >= first) *ascii++ = digits[--ndigits];
+               /* Don't output the trailing zeros! */
+            }
+         }
+         else
+            *ascii++ = 48;
+
+         /* And null terminate the string: */
+         *ascii = 0;
+         return;
+      }
+   }
+
+   /* Here on buffer too small. */
+   png_error(png_ptr, "ASCII conversion buffer too small");
+}
+#   endif /* FIXED_POINT */
+#endif /* SCAL */
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
+   !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
+   (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \
+   defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \
+   (defined(PNG_sCAL_SUPPORTED) && \
+   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))
+png_fixed_point
+png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text)
+{
+   double r = floor(100000 * fp + .5);
+
+   if (r > 2147483647. || r < -2147483648.)
+      png_fixed_error(png_ptr, text);
+
+#  ifndef PNG_ERROR_TEXT_SUPPORTED
+   PNG_UNUSED(text)
+#  endif
+
+   return (png_fixed_point)r;
+}
+#endif
+
+#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_COLORSPACE_SUPPORTED) ||\
+    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
+/* muldiv functions */
+/* This API takes signed arguments and rounds the result to the nearest
+ * integer (or, for a fixed point number - the standard argument - to
+ * the nearest .00001).  Overflow and divide by zero are signalled in
+ * the result, a boolean - true on success, false on overflow.
+ */
+int
+png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
+    png_int_32 divisor)
+{
+   /* Return a * times / divisor, rounded. */
+   if (divisor != 0)
+   {
+      if (a == 0 || times == 0)
+      {
+         *res = 0;
+         return 1;
+      }
+      else
+      {
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+         double r = a;
+         r *= times;
+         r /= divisor;
+         r = floor(r+.5);
+
+         /* A png_fixed_point is a 32-bit integer. */
+         if (r <= 2147483647. && r >= -2147483648.)
+         {
+            *res = (png_fixed_point)r;
+            return 1;
+         }
+#else
+         int negative = 0;
+         png_uint_32 A, T, D;
+         png_uint_32 s16, s32, s00;
+
+         if (a < 0)
+            negative = 1, A = -a;
+         else
+            A = a;
+
+         if (times < 0)
+            negative = !negative, T = -times;
+         else
+            T = times;
+
+         if (divisor < 0)
+            negative = !negative, D = -divisor;
+         else
+            D = divisor;
+
+         /* Following can't overflow because the arguments only
+          * have 31 bits each, however the result may be 32 bits.
+          */
+         s16 = (A >> 16) * (T & 0xffff) +
+                           (A & 0xffff) * (T >> 16);
+         /* Can't overflow because the a*times bit is only 30
+          * bits at most.
+          */
+         s32 = (A >> 16) * (T >> 16) + (s16 >> 16);
+         s00 = (A & 0xffff) * (T & 0xffff);
+
+         s16 = (s16 & 0xffff) << 16;
+         s00 += s16;
+
+         if (s00 < s16)
+            ++s32; /* carry */
+
+         if (s32 < D) /* else overflow */
+         {
+            /* s32.s00 is now the 64-bit product, do a standard
+             * division, we know that s32 < D, so the maximum
+             * required shift is 31.
+             */
+            int bitshift = 32;
+            png_fixed_point result = 0; /* NOTE: signed */
+
+            while (--bitshift >= 0)
+            {
+               png_uint_32 d32, d00;
+
+               if (bitshift > 0)
+                  d32 = D >> (32-bitshift), d00 = D << bitshift;
+
+               else
+                  d32 = 0, d00 = D;
+
+               if (s32 > d32)
+               {
+                  if (s00 < d00) --s32; /* carry */
+                  s32 -= d32, s00 -= d00, result += 1<<bitshift;
+               }
+
+               else
+                  if (s32 == d32 && s00 >= d00)
+                     s32 = 0, s00 -= d00, result += 1<<bitshift;
+            }
+
+            /* Handle the rounding. */
+            if (s00 >= (D >> 1))
+               ++result;
+
+            if (negative != 0)
+               result = -result;
+
+            /* Check for overflow. */
+            if ((negative != 0 && result <= 0) ||
+                (negative == 0 && result >= 0))
+            {
+               *res = result;
+               return 1;
+            }
+         }
+#endif
+      }
+   }
+
+   return 0;
+}
+#endif /* READ_GAMMA || INCH_CONVERSIONS */
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
+/* The following is for when the caller doesn't much care about the
+ * result.
+ */
+png_fixed_point
+png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times,
+    png_int_32 divisor)
+{
+   png_fixed_point result;
+
+   if (png_muldiv(&result, a, times, divisor) != 0)
+      return result;
+
+   png_warning(png_ptr, "fixed point overflow ignored");
+   return 0;
+}
+#endif
+
+#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */
+/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
+png_fixed_point
+png_reciprocal(png_fixed_point a)
+{
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+   double r = floor(1E10/a+.5);
+
+   if (r <= 2147483647. && r >= -2147483648.)
+      return (png_fixed_point)r;
+#else
+   png_fixed_point res;
+
+   if (png_muldiv(&res, 100000, 100000, a) != 0)
+      return res;
+#endif
+
+   return 0; /* error/overflow */
+}
+
+/* This is the shared test on whether a gamma value is 'significant' - whether
+ * it is worth doing gamma correction.
+ */
+int /* PRIVATE */
+png_gamma_significant(png_fixed_point gamma_val)
+{
+   return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED ||
+       gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;
+}
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+#ifdef PNG_16BIT_SUPPORTED
+/* A local convenience routine. */
+static png_fixed_point
+png_product2(png_fixed_point a, png_fixed_point b)
+{
+   /* The required result is 1/a * 1/b; the following preserves accuracy. */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+   double r = a * 1E-5;
+   r *= b;
+   r = floor(r+.5);
+
+   if (r <= 2147483647. && r >= -2147483648.)
+      return (png_fixed_point)r;
+#else
+   png_fixed_point res;
+
+   if (png_muldiv(&res, a, b, 100000) != 0)
+      return res;
+#endif
+
+   return 0; /* overflow */
+}
+#endif /* 16BIT */
+
+/* The inverse of the above. */
+png_fixed_point
+png_reciprocal2(png_fixed_point a, png_fixed_point b)
+{
+   /* The required result is 1/a * 1/b; the following preserves accuracy. */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+   if (a != 0 && b != 0)
+   {
+      double r = 1E15/a;
+      r /= b;
+      r = floor(r+.5);
+
+      if (r <= 2147483647. && r >= -2147483648.)
+         return (png_fixed_point)r;
+   }
+#else
+   /* This may overflow because the range of png_fixed_point isn't symmetric,
+    * but this API is only used for the product of file and screen gamma so it
+    * doesn't matter that the smallest number it can produce is 1/21474, not
+    * 1/100000
+    */
+   png_fixed_point res = png_product2(a, b);
+
+   if (res != 0)
+      return png_reciprocal(res);
+#endif
+
+   return 0; /* overflow */
+}
+#endif /* READ_GAMMA */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */
+#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
+/* Fixed point gamma.
+ *
+ * The code to calculate the tables used below can be found in the shell script
+ * contrib/tools/intgamma.sh
+ *
+ * To calculate gamma this code implements fast log() and exp() calls using only
+ * fixed point arithmetic.  This code has sufficient precision for either 8-bit
+ * or 16-bit sample values.
+ *
+ * The tables used here were calculated using simple 'bc' programs, but C double
+ * precision floating point arithmetic would work fine.
+ *
+ * 8-bit log table
+ *   This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
+ *   255, so it's the base 2 logarithm of a normalized 8-bit floating point
+ *   mantissa.  The numbers are 32-bit fractions.
+ */
+static const png_uint_32
+png_8bit_l2[128] =
+{
+   4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
+   3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
+   3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
+   3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,
+   3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,
+   2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,
+   2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,
+   2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,
+   2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,
+   2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,
+   1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,
+   1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,
+   1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,
+   1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,
+   1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,
+   971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,
+   803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,
+   639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,
+   479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,
+   324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,
+   172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
+   24347096U, 0U
+
+#if 0
+   /* The following are the values for 16-bit tables - these work fine for the
+    * 8-bit conversions but produce very slightly larger errors in the 16-bit
+    * log (about 1.2 as opposed to 0.7 absolute error in the final value).  To
+    * use these all the shifts below must be adjusted appropriately.
+    */
+   65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
+   57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
+   50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,
+   43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,
+   37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,
+   31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,
+   25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,
+   20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,
+   15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,
+   10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,
+   6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,
+   1119, 744, 372
+#endif
+};
+
+static png_int_32
+png_log8bit(unsigned int x)
+{
+   unsigned int lg2 = 0;
+   /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,
+    * because the log is actually negate that means adding 1.  The final
+    * returned value thus has the range 0 (for 255 input) to 7.994 (for 1
+    * input), return -1 for the overflow (log 0) case, - so the result is
+    * always at most 19 bits.
+    */
+   if ((x &= 0xff) == 0)
+      return -1;
+
+   if ((x & 0xf0) == 0)
+      lg2  = 4, x <<= 4;
+
+   if ((x & 0xc0) == 0)
+      lg2 += 2, x <<= 2;
+
+   if ((x & 0x80) == 0)
+      lg2 += 1, x <<= 1;
+
+   /* result is at most 19 bits, so this cast is safe: */
+   return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16));
+}
+
+/* The above gives exact (to 16 binary places) log2 values for 8-bit images,
+ * for 16-bit images we use the most significant 8 bits of the 16-bit value to
+ * get an approximation then multiply the approximation by a correction factor
+ * determined by the remaining up to 8 bits.  This requires an additional step
+ * in the 16-bit case.
+ *
+ * We want log2(value/65535), we have log2(v'/255), where:
+ *
+ *    value = v' * 256 + v''
+ *          = v' * f
+ *
+ * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
+ * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
+ * than 258.  The final factor also needs to correct for the fact that our 8-bit
+ * value is scaled by 255, whereas the 16-bit values must be scaled by 65535.
+ *
+ * This gives a final formula using a calculated value 'x' which is value/v' and
+ * scaling by 65536 to match the above table:
+ *
+ *   log2(x/257) * 65536
+ *
+ * Since these numbers are so close to '1' we can use simple linear
+ * interpolation between the two end values 256/257 (result -368.61) and 258/257
+ * (result 367.179).  The values used below are scaled by a further 64 to give
+ * 16-bit precision in the interpolation:
+ *
+ * Start (256): -23591
+ * Zero  (257):      0
+ * End   (258):  23499
+ */
+#ifdef PNG_16BIT_SUPPORTED
+static png_int_32
+png_log16bit(png_uint_32 x)
+{
+   unsigned int lg2 = 0;
+
+   /* As above, but now the input has 16 bits. */
+   if ((x &= 0xffff) == 0)
+      return -1;
+
+   if ((x & 0xff00) == 0)
+      lg2  = 8, x <<= 8;
+
+   if ((x & 0xf000) == 0)
+      lg2 += 4, x <<= 4;
+
+   if ((x & 0xc000) == 0)
+      lg2 += 2, x <<= 2;
+
+   if ((x & 0x8000) == 0)
+      lg2 += 1, x <<= 1;
+
+   /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional
+    * value.
+    */
+   lg2 <<= 28;
+   lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4;
+
+   /* Now we need to interpolate the factor, this requires a division by the top
+    * 8 bits.  Do this with maximum precision.
+    */
+   x = ((x << 16) + (x >> 9)) / (x >> 8);
+
+   /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,
+    * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly
+    * 16 bits to interpolate to get the low bits of the result.  Round the
+    * answer.  Note that the end point values are scaled by 64 to retain overall
+    * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust
+    * the overall scaling by 6-12.  Round at every step.
+    */
+   x -= 1U << 24;
+
+   if (x <= 65536U) /* <= '257' */
+      lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12);
+
+   else
+      lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);
+
+   /* Safe, because the result can't have more than 20 bits: */
+   return (png_int_32)((lg2 + 2048) >> 12);
+}
+#endif /* 16BIT */
+
+/* The 'exp()' case must invert the above, taking a 20-bit fixed point
+ * logarithmic value and returning a 16 or 8-bit number as appropriate.  In
+ * each case only the low 16 bits are relevant - the fraction - since the
+ * integer bits (the top 4) simply determine a shift.
+ *
+ * The worst case is the 16-bit distinction between 65535 and 65534. This
+ * requires perhaps spurious accuracy in the decoding of the logarithm to
+ * distinguish log2(65535/65534.5) - 10^-5 or 17 bits.  There is little chance
+ * of getting this accuracy in practice.
+ *
+ * To deal with this the following exp() function works out the exponent of the
+ * frational part of the logarithm by using an accurate 32-bit value from the
+ * top four fractional bits then multiplying in the remaining bits.
+ */
+static const png_uint_32
+png_32bit_exp[16] =
+{
+   /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */
+   4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
+   3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
+   2553802834U, 2445529972U, 2341847524U, 2242560872U
+};
+
+/* Adjustment table; provided to explain the numbers in the code below. */
+#if 0
+for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
+   11 44937.64284865548751208448
+   10 45180.98734845585101160448
+    9 45303.31936980687359311872
+    8 45364.65110595323018870784
+    7 45395.35850361789624614912
+    6 45410.72259715102037508096
+    5 45418.40724413220722311168
+    4 45422.25021786898173001728
+    3 45424.17186732298419044352
+    2 45425.13273269940811464704
+    1 45425.61317555035558641664
+    0 45425.85339951654943850496
+#endif
+
+static png_uint_32
+png_exp(png_fixed_point x)
+{
+   if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
+   {
+      /* Obtain a 4-bit approximation */
+      png_uint_32 e = png_32bit_exp[(x >> 12) & 0x0f];
+
+      /* Incorporate the low 12 bits - these decrease the returned value by
+       * multiplying by a number less than 1 if the bit is set.  The multiplier
+       * is determined by the above table and the shift. Notice that the values
+       * converge on 45426 and this is used to allow linear interpolation of the
+       * low bits.
+       */
+      if (x & 0x800)
+         e -= (((e >> 16) * 44938U) +  16U) >> 5;
+
+      if (x & 0x400)
+         e -= (((e >> 16) * 45181U) +  32U) >> 6;
+
+      if (x & 0x200)
+         e -= (((e >> 16) * 45303U) +  64U) >> 7;
+
+      if (x & 0x100)
+         e -= (((e >> 16) * 45365U) + 128U) >> 8;
+
+      if (x & 0x080)
+         e -= (((e >> 16) * 45395U) + 256U) >> 9;
+
+      if (x & 0x040)
+         e -= (((e >> 16) * 45410U) + 512U) >> 10;
+
+      /* And handle the low 6 bits in a single block. */
+      e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;
+
+      /* Handle the upper bits of x. */
+      e >>= x >> 16;
+      return e;
+   }
+
+   /* Check for overflow */
+   if (x <= 0)
+      return png_32bit_exp[0];
+
+   /* Else underflow */
+   return 0;
+}
+
+static png_byte
+png_exp8bit(png_fixed_point lg2)
+{
+   /* Get a 32-bit value: */
+   png_uint_32 x = png_exp(lg2);
+
+   /* Convert the 32-bit value to 0..255 by multiplying by 256-1. Note that the
+    * second, rounding, step can't overflow because of the first, subtraction,
+    * step.
+    */
+   x -= x >> 8;
+   return (png_byte)(((x + 0x7fffffU) >> 24) & 0xff);
+}
+
+#ifdef PNG_16BIT_SUPPORTED
+static png_uint_16
+png_exp16bit(png_fixed_point lg2)
+{
+   /* Get a 32-bit value: */
+   png_uint_32 x = png_exp(lg2);
+
+   /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */
+   x -= x >> 16;
+   return (png_uint_16)((x + 32767U) >> 16);
+}
+#endif /* 16BIT */
+#endif /* FLOATING_ARITHMETIC */
+
+png_byte
+png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)
+{
+   if (value > 0 && value < 255)
+   {
+#     ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+         /* 'value' is unsigned, ANSI-C90 requires the compiler to correctly
+          * convert this to a floating point value.  This includes values that
+          * would overflow if 'value' were to be converted to 'int'.
+          *
+          * Apparently GCC, however, does an intermediate conversion to (int)
+          * on some (ARM) but not all (x86) platforms, possibly because of
+          * hardware FP limitations.  (E.g. if the hardware conversion always
+          * assumes the integer register contains a signed value.)  This results
+          * in ANSI-C undefined behavior for large values.
+          *
+          * Other implementations on the same machine might actually be ANSI-C90
+          * conformant and therefore compile spurious extra code for the large
+          * values.
+          *
+          * We can be reasonably sure that an unsigned to float conversion
+          * won't be faster than an int to float one.  Therefore this code
+          * assumes responsibility for the undefined behavior, which it knows
+          * can't happen because of the check above.
+          *
+          * Note the argument to this routine is an (unsigned int) because, on
+          * 16-bit platforms, it is assigned a value which might be out of
+          * range for an (int); that would result in undefined behavior in the
+          * caller if the *argument* ('value') were to be declared (int).
+          */
+         double r = floor(255*pow((int)/*SAFE*/value/255.,gamma_val*.00001)+.5);
+         return (png_byte)r;
+#     else
+         png_int_32 lg2 = png_log8bit(value);
+         png_fixed_point res;
+
+         if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0)
+            return png_exp8bit(res);
+
+         /* Overflow. */
+         value = 0;
+#     endif
+   }
+
+   return (png_byte)(value & 0xff);
+}
+
+#ifdef PNG_16BIT_SUPPORTED
+png_uint_16
+png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)
+{
+   if (value > 0 && value < 65535)
+   {
+#     ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+         /* The same (unsigned int)->(double) constraints apply here as above,
+          * however in this case the (unsigned int) to (int) conversion can
+          * overflow on an ANSI-C90 compliant system so the cast needs to ensure
+          * that this is not possible.
+          */
+         double r = floor(65535*pow((png_int_32)value/65535.,
+                     gamma_val*.00001)+.5);
+         return (png_uint_16)r;
+#     else
+         png_int_32 lg2 = png_log16bit(value);
+         png_fixed_point res;
+
+         if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1) != 0)
+            return png_exp16bit(res);
+
+         /* Overflow. */
+         value = 0;
+#     endif
+   }
+
+   return (png_uint_16)value;
+}
+#endif /* 16BIT */
+
+/* This does the right thing based on the bit_depth field of the
+ * png_struct, interpreting values as 8-bit or 16-bit.  While the result
+ * is nominally a 16-bit value if bit depth is 8 then the result is
+ * 8-bit (as are the arguments.)
+ */
+png_uint_16 /* PRIVATE */
+png_gamma_correct(png_structrp png_ptr, unsigned int value,
+    png_fixed_point gamma_val)
+{
+   if (png_ptr->bit_depth == 8)
+      return png_gamma_8bit_correct(value, gamma_val);
+
+#ifdef PNG_16BIT_SUPPORTED
+   else
+      return png_gamma_16bit_correct(value, gamma_val);
+#else
+      /* should not reach this */
+      return 0;
+#endif /* 16BIT */
+}
+
+#ifdef PNG_16BIT_SUPPORTED
+/* Internal function to build a single 16-bit table - the table consists of
+ * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount
+ * to shift the input values right (or 16-number_of_signifiant_bits).
+ *
+ * The caller is responsible for ensuring that the table gets cleaned up on
+ * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument
+ * should be somewhere that will be cleaned.
+ */
+static void
+png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable,
+   PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)
+{
+   /* Various values derived from 'shift': */
+   PNG_CONST unsigned int num = 1U << (8U - shift);
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+   /* CSE the division and work round wacky GCC warnings (see the comments
+    * in png_gamma_8bit_correct for where these come from.)
+    */
+   PNG_CONST double fmax = 1./(((png_int_32)1 << (16U - shift))-1);
+#endif
+   PNG_CONST unsigned int max = (1U << (16U - shift))-1U;
+   PNG_CONST unsigned int max_by_2 = 1U << (15U-shift);
+   unsigned int i;
+
+   png_uint_16pp table = *ptable =
+       (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p)));
+
+   for (i = 0; i < num; i++)
+   {
+      png_uint_16p sub_table = table[i] =
+          (png_uint_16p)png_malloc(png_ptr, 256 * (sizeof (png_uint_16)));
+
+      /* The 'threshold' test is repeated here because it can arise for one of
+       * the 16-bit tables even if the others don't hit it.
+       */
+      if (png_gamma_significant(gamma_val) != 0)
+      {
+         /* The old code would overflow at the end and this would cause the
+          * 'pow' function to return a result >1, resulting in an
+          * arithmetic error.  This code follows the spec exactly; ig is
+          * the recovered input sample, it always has 8-16 bits.
+          *
+          * We want input * 65535/max, rounded, the arithmetic fits in 32
+          * bits (unsigned) so long as max <= 32767.
+          */
+         unsigned int j;
+         for (j = 0; j < 256; j++)
+         {
+            png_uint_32 ig = (j << (8-shift)) + i;
+#           ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+               /* Inline the 'max' scaling operation: */
+               /* See png_gamma_8bit_correct for why the cast to (int) is
+                * required here.
+                */
+               double d = floor(65535.*pow(ig*fmax, gamma_val*.00001)+.5);
+               sub_table[j] = (png_uint_16)d;
+#           else
+               if (shift != 0)
+                  ig = (ig * 65535U + max_by_2)/max;
+
+               sub_table[j] = png_gamma_16bit_correct(ig, gamma_val);
+#           endif
+         }
+      }
+      else
+      {
+         /* We must still build a table, but do it the fast way. */
+         unsigned int j;
+
+         for (j = 0; j < 256; j++)
+         {
+            png_uint_32 ig = (j << (8-shift)) + i;
+
+            if (shift != 0)
+               ig = (ig * 65535U + max_by_2)/max;
+
+            sub_table[j] = (png_uint_16)ig;
+         }
+      }
+   }
+}
+
+/* NOTE: this function expects the *inverse* of the overall gamma transformation
+ * required.
+ */
+static void
+png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable,
+   PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)
+{
+   PNG_CONST unsigned int num = 1U << (8U - shift);
+   PNG_CONST unsigned int max = (1U << (16U - shift))-1U;
+   unsigned int i;
+   png_uint_32 last;
+
+   png_uint_16pp table = *ptable =
+       (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p)));
+
+   /* 'num' is the number of tables and also the number of low bits of low
+    * bits of the input 16-bit value used to select a table.  Each table is
+    * itself indexed by the high 8 bits of the value.
+    */
+   for (i = 0; i < num; i++)
+      table[i] = (png_uint_16p)png_malloc(png_ptr,
+          256 * (sizeof (png_uint_16)));
+
+   /* 'gamma_val' is set to the reciprocal of the value calculated above, so
+    * pow(out,g) is an *input* value.  'last' is the last input value set.
+    *
+    * In the loop 'i' is used to find output values.  Since the output is
+    * 8-bit there are only 256 possible values.  The tables are set up to
+    * select the closest possible output value for each input by finding
+    * the input value at the boundary between each pair of output values
+    * and filling the table up to that boundary with the lower output
+    * value.
+    *
+    * The boundary values are 0.5,1.5..253.5,254.5.  Since these are 9-bit
+    * values the code below uses a 16-bit value in i; the values start at
+    * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last
+    * entries are filled with 255).  Start i at 128 and fill all 'last'
+    * table entries <= 'max'
+    */
+   last = 0;
+   for (i = 0; i < 255; ++i) /* 8-bit output value */
+   {
+      /* Find the corresponding maximum input value */
+      png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */
+
+      /* Find the boundary value in 16 bits: */
+      png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val);
+
+      /* Adjust (round) to (16-shift) bits: */
+      bound = (bound * max + 32768U)/65535U + 1U;
+
+      while (last < bound)
+      {
+         table[last & (0xffU >> shift)][last >> (8U - shift)] = out;
+         last++;
+      }
+   }
+
+   /* And fill in the final entries. */
+   while (last < (num << 8))
+   {
+      table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U;
+      last++;
+   }
+}
+#endif /* 16BIT */
+
+/* Build a single 8-bit table: same as the 16-bit case but much simpler (and
+ * typically much faster).  Note that libpng currently does no sBIT processing
+ * (apparently contrary to the spec) so a 256-entry table is always generated.
+ */
+static void
+png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable,
+   PNG_CONST png_fixed_point gamma_val)
+{
+   unsigned int i;
+   png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256);
+
+   if (png_gamma_significant(gamma_val) != 0)
+      for (i=0; i<256; i++)
+         table[i] = png_gamma_8bit_correct(i, gamma_val);
+
+   else
+      for (i=0; i<256; ++i)
+         table[i] = (png_byte)(i & 0xff);
+}
+
+/* Used from png_read_destroy and below to release the memory used by the gamma
+ * tables.
+ */
+void /* PRIVATE */
+png_destroy_gamma_table(png_structrp png_ptr)
+{
+   png_free(png_ptr, png_ptr->gamma_table);
+   png_ptr->gamma_table = NULL;
+
+#ifdef PNG_16BIT_SUPPORTED
+   if (png_ptr->gamma_16_table != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_table[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_table);
+   png_ptr->gamma_16_table = NULL;
+   }
+#endif /* 16BIT */
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_from_1);
+   png_ptr->gamma_from_1 = NULL;
+   png_free(png_ptr, png_ptr->gamma_to_1);
+   png_ptr->gamma_to_1 = NULL;
+
+#ifdef PNG_16BIT_SUPPORTED
+   if (png_ptr->gamma_16_from_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_from_1);
+   png_ptr->gamma_16_from_1 = NULL;
+   }
+   if (png_ptr->gamma_16_to_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_to_1);
+   png_ptr->gamma_16_to_1 = NULL;
+   }
+#endif /* 16BIT */
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
+}
+
+/* We build the 8- or 16-bit gamma tables here.  Note that for 16-bit
+ * tables, we don't make a full table if we are reducing to 8-bit in
+ * the future.  Note also how the gamma_16 tables are segmented so that
+ * we don't need to allocate > 64K chunks for a full 16-bit table.
+ */
+void /* PRIVATE */
+png_build_gamma_table(png_structrp png_ptr, int bit_depth)
+{
+  png_debug(1, "in png_build_gamma_table");
+
+  /* Remove any existing table; this copes with multiple calls to
+   * png_read_update_info.  The warning is because building the gamma tables
+   * multiple times is a performance hit - it's harmless but the ability to call
+   * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible
+   * to warn if the app introduces such a hit.
+   */
+  if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL)
+  {
+    png_warning(png_ptr, "gamma table being rebuilt");
+    png_destroy_gamma_table(png_ptr);
+  }
+
+  if (bit_depth <= 8)
+  {
+     png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
+         png_ptr->screen_gamma > 0 ?  png_reciprocal2(png_ptr->colorspace.gamma,
+         png_ptr->screen_gamma) : PNG_FP_1);
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+     if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
+     {
+        png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
+            png_reciprocal(png_ptr->colorspace.gamma));
+
+        png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,
+            png_ptr->screen_gamma > 0 ?  png_reciprocal(png_ptr->screen_gamma) :
+            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
+     }
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
+  }
+#ifdef PNG_16BIT_SUPPORTED
+  else
+  {
+     png_byte shift, sig_bit;
+
+     if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+     {
+        sig_bit = png_ptr->sig_bit.red;
+
+        if (png_ptr->sig_bit.green > sig_bit)
+           sig_bit = png_ptr->sig_bit.green;
+
+        if (png_ptr->sig_bit.blue > sig_bit)
+           sig_bit = png_ptr->sig_bit.blue;
+     }
+     else
+        sig_bit = png_ptr->sig_bit.gray;
+
+     /* 16-bit gamma code uses this equation:
+      *
+      *   ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]
+      *
+      * Where 'iv' is the input color value and 'ov' is the output value -
+      * pow(iv, gamma).
+      *
+      * Thus the gamma table consists of up to 256 256-entry tables.  The table
+      * is selected by the (8-gamma_shift) most significant of the low 8 bits of
+      * the color value then indexed by the upper 8 bits:
+      *
+      *   table[low bits][high 8 bits]
+      *
+      * So the table 'n' corresponds to all those 'iv' of:
+      *
+      *   <all high 8-bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
+      *
+      */
+     if (sig_bit > 0 && sig_bit < 16U)
+        /* shift == insignificant bits */
+        shift = (png_byte)((16U - sig_bit) & 0xff);
+
+     else
+        shift = 0; /* keep all 16 bits */
+
+     if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
+     {
+        /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
+         * the significant bits in the *input* when the output will
+         * eventually be 8 bits.  By default it is 11.
+         */
+        if (shift < (16U - PNG_MAX_GAMMA_8))
+           shift = (16U - PNG_MAX_GAMMA_8);
+     }
+
+     if (shift > 8U)
+        shift = 8U; /* Guarantees at least one table! */
+
+     png_ptr->gamma_shift = shift;
+
+     /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now
+      * PNG_COMPOSE).  This effectively smashed the background calculation for
+      * 16-bit output because the 8-bit table assumes the result will be reduced
+      * to 8 bits.
+      */
+     if ((png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) != 0)
+         png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
+         png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma,
+         png_ptr->screen_gamma) : PNG_FP_1);
+
+     else
+         png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
+         png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,
+         png_ptr->screen_gamma) : PNG_FP_1);
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+     if ((png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) != 0)
+     {
+        png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
+            png_reciprocal(png_ptr->colorspace.gamma));
+
+        /* Notice that the '16 from 1' table should be full precision, however
+         * the lookup on this table still uses gamma_shift, so it can't be.
+         * TODO: fix this.
+         */
+        png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,
+            png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
+            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
+     }
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
+  }
+#endif /* 16BIT */
+}
+#endif /* READ_GAMMA */
+
+/* HARDWARE OR SOFTWARE OPTION SUPPORT */
+#ifdef PNG_SET_OPTION_SUPPORTED
+int PNGAPI
+png_set_option(png_structrp png_ptr, int option, int onoff)
+{
+   if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT &&
+      (option & 1) == 0)
+   {
+      int mask = 3 << option;
+      int setting = (2 + (onoff != 0)) << option;
+      int current = png_ptr->options;
+
+      png_ptr->options = (png_byte)(((current & ~mask) | setting) & 0xff);
+
+      return (current & mask) >> option;
+   }
+
+   return PNG_OPTION_INVALID;
+}
+#endif
+
+/* sRGB support */
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
+   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+/* sRGB conversion tables; these are machine generated with the code in
+ * contrib/tools/makesRGB.c.  The actual sRGB transfer curve defined in the
+ * specification (see the article at http://en.wikipedia.org/wiki/SRGB)
+ * is used, not the gamma=1/2.2 approximation use elsewhere in libpng.
+ * The sRGB to linear table is exact (to the nearest 16-bit linear fraction).
+ * The inverse (linear to sRGB) table has accuracies as follows:
+ *
+ * For all possible (255*65535+1) input values:
+ *
+ *    error: -0.515566 - 0.625971, 79441 (0.475369%) of readings inexact
+ *
+ * For the input values corresponding to the 65536 16-bit values:
+ *
+ *    error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact
+ *
+ * In all cases the inexact readings are only off by one.
+ */
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+/* The convert-to-sRGB table is only currently required for read. */
+const png_uint_16 png_sRGB_table[256] =
+{
+   0,20,40,60,80,99,119,139,
+   159,179,199,219,241,264,288,313,
+   340,367,396,427,458,491,526,562,
+   599,637,677,718,761,805,851,898,
+   947,997,1048,1101,1156,1212,1270,1330,
+   1391,1453,1517,1583,1651,1720,1790,1863,
+   1937,2013,2090,2170,2250,2333,2418,2504,
+   2592,2681,2773,2866,2961,3058,3157,3258,
+   3360,3464,3570,3678,3788,3900,4014,4129,
+   4247,4366,4488,4611,4736,4864,4993,5124,
+   5257,5392,5530,5669,5810,5953,6099,6246,
+   6395,6547,6700,6856,7014,7174,7335,7500,
+   7666,7834,8004,8177,8352,8528,8708,8889,
+   9072,9258,9445,9635,9828,10022,10219,10417,
+   10619,10822,11028,11235,11446,11658,11873,12090,
+   12309,12530,12754,12980,13209,13440,13673,13909,
+   14146,14387,14629,14874,15122,15371,15623,15878,
+   16135,16394,16656,16920,17187,17456,17727,18001,
+   18277,18556,18837,19121,19407,19696,19987,20281,
+   20577,20876,21177,21481,21787,22096,22407,22721,
+   23038,23357,23678,24002,24329,24658,24990,25325,
+   25662,26001,26344,26688,27036,27386,27739,28094,
+   28452,28813,29176,29542,29911,30282,30656,31033,
+   31412,31794,32179,32567,32957,33350,33745,34143,
+   34544,34948,35355,35764,36176,36591,37008,37429,
+   37852,38278,38706,39138,39572,40009,40449,40891,
+   41337,41785,42236,42690,43147,43606,44069,44534,
+   45002,45473,45947,46423,46903,47385,47871,48359,
+   48850,49344,49841,50341,50844,51349,51858,52369,
+   52884,53401,53921,54445,54971,55500,56032,56567,
+   57105,57646,58190,58737,59287,59840,60396,60955,
+   61517,62082,62650,63221,63795,64372,64952,65535
+};
+#endif /* SIMPLIFIED_READ */
+
+/* The base/delta tables are required for both read and write (but currently
+ * only the simplified versions.)
+ */
+const png_uint_16 png_sRGB_base[512] =
+{
+   128,1782,3383,4644,5675,6564,7357,8074,
+   8732,9346,9921,10463,10977,11466,11935,12384,
+   12816,13233,13634,14024,14402,14769,15125,15473,
+   15812,16142,16466,16781,17090,17393,17690,17981,
+   18266,18546,18822,19093,19359,19621,19879,20133,
+   20383,20630,20873,21113,21349,21583,21813,22041,
+   22265,22487,22707,22923,23138,23350,23559,23767,
+   23972,24175,24376,24575,24772,24967,25160,25352,
+   25542,25730,25916,26101,26284,26465,26645,26823,
+   27000,27176,27350,27523,27695,27865,28034,28201,
+   28368,28533,28697,28860,29021,29182,29341,29500,
+   29657,29813,29969,30123,30276,30429,30580,30730,
+   30880,31028,31176,31323,31469,31614,31758,31902,
+   32045,32186,32327,32468,32607,32746,32884,33021,
+   33158,33294,33429,33564,33697,33831,33963,34095,
+   34226,34357,34486,34616,34744,34873,35000,35127,
+   35253,35379,35504,35629,35753,35876,35999,36122,
+   36244,36365,36486,36606,36726,36845,36964,37083,
+   37201,37318,37435,37551,37668,37783,37898,38013,
+   38127,38241,38354,38467,38580,38692,38803,38915,
+   39026,39136,39246,39356,39465,39574,39682,39790,
+   39898,40005,40112,40219,40325,40431,40537,40642,
+   40747,40851,40955,41059,41163,41266,41369,41471,
+   41573,41675,41777,41878,41979,42079,42179,42279,
+   42379,42478,42577,42676,42775,42873,42971,43068,
+   43165,43262,43359,43456,43552,43648,43743,43839,
+   43934,44028,44123,44217,44311,44405,44499,44592,
+   44685,44778,44870,44962,45054,45146,45238,45329,
+   45420,45511,45601,45692,45782,45872,45961,46051,
+   46140,46229,46318,46406,46494,46583,46670,46758,
+   46846,46933,47020,47107,47193,47280,47366,47452,
+   47538,47623,47709,47794,47879,47964,48048,48133,
+   48217,48301,48385,48468,48552,48635,48718,48801,
+   48884,48966,49048,49131,49213,49294,49376,49458,
+   49539,49620,49701,49782,49862,49943,50023,50103,
+   50183,50263,50342,50422,50501,50580,50659,50738,
+   50816,50895,50973,51051,51129,51207,51285,51362,
+   51439,51517,51594,51671,51747,51824,51900,51977,
+   52053,52129,52205,52280,52356,52432,52507,52582,
+   52657,52732,52807,52881,52956,53030,53104,53178,
+   53252,53326,53400,53473,53546,53620,53693,53766,
+   53839,53911,53984,54056,54129,54201,54273,54345,
+   54417,54489,54560,54632,54703,54774,54845,54916,
+   54987,55058,55129,55199,55269,55340,55410,55480,
+   55550,55620,55689,55759,55828,55898,55967,56036,
+   56105,56174,56243,56311,56380,56448,56517,56585,
+   56653,56721,56789,56857,56924,56992,57059,57127,
+   57194,57261,57328,57395,57462,57529,57595,57662,
+   57728,57795,57861,57927,57993,58059,58125,58191,
+   58256,58322,58387,58453,58518,58583,58648,58713,
+   58778,58843,58908,58972,59037,59101,59165,59230,
+   59294,59358,59422,59486,59549,59613,59677,59740,
+   59804,59867,59930,59993,60056,60119,60182,60245,
+   60308,60370,60433,60495,60558,60620,60682,60744,
+   60806,60868,60930,60992,61054,61115,61177,61238,
+   61300,61361,61422,61483,61544,61605,61666,61727,
+   61788,61848,61909,61969,62030,62090,62150,62211,
+   62271,62331,62391,62450,62510,62570,62630,62689,
+   62749,62808,62867,62927,62986,63045,63104,63163,
+   63222,63281,63340,63398,63457,63515,63574,63632,
+   63691,63749,63807,63865,63923,63981,64039,64097,
+   64155,64212,64270,64328,64385,64443,64500,64557,
+   64614,64672,64729,64786,64843,64900,64956,65013,
+   65070,65126,65183,65239,65296,65352,65409,65465
+};
+
+const png_byte png_sRGB_delta[512] =
+{
+   207,201,158,129,113,100,90,82,77,72,68,64,61,59,56,54,
+   52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36,
+   35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28,
+   28,27,27,27,27,26,26,26,25,25,25,25,24,24,24,24,
+   23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21,
+   21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19,
+   19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17,
+   17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16,
+   16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15,
+   15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14,
+   14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13,
+   13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12,
+   12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
+   12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11,
+   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+   11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
+   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
+   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
+   10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+   9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+   8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+#endif /* SIMPLIFIED READ/WRITE sRGB support */
+
+/* SIMPLIFIED READ/WRITE SUPPORT */
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
+   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+static int
+png_image_free_function(png_voidp argument)
+{
+   png_imagep image = png_voidcast(png_imagep, argument);
+   png_controlp cp = image->opaque;
+   png_control c;
+
+   /* Double check that we have a png_ptr - it should be impossible to get here
+    * without one.
+    */
+   if (cp->png_ptr == NULL)
+      return 0;
+
+   /* First free any data held in the control structure. */
+#  ifdef PNG_STDIO_SUPPORTED
+      if (cp->owned_file != 0)
+      {
+         FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr);
+         cp->owned_file = 0;
+
+         /* Ignore errors here. */
+         if (fp != NULL)
+         {
+            cp->png_ptr->io_ptr = NULL;
+            (void)fclose(fp);
+         }
+      }
+#  endif
+
+   /* Copy the control structure so that the original, allocated, version can be
+    * safely freed.  Notice that a png_error here stops the remainder of the
+    * cleanup, but this is probably fine because that would indicate bad memory
+    * problems anyway.
+    */
+   c = *cp;
+   image->opaque = &c;
+   png_free(c.png_ptr, cp);
+
+   /* Then the structures, calling the correct API. */
+   if (c.for_write != 0)
+   {
+#     ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+         png_destroy_write_struct(&c.png_ptr, &c.info_ptr);
+#     else
+         png_error(c.png_ptr, "simplified write not supported");
+#     endif
+   }
+   else
+   {
+#     ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+         png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL);
+#     else
+         png_error(c.png_ptr, "simplified read not supported");
+#     endif
+   }
+
+   /* Success. */
+   return 1;
+}
+
+void PNGAPI
+png_image_free(png_imagep image)
+{
+   /* Safely call the real function, but only if doing so is safe at this point
+    * (if not inside an error handling context).  Otherwise assume
+    * png_safe_execute will call this API after the return.
+    */
+   if (image != NULL && image->opaque != NULL &&
+      image->opaque->error_buf == NULL)
+   {
+      /* Ignore errors here: */
+      (void)png_safe_execute(image, png_image_free_function, image);
+      image->opaque = NULL;
+   }
+}
+
+int /* PRIVATE */
+png_image_error(png_imagep image, png_const_charp error_message)
+{
+   /* Utility to log an error. */
+   png_safecat(image->message, (sizeof image->message), 0, error_message);
+   image->warning_or_error |= PNG_IMAGE_ERROR;
+   png_image_free(image);
+   return 0;
+}
+
+#endif /* SIMPLIFIED READ/WRITE */
+#endif /* READ || WRITE */
diff --git a/third_party/lpng_v163/png.h b/third_party/libpng/png.h
similarity index 78%
rename from third_party/lpng_v163/png.h
rename to third_party/libpng/png.h
index 538b561..4d03dfc 100644
--- a/third_party/lpng_v163/png.h
+++ b/third_party/libpng/png.h
@@ -1,8 +1,9 @@
 
 /* png.h - header file for PNG reference library
  *
- * libpng version 1.6.3 - July 18, 2013
- * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * libpng version 1.6.20, December 3, 2015
+ *
+ * Copyright (c) 1998-2015 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.)
  *
@@ -10,17 +11,137 @@
  *
  * Authors and maintainers:
  *   libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
- *   libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
- *   libpng versions 0.97, January 1998, through 1.6.3 - July 18, 2013: Glenn
+ *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
+ *   libpng versions 0.97, January 1998, through 1.6.20, December 3, 2015:
+ *     Glenn Randers-Pehrson.
  *   See also "Contributing Authors", below.
+ */
+
+/*
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
  *
- * Note about libpng version numbers:
+ * If you modify libpng you may insert additional notices immediately following
+ * this sentence.
  *
- *   Due to various miscommunications, unforeseen code incompatibilities
- *   and occasional factors outside the authors' control, version numbering
- *   on the library has not always been consistent and straightforward.
- *   The following table summarizes matters since version 0.89c, which was
- *   the first widely used release:
+ * This code is released under the libpng license.
+ *
+ * libpng versions 1.0.7, July 1, 2000, through 1.6.20, December 3, 2015, are
+ * Copyright (c) 2000-2002, 2004, 2006-2015 Glenn Randers-Pehrson, are
+ * derived from libpng-1.0.6, and are distributed according to the same
+ * disclaimer and license as libpng-1.0.6 with the following individuals
+ * added to the list of Contributing Authors:
+ *
+ *    Simon-Pierre Cadieux
+ *    Eric S. Raymond
+ *    Mans Rullgard
+ *    Cosmin Truta
+ *    Gilles Vollant
+ *    James Yu
+ *
+ * and with the following additions to the disclaimer:
+ *
+ *    There is no warranty against interference with your enjoyment of the
+ *    library or against infringement.  There is no warranty that our
+ *    efforts or the library will fulfill any of your particular purposes
+ *    or needs.  This library is provided with all faults, and the entire
+ *    risk of satisfactory quality, performance, accuracy, and effort is with
+ *    the user.
+ *
+ * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+ * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
+ * libpng-0.96, and are distributed according to the same disclaimer and
+ * license as libpng-0.96, with the following individuals added to the list
+ * of Contributing Authors:
+ *
+ *    Tom Lane
+ *    Glenn Randers-Pehrson
+ *    Willem van Schaik
+ *
+ * libpng versions 0.89, June 1996, through 0.96, May 1997, are
+ * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
+ * and are distributed according to the same disclaimer and license as
+ * libpng-0.88, with the following individuals added to the list of
+ * Contributing Authors:
+ *
+ *    John Bowler
+ *    Kevin Bracey
+ *    Sam Bushell
+ *    Magnus Holmgren
+ *    Greg Roelofs
+ *    Tom Tanner
+ *
+ * libpng versions 0.5, May 1995, through 0.88, January 1996, are
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+ *
+ * For the purposes of this copyright and license, "Contributing Authors"
+ * is defined as the following set of individuals:
+ *
+ *    Andreas Dilger
+ *    Dave Martindale
+ *    Guy Eric Schalnat
+ *    Paul Schmidt
+ *    Tim Wegner
+ *
+ * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ *
+ *   1. The origin of this source code must not be misrepresented.
+ *
+ *   2. Altered versions must be plainly marked as such and must not
+ *      be misrepresented as being the original source.
+ *
+ *   3. This Copyright notice may not be removed or altered from any
+ *      source or altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products.  If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ *
+ * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE.
+ */
+
+/*
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ *
+ *    printf("%s", png_get_copyright(NULL));
+ *
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+ */
+
+/*
+ * Libpng is OSI Certified Open Source Software.  OSI Certified Open Source is
+ * a certification mark of the Open Source Initiative. OSI has not addressed
+ * the additional disclaimers inserted at version 1.0.7.
+ */
+
+/*
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience.  This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ */
+
+/* Note about libpng version numbers:
+ *
+ *    Due to various miscommunications, unforeseen code incompatibilities
+ *    and occasional factors outside the authors' control, version numbering
+ *    on the library has not always been consistent and straightforward.
+ *    The following table summarizes matters since version 0.89c, which was
+ *    the first widely used release:
  *
  *    source                 png.h  png.h  shared-lib
  *    version                string   int  version
@@ -58,276 +179,48 @@
  *    1.0.7beta15-18           1    10007  2.1.0.7beta15-18 (binary compatible)
  *    1.0.7rc1-2               1    10007  2.1.0.7rc1-2 (binary compatible)
  *    1.0.7                    1    10007  (still compatible)
- *    1.0.8beta1-4             1    10008  2.1.0.8beta1-4
- *    1.0.8rc1                 1    10008  2.1.0.8rc1
- *    1.0.8                    1    10008  2.1.0.8
- *    1.0.9beta1-6             1    10009  2.1.0.9beta1-6
- *    1.0.9rc1                 1    10009  2.1.0.9rc1
- *    1.0.9beta7-10            1    10009  2.1.0.9beta7-10
- *    1.0.9rc2                 1    10009  2.1.0.9rc2
- *    1.0.9                    1    10009  2.1.0.9
- *    1.0.10beta1              1    10010  2.1.0.10beta1
- *    1.0.10rc1                1    10010  2.1.0.10rc1
- *    1.0.10                   1    10010  2.1.0.10
- *    1.0.11beta1-3            1    10011  2.1.0.11beta1-3
- *    1.0.11rc1                1    10011  2.1.0.11rc1
- *    1.0.11                   1    10011  2.1.0.11
- *    1.0.12beta1-2            2    10012  2.1.0.12beta1-2
- *    1.0.12rc1                2    10012  2.1.0.12rc1
- *    1.0.12                   2    10012  2.1.0.12
- *    1.1.0a-f                 -    10100  2.1.1.0a-f (branch abandoned)
- *    1.2.0beta1-2             2    10200  2.1.2.0beta1-2
- *    1.2.0beta3-5             3    10200  3.1.2.0beta3-5
- *    1.2.0rc1                 3    10200  3.1.2.0rc1
- *    1.2.0                    3    10200  3.1.2.0
- *    1.2.1beta1-4             3    10201  3.1.2.1beta1-4
- *    1.2.1rc1-2               3    10201  3.1.2.1rc1-2
- *    1.2.1                    3    10201  3.1.2.1
- *    1.2.2beta1-6            12    10202  12.so.0.1.2.2beta1-6
- *    1.0.13beta1             10    10013  10.so.0.1.0.13beta1
- *    1.0.13rc1               10    10013  10.so.0.1.0.13rc1
- *    1.2.2rc1                12    10202  12.so.0.1.2.2rc1
- *    1.0.13                  10    10013  10.so.0.1.0.13
- *    1.2.2                   12    10202  12.so.0.1.2.2
- *    1.2.3rc1-6              12    10203  12.so.0.1.2.3rc1-6
- *    1.2.3                   12    10203  12.so.0.1.2.3
- *    1.2.4beta1-3            13    10204  12.so.0.1.2.4beta1-3
- *    1.0.14rc1               13    10014  10.so.0.1.0.14rc1
- *    1.2.4rc1                13    10204  12.so.0.1.2.4rc1
- *    1.0.14                  10    10014  10.so.0.1.0.14
- *    1.2.4                   13    10204  12.so.0.1.2.4
- *    1.2.5beta1-2            13    10205  12.so.0.1.2.5beta1-2
- *    1.0.15rc1-3             10    10015  10.so.0.1.0.15rc1-3
- *    1.2.5rc1-3              13    10205  12.so.0.1.2.5rc1-3
- *    1.0.15                  10    10015  10.so.0.1.0.15
- *    1.2.5                   13    10205  12.so.0.1.2.5
- *    1.2.6beta1-4            13    10206  12.so.0.1.2.6beta1-4
- *    1.0.16                  10    10016  10.so.0.1.0.16
- *    1.2.6                   13    10206  12.so.0.1.2.6
- *    1.2.7beta1-2            13    10207  12.so.0.1.2.7beta1-2
- *    1.0.17rc1               10    10017  12.so.0.1.0.17rc1
- *    1.2.7rc1                13    10207  12.so.0.1.2.7rc1
- *    1.0.17                  10    10017  12.so.0.1.0.17
- *    1.2.7                   13    10207  12.so.0.1.2.7
- *    1.2.8beta1-5            13    10208  12.so.0.1.2.8beta1-5
- *    1.0.18rc1-5             10    10018  12.so.0.1.0.18rc1-5
- *    1.2.8rc1-5              13    10208  12.so.0.1.2.8rc1-5
- *    1.0.18                  10    10018  12.so.0.1.0.18
- *    1.2.8                   13    10208  12.so.0.1.2.8
- *    1.2.9beta1-3            13    10209  12.so.0.1.2.9beta1-3
- *    1.2.9beta4-11           13    10209  12.so.0.9[.0]
- *    1.2.9rc1                13    10209  12.so.0.9[.0]
- *    1.2.9                   13    10209  12.so.0.9[.0]
- *    1.2.10beta1-7           13    10210  12.so.0.10[.0]
- *    1.2.10rc1-2             13    10210  12.so.0.10[.0]
- *    1.2.10                  13    10210  12.so.0.10[.0]
- *    1.4.0beta1-5            14    10400  14.so.0.0[.0]
- *    1.2.11beta1-4           13    10211  12.so.0.11[.0]
- *    1.4.0beta7-8            14    10400  14.so.0.0[.0]
- *    1.2.11                  13    10211  12.so.0.11[.0]
- *    1.2.12                  13    10212  12.so.0.12[.0]
- *    1.4.0beta9-14           14    10400  14.so.0.0[.0]
- *    1.2.13                  13    10213  12.so.0.13[.0]
- *    1.4.0beta15-36          14    10400  14.so.0.0[.0]
- *    1.4.0beta37-87          14    10400  14.so.14.0[.0]
- *    1.4.0rc01               14    10400  14.so.14.0[.0]
- *    1.4.0beta88-109         14    10400  14.so.14.0[.0]
- *    1.4.0rc02-08            14    10400  14.so.14.0[.0]
- *    1.4.0                   14    10400  14.so.14.0[.0]
- *    1.4.1beta01-03          14    10401  14.so.14.1[.0]
- *    1.4.1rc01               14    10401  14.so.14.1[.0]
- *    1.4.1beta04-12          14    10401  14.so.14.1[.0]
- *    1.4.1                   14    10401  14.so.14.1[.0]
- *    1.4.2                   14    10402  14.so.14.2[.0]
- *    1.4.3                   14    10403  14.so.14.3[.0]
- *    1.4.4                   14    10404  14.so.14.4[.0]
- *    1.5.0beta01-58          15    10500  15.so.15.0[.0]
- *    1.5.0rc01-07            15    10500  15.so.15.0[.0]
- *    1.5.0                   15    10500  15.so.15.0[.0]
- *    1.5.1beta01-11          15    10501  15.so.15.1[.0]
- *    1.5.1rc01-02            15    10501  15.so.15.1[.0]
- *    1.5.1                   15    10501  15.so.15.1[.0]
- *    1.5.2beta01-03          15    10502  15.so.15.2[.0]
- *    1.5.2rc01-03            15    10502  15.so.15.2[.0]
- *    1.5.2                   15    10502  15.so.15.2[.0]
- *    1.5.3beta01-10          15    10503  15.so.15.3[.0]
- *    1.5.3rc01-02            15    10503  15.so.15.3[.0]
- *    1.5.3beta11             15    10503  15.so.15.3[.0]
- *    1.5.3 [omitted]
- *    1.5.4beta01-08          15    10504  15.so.15.4[.0]
- *    1.5.4rc01               15    10504  15.so.15.4[.0]
- *    1.5.4                   15    10504  15.so.15.4[.0]
- *    1.5.5beta01-08          15    10505  15.so.15.5[.0]
- *    1.5.5rc01               15    10505  15.so.15.5[.0]
- *    1.5.5                   15    10505  15.so.15.5[.0]
- *    1.5.6beta01-07          15    10506  15.so.15.6[.0]
- *    1.5.6rc01-03            15    10506  15.so.15.6[.0]
- *    1.5.6                   15    10506  15.so.15.6[.0]
- *    1.5.7beta01-05          15    10507  15.so.15.7[.0]
- *    1.5.7rc01-03            15    10507  15.so.15.7[.0]
- *    1.5.7                   15    10507  15.so.15.7[.0]
- *    1.6.0beta01-40          16    10600  16.so.16.0[.0]
- *    1.6.0rc01-08            16    10600  16.so.16.0[.0]
- *    1.6.0                   16    10600  16.so.16.0[.0]
- *    1.6.1beta01-09          16    10601  16.so.16.1[.0]
- *    1.6.1rc01               16    10601  16.so.16.1[.0]
- *    1.6.1                   16    10601  16.so.16.1[.0]
- *    1.6.2beta01             16    10602  16.so.16.2[.0]
- *    1.6.2rc01-06            16    10602  16.so.16.2[.0]
- *    1.6.2                   16    10602  16.so.16.2[.0]
- *    1.6.3beta01-11          16    10603  16.so.16.3[.0]
- *    1.6.3rc01               16    10603  16.so.16.3[.0]
- *    1.6.3                   16    10603  16.so.16.3[.0]
+ *    ...
+ *    1.0.19                  10    10019  10.so.0.19[.0]
+ *    ...
+ *    1.2.53                  13    10253  12.so.0.53[.0]
+ *    ...
+ *    1.5.23                  15    10523  15.so.15.23[.0]
+ *    ...
+ *    1.6.20                  16    10620  16.so.16.20[.0]
  *
- *   Henceforth the source version will match the shared-library major
- *   and minor numbers; the shared-library major version number will be
- *   used for changes in backward compatibility, as it is intended.  The
- *   PNG_LIBPNG_VER macro, which is not used within libpng but is available
- *   for applications, is an unsigned integer of the form xyyzz corresponding
- *   to the source version x.y.z (leading zeros in y and z).  Beta versions
- *   were given the previous public release number plus a letter, until
- *   version 1.0.6j; from then on they were given the upcoming public
- *   release number plus "betaNN" or "rcNN".
+ *    Henceforth the source version will match the shared-library major
+ *    and minor numbers; the shared-library major version number will be
+ *    used for changes in backward compatibility, as it is intended.  The
+ *    PNG_LIBPNG_VER macro, which is not used within libpng but is available
+ *    for applications, is an unsigned integer of the form xyyzz corresponding
+ *    to the source version x.y.z (leading zeros in y and z).  Beta versions
+ *    were given the previous public release number plus a letter, until
+ *    version 1.0.6j; from then on they were given the upcoming public
+ *    release number plus "betaNN" or "rcNN".
  *
- *   Binary incompatibility exists only when applications make direct access
- *   to the info_ptr or png_ptr members through png.h, and the compiled
- *   application is loaded with a different version of the library.
+ *    Binary incompatibility exists only when applications make direct access
+ *    to the info_ptr or png_ptr members through png.h, and the compiled
+ *    application is loaded with a different version of the library.
  *
- *   DLLNUM will change each time there are forward or backward changes
- *   in binary compatibility (e.g., when a new feature is added).
+ *    DLLNUM will change each time there are forward or backward changes
+ *    in binary compatibility (e.g., when a new feature is added).
  *
- * See libpng-manual.txt or libpng.3 for more information.  The PNG
- * specification is available as a W3C Recommendation and as an ISO
- * Specification, <http://www.w3.org/TR/2003/REC-PNG-20031110/
- */
-
-/*
- * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
- *
- * If you modify libpng you may insert additional notices immediately following
- * this sentence.
- *
- * This code is released under the libpng license.
- *
- * libpng versions 1.2.6, August 15, 2004, through 1.6.3, July 18, 2013, are
- * Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson, and are
- * distributed according to the same disclaimer and license as libpng-1.2.5
- * with the following individual added to the list of Contributing Authors:
- *
- *    Cosmin Truta
- *
- * libpng versions 1.0.7, July 1, 2000, through 1.2.5, October 3, 2002, are
- * Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
- * distributed according to the same disclaimer and license as libpng-1.0.6
- * with the following individuals added to the list of Contributing Authors:
- *
- *    Simon-Pierre Cadieux
- *    Eric S. Raymond
- *    Gilles Vollant
- *
- * and with the following additions to the disclaimer:
- *
- *    There is no warranty against interference with your enjoyment of the
- *    library or against infringement.  There is no warranty that our
- *    efforts or the library will fulfill any of your particular purposes
- *    or needs.  This library is provided with all faults, and the entire
- *    risk of satisfactory quality, performance, accuracy, and effort is with
- *    the user.
- *
- * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
- * Copyright (c) 1998, 1999, 2000 Glenn Randers-Pehrson, and are
- * distributed according to the same disclaimer and license as libpng-0.96,
- * with the following individuals added to the list of Contributing Authors:
- *
- *    Tom Lane
- *    Glenn Randers-Pehrson
- *    Willem van Schaik
- *
- * libpng versions 0.89, June 1996, through 0.96, May 1997, are
- * Copyright (c) 1996, 1997 Andreas Dilger
- * Distributed according to the same disclaimer and license as libpng-0.88,
- * with the following individuals added to the list of Contributing Authors:
- *
- *    John Bowler
- *    Kevin Bracey
- *    Sam Bushell
- *    Magnus Holmgren
- *    Greg Roelofs
- *    Tom Tanner
- *
- * libpng versions 0.5, May 1995, through 0.88, January 1996, are
- * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
- *
- * For the purposes of this copyright and license, "Contributing Authors"
- * is defined as the following set of individuals:
- *
- *    Andreas Dilger
- *    Dave Martindale
- *    Guy Eric Schalnat
- *    Paul Schmidt
- *    Tim Wegner
- *
- * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
- * and Group 42, Inc. disclaim all warranties, expressed or implied,
- * including, without limitation, the warranties of merchantability and of
- * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
- * assume no liability for direct, indirect, incidental, special, exemplary,
- * or consequential damages, which may result from the use of the PNG
- * Reference Library, even if advised of the possibility of such damage.
- *
- * Permission is hereby granted to use, copy, modify, and distribute this
- * source code, or portions hereof, for any purpose, without fee, subject
- * to the following restrictions:
- *
- *   1. The origin of this source code must not be misrepresented.
- *
- *   2. Altered versions must be plainly marked as such and must not
- *      be misrepresented as being the original source.
- *
- *   3. This Copyright notice may not be removed or altered from
- *      any source or altered source distribution.
- *
- * The Contributing Authors and Group 42, Inc. specifically permit, without
- * fee, and encourage the use of this source code as a component to
- * supporting the PNG file format in commercial products.  If you use this
- * source code in a product, acknowledgment is not required but would be
- * appreciated.
- */
-
-/*
- * A "png_get_copyright" function is available, for convenient use in "about"
- * boxes and the like:
- *
- *     printf("%s", png_get_copyright(NULL));
- *
- * Also, the PNG logo (in PNG format, of course) is supplied in the
- * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
- */
-
-/*
- * Libpng is OSI Certified Open Source Software.  OSI Certified is a
- * certification mark of the Open Source Initiative.
- */
-
-/*
- * The contributing authors would like to thank all those who helped
- * with testing, bug fixes, and patience.  This wouldn't have been
- * possible without all of you.
- *
- * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ * See libpng.txt or libpng.3 for more information.  The PNG specification
+ * is available as a W3C Recommendation and as an ISO Specification,
+ * <http://www.w3.org/TR/2003/REC-PNG-20031110/
  */
 
 /*
  * Y2K compliance in libpng:
  * =========================
  *
- *    July 18, 2013
+ *    December 3, 2015
  *
  *    Since the PNG Development group is an ad-hoc body, we can't make
  *    an official declaration.
  *
  *    This is your unofficial assurance that libpng from version 0.71 and
- *    upward through 1.6.3 are Y2K compliant.  It is my belief that
+ *    upward through 1.6.20 are Y2K compliant.  It is my belief that
  *    earlier versions were also Y2K compliant.
  *
  *    Libpng only has two year fields.  One is a 2-byte unsigned integer
@@ -380,272 +273,18 @@
 /* This is not the place to learn how to use libpng. The file libpng-manual.txt
  * describes how to use libpng, and the file example.c summarizes it
  * with some code on which to build.  This file is useful for looking
- * at the actual function definitions and structure components.
+ * at the actual function definitions and structure components.  If that
+ * file has been stripped from your copy of libpng, you can find it at
+ * <http://www.libpng.org/pub/png/libpng-manual.txt>
  *
  * If you just need to read a PNG file and don't want to read the documentation
  * skip to the end of this file and read the section entitled 'simplified API'.
  */
 
-/* Sunliang.liu add 20100319 for avoid name conflict. */
-#define png_set_sig_bytes FOXIT_png_set_sig_bytes
-#define png_sig_cmp FOXIT_png_sig_cmp
-#define png_create_info_struct FOXIT_png_create_info_struct
-#define png_destroy_info_struct FOXIT_png_destroy_info_struct
-#define png_info_init_3 FOXIT_png_info_init_3
-#define png_data_freer FOXIT_png_data_freer
-#define png_free_data FOXIT_png_free_data
-#define png_info_destroy FOXIT_png_info_destroy
-#define png_get_io_ptr FOXIT_png_get_io_ptr
-#define png_init_io FOXIT_png_init_io
-#define png_convert_to_rfc1123 FOXIT_png_convert_to_rfc1123
-#define png_get_copyright FOXIT_png_get_copyright
-#define png_get_libpng_ver FOXIT_png_get_libpng_ver
-#define png_get_header_ver FOXIT_png_get_header_ver
-#define png_get_header_version FOXIT_png_get_header_version
-#define png_handle_as_unknown FOXIT_png_handle_as_unknown
-#define png_reset_zstream FOXIT_png_reset_zstream
-#define png_access_version_number FOXIT_png_access_version_number
-#define png_mmx_support FOXIT_png_mmx_support
-#define png_create_read_struct FOXIT_png_create_read_struct
-#define png_create_read_struct_2 FOXIT_png_create_read_struct_2
-#define png_create_write_struct FOXIT_png_create_write_struct
-#define png_create_write_struct_2 FOXIT_png_create_write_struct_2
-#define png_get_compression_buffer_size FOXIT_png_get_compression_buffer_size
-#define png_set_compression_buffer_size FOXIT_png_set_compression_buffer_size
-#define png_set_longjmp_fn FOXIT_png_set_longjmp_fn
-#define png_longjmp FOXIT_png_longjmp
-#define png_write_sig FOXIT_png_write_sig
-#define png_write_chunk FOXIT_png_write_chunk
-#define png_write_chunk_start FOXIT_png_write_chunk_start
-#define png_write_chunk_data FOXIT_png_write_chunk_data
-#define png_write_chunk_end FOXIT_png_write_chunk_end
-#define png_write_info_before_PLTE FOXIT_png_write_info_before_PLTE
-#define png_write_info FOXIT_png_write_info
-#define png_read_info FOXIT_png_read_info
-#define png_convert_to_rfc1123_buffer FOXIT_png_convert_to_rfc1123_buffer
-#define png_convert_from_struct_tm FOXIT_png_convert_from_struct_tm
-#define png_convert_from_time_t FOXIT_png_convert_from_time_t
-#define png_set_expand FOXIT_png_set_expand
-#define png_set_expand_gray_1_2_4_to_8 FOXIT_png_set_expand_gray_1_2_4_to_8
-#define png_set_palette_to_rgb FOXIT_png_set_palette_to_rgb
-#define png_set_tRNS_to_alpha FOXIT_png_set_tRNS_to_alpha
-#define png_set_expand_16 FOXIT_png_set_expand_16
-#define png_set_bgr FOXIT_png_set_bgr
-#define png_set_gray_to_rgb FOXIT_png_set_gray_to_rgb
-#define png_set_rgb_to_gray FOXIT_png_set_rgb_to_gray
-#define png_set_rgb_to_gray_fixed FOXIT_png_set_rgb_to_gray_fixed
-#define png_get_rgb_to_gray_status FOXIT_png_get_rgb_to_gray_status
-#define png_build_grayscale_palette FOXIT_png_build_grayscale_palette
-#define png_set_alpha_mode FOXIT_png_set_alpha_mode
-#define png_set_alpha_mode_fixed FOXIT_png_set_alpha_mode_fixed
-#define png_set_strip_alpha FOXIT_png_set_strip_alpha
-#define png_set_swap_alpha FOXIT_png_set_swap_alpha
-#define png_set_invert_alpha FOXIT_png_set_invert_alpha
-#define png_set_filler FOXIT_png_set_filler
-#define png_set_add_alpha FOXIT_png_set_add_alpha
-#define png_set_swap FOXIT_png_set_swap
-#define png_set_packing FOXIT_png_set_packing
-#define png_set_packswap FOXIT_png_set_packswap
-#define png_set_shift FOXIT_png_set_shift
-#define png_set_interlace_handling FOXIT_png_set_interlace_handling
-#define png_set_invert_mono FOXIT_png_set_invert_mono
-#define png_set_background FOXIT_png_set_background
-#define png_set_background_fixed FOXIT_png_set_background_fixed
-#define png_set_scale_16 FOXIT_png_set_scale_16
-#define png_set_quantize FOXIT_png_set_quantize
-#define png_set_gamma FOXIT_png_set_gamma
-#define png_set_gamma_fixed FOXIT_png_set_gamma_fixed
-#define png_set_flush FOXIT_png_set_flush
-#define png_write_flush FOXIT_png_write_flush
-#define png_start_read_image FOXIT_png_start_read_image
-#define png_read_update_info FOXIT_png_read_update_info
-#define png_read_rows FOXIT_png_read_rows
-#define png_read_row FOXIT_png_read_row
-#define png_read_image FOXIT_png_read_image
-#define png_write_row FOXIT_png_write_row
-#define png_write_rows FOXIT_png_write_rows
-#define png_write_image FOXIT_png_write_image
-#define png_write_end FOXIT_png_write_end
-#define png_read_end FOXIT_png_read_end
-#define png_destroy_read_struct FOXIT_png_destroy_read_struct
-#define png_destroy_write_struct FOXIT_png_destroy_write_struct
-#define png_set_crc_action FOXIT_png_set_crc_action
-#define png_set_filter FOXIT_png_set_filter
-#define png_set_filter_heuristics FOXIT_png_set_filter_heuristics
-#define png_set_filter_heuristics_fixed FOXIT_png_set_filter_heuristics_fixed
-#define png_set_compression_level FOXIT_png_set_compression_level
-#define png_set_compression_mem_level FOXIT_png_set_compression_mem_level
-#define png_set_compression_strategy FOXIT_png_set_compression_strategy
-#define png_set_compression_window_bits FOXIT_png_set_compression_window_bits
-#define png_set_compression_method FOXIT_png_set_compression_method
-#define png_set_text_compression_level FOXIT_png_set_text_compression_level
-#define png_set_text_compression_mem_level FOXIT_png_set_text_compression_mem_level
-#define png_set_text_compression_strategy FOXIT_png_set_text_compression_strategy
-#define png_set_text_compression_window_bits FOXIT_png_set_text_compression_window_bits
-#define png_set_text_compression_method FOXIT_png_set_text_compression_method
-#define png_set_error_fn FOXIT_png_set_error_fn
-#define png_get_error_ptr FOXIT_png_get_error_ptr
-#define png_set_write_fn FOXIT_png_set_write_fn
-#define png_set_read_fn FOXIT_png_set_read_fn
-#define png_set_read_status_fn FOXIT_png_set_read_status_fn
-#define png_set_write_status_fn FOXIT_png_set_write_status_fn
-#define png_set_read_user_transform_fn FOXIT_png_set_read_user_transform_fn
-#define png_set_write_user_transform_fn FOXIT_png_set_write_user_transform_fn
-#define png_set_user_transform_info FOXIT_png_set_user_transform_info
-#define png_get_user_transform_ptr FOXIT_png_get_user_transform_ptr
-#define png_get_current_row_number FOXIT_png_get_current_row_number
-#define png_get_current_pass_number FOXIT_png_get_current_pass_number
-#define png_set_read_user_chunk_fn FOXIT_png_set_read_user_chunk_fn
-#define png_get_user_chunk_ptr FOXIT_png_get_user_chunk_ptr
-#define png_set_progressive_read_fn FOXIT_png_set_progressive_read_fn
-#define png_get_progressive_ptr FOXIT_png_get_progressive_ptr
-#define png_process_data FOXIT_png_process_data
-#define png_process_data_pause FOXIT_png_process_data_pause
-#define png_process_data_skip FOXIT_png_process_data_skip
-#define png_progressive_combine_row FOXIT_png_progressive_combine_row
-#define png_calloc FOXIT_png_calloc
-#define png_error FOXIT_png_error
-#define png_chunk_error FOXIT_png_chunk_error
-#define png_err FOXIT_png_err
-#define png_warning FOXIT_png_warning
-#define png_chunk_warning FOXIT_png_chunk_warning
-#define png_benign_error FOXIT_png_benign_error
-#define png_chunk_benign_error FOXIT_png_chunk_benign_error
-#define png_set_benign_errors FOXIT_png_set_benign_errors
-#define png_get_valid FOXIT_png_get_valid
-#define png_get_rowbytes FOXIT_png_get_rowbytes
-#define png_get_rows FOXIT_png_get_rows
-#define png_set_rows FOXIT_png_set_rows
-#define png_get_channels FOXIT_png_get_channels
-#define png_get_image_width FOXIT_png_get_image_width
-#define png_get_image_height FOXIT_png_get_image_height
-#define png_get_bit_depth FOXIT_png_get_bit_depth
-#define png_get_color_type FOXIT_png_get_color_type
-#define png_get_filter_type FOXIT_png_get_filter_type
-#define png_get_interlace_type FOXIT_png_get_interlace_type
-#define png_get_compression_type FOXIT_png_get_compression_type
-#define png_get_pixels_per_meter FOXIT_png_get_pixels_per_meter
-#define png_get_x_pixels_per_meter FOXIT_png_get_x_pixels_per_meter
-#define png_get_y_pixels_per_meter FOXIT_png_get_y_pixels_per_meter
-#define png_get_pixel_aspect_ratio FOXIT_png_get_pixel_aspect_ratio
-#define png_get_pixel_aspect_ratio_fixed FOXIT_png_get_pixel_aspect_ratio_fixed
-#define png_get_x_offset_pixels FOXIT_png_get_x_offset_pixels
-#define png_get_y_offset_pixels FOXIT_png_get_y_offset_pixels
-#define png_get_x_offset_microns FOXIT_png_get_x_offset_microns
-#define png_get_y_offset_microns FOXIT_png_get_y_offset_microns
-#define png_get_signature FOXIT_png_get_signature
-#define png_get_bKGD FOXIT_png_get_bKGD
-#define png_set_bKGD FOXIT_png_set_bKGD
-#define png_get_cHRM FOXIT_png_get_cHRM
-#define png_get_cHRM_XYZ FOXIT_png_get_cHRM_XYZ
-#define png_get_cHRM_fixed FOXIT_png_get_cHRM_fixed
-#define png_get_cHRM_XYZ_fixed FOXIT_png_get_cHRM_XYZ_fixed
-#define png_set_cHRM FOXIT_png_set_cHRM
-#define png_set_cHRM_XYZ FOXIT_png_set_cHRM_XYZ
-#define png_set_cHRM_fixed FOXIT_png_set_cHRM_fixed
-#define png_set_cHRM_XYZ_fixed FOXIT_png_set_cHRM_XYZ_fixed
-#define png_get_gAMA FOXIT_png_get_gAMA
-#define png_set_gAMA FOXIT_png_set_gAMA
-#define png_set_gAMA_fixed FOXIT_png_set_gAMA_fixed
-#define png_get_hIST FOXIT_png_get_hIST
-#define png_set_hIST FOXIT_png_set_hIST
-#define png_get_IHDR FOXIT_png_get_IHDR
-#define png_set_IHDR FOXIT_png_set_IHDR
-#define png_get_oFFs FOXIT_png_get_oFFs
-#define png_set_oFFs FOXIT_png_set_oFFs
-#define png_get_pCAL FOXIT_png_get_pCAL
-#define png_set_pCAL FOXIT_png_set_pCAL
-#define png_set_pHYs FOXIT_png_set_pHYs
-#define png_get_PLTE FOXIT_png_get_PLTE
-#define png_set_PLTE FOXIT_png_set_PLTE
-#define png_get_sBIT FOXIT_png_get_sBIT
-#define png_set_sBIT FOXIT_png_set_sBIT
-#define png_get_sRGB FOXIT_png_get_sRGB
-#define png_set_sRGB FOXIT_png_set_sRGB
-#define png_set_sRGB_gAMA_and_cHRM FOXIT_png_set_sRGB_gAMA_and_cHRM
-#define png_get_iCCP FOXIT_png_get_iCCP
-#define png_set_iCCP FOXIT_png_set_iCCP
-#define png_get_sPLT FOXIT_png_get_sPLT
-#define png_set_sPLT FOXIT_png_set_sPLT
-#define png_get_text FOXIT_png_get_text
-#define png_set_text FOXIT_png_set_text
-#define png_get_tIME FOXIT_png_get_tIME
-#define png_set_tIME FOXIT_png_set_tIME
-#define png_get_tRNS FOXIT_png_get_tRNS
-#define png_set_tRNS FOXIT_png_set_tRNS
-#define png_get_sCAL FOXIT_png_get_sCAL
-#define png_get_sCAL_fixed FOXIT_png_get_sCAL_fixed
-#define png_get_sCAL_s FOXIT_png_get_sCAL_s
-#define png_set_sCAL FOXIT_png_set_sCAL
-#define png_set_sCAL_s FOXIT_png_set_sCAL_s
-#define png_set_keep_unknown_chunks FOXIT_png_set_keep_unknown_chunks
-#define png_set_unknown_chunks FOXIT_png_set_unknown_chunks
-#define png_set_unknown_chunk_location FOXIT_png_set_unknown_chunk_location
-#define png_get_unknown_chunks FOXIT_png_get_unknown_chunks
-#define png_set_invalid FOXIT_png_set_invalid
-#define png_read_png FOXIT_png_read_png
-#define png_write_png FOXIT_png_write_png
-#define png_permit_mng_features FOXIT_png_permit_mng_features
-#define png_set_strip_error_numbers FOXIT_png_set_strip_error_numbers
-#define png_set_user_limits FOXIT_png_set_user_limits
-#define png_get_user_width_max FOXIT_png_get_user_width_max
-#define png_get_user_height_max FOXIT_png_get_user_height_max
-#define png_set_chunk_cache_max FOXIT_png_set_chunk_cache_max
-#define png_get_chunk_cache_max FOXIT_png_get_chunk_cache_max
-#define png_set_chunk_malloc_max FOXIT_png_set_chunk_malloc_max
-#define png_get_chunk_malloc_max FOXIT_png_get_chunk_malloc_max
-#define png_get_pixels_per_inch FOXIT_png_get_pixels_per_inch
-#define png_get_x_pixels_per_inch FOXIT_png_get_x_pixels_per_inch
-#define png_get_y_pixels_per_inch FOXIT_png_get_y_pixels_per_inch
-#define png_get_x_offset_inches FOXIT_png_get_x_offset_inches
-#define png_get_x_offset_inches_fixed FOXIT_png_get_x_offset_inches_fixed
-#define png_get_y_offset_inches FOXIT_png_get_y_offset_inches
-#define png_get_y_offset_inches_fixed FOXIT_png_get_y_offset_inches_fixed
-#define png_get_pHYs_dpi FOXIT_png_get_pHYs_dpi
-#define png_get_io_state FOXIT_png_get_io_state
-#define png_get_io_chunk_name FOXIT_png_get_io_chunk_name
-#define png_get_io_chunk_type FOXIT_png_get_io_chunk_type
-#define png_get_uint_31 FOXIT_png_get_uint_31
-#define png_get_uint_32 FOXIT_png_get_uint_32
-#define png_get_uint_16 FOXIT_png_get_uint_16
-#define png_get_int_32 FOXIT_png_get_int_32
-#define png_save_uint_32 FOXIT_png_save_uint_32
-#define png_save_int_32 FOXIT_png_save_int_32
-#define png_save_uint_16 FOXIT_png_save_uint_16
-#define png_image_begin_read_from_file FOXIT_png_image_begin_read_from_file
-#define png_image_begin_read_from_stdio FOXIT_png_image_begin_read_from_stdio
-#define png_image_begin_read_from_memory FOXIT_png_image_begin_read_from_memory
-#define png_image_finish_read FOXIT_png_image_finish_read
-#define png_image_free FOXIT_png_image_free
-#define png_image_write_to_file FOXIT_png_image_write_to_file
-#define png_image_write_to_stdio FOXIT_png_image_write_to_stdio
-#define png_set_check_for_invalid_index FOXIT_png_set_check_for_invalid_index
-#define png_get_palette_max FOXIT_png_get_palette_max
-#define png_set_option FOXIT_png_set_option
-
-#define png_set_strip_16 FOXIT_png_set_strip_16
-#define png_64bit_product FOXIT_png_64bit_product
-#define png_check_cHRM_fixed FOXIT_png_check_cHRM_fixed
-#define png_free FOXIT_png_free
-#define png_free_default FOXIT_png_free_default
-#define png_get_mem_ptr FOXIT_png_get_mem_ptr
-#define png_malloc FOXIT_png_malloc
-#define png_malloc_default FOXIT_png_malloc_default
-#define png_malloc_warn FOXIT_png_malloc_warn
-#define png_memcpy_check FOXIT_png_memcpy_check
-#define png_memset_check FOXIT_png_memset_check
-#define png_pass_dsp_mask FOXIT_png_pass_dsp_mask
-#define png_pass_inc FOXIT_png_pass_inc
-#define png_pass_mask FOXIT_png_pass_mask
-#define png_pass_start FOXIT_png_pass_start
-#define png_pass_yinc FOXIT_png_pass_yinc
-#define png_pass_ystart FOXIT_png_pass_ystart
-#define png_set_mem_fn FOXIT_png_set_mem_fn
-
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.3"
+#define PNG_LIBPNG_VER_STRING "1.6.20"
 #define PNG_HEADER_VERSION_STRING \
-     " libpng version 1.6.3 - July 18, 2013\n"
+     " libpng version 1.6.20 - December 3, 2015\n"
 
 #define PNG_LIBPNG_VER_SONUM   16
 #define PNG_LIBPNG_VER_DLLNUM  16
@@ -653,7 +292,7 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 3
+#define PNG_LIBPNG_VER_RELEASE 20
 
 /* This should match the numeric part of the final component of
  * PNG_LIBPNG_VER_STRING, omitting any leading zero:
@@ -684,7 +323,7 @@
  * version 1.0.0 was mis-numbered 100 instead of 10000).  From
  * version 1.0.1 it's    xxyyzz, where x=major, y=minor, z=release
  */
-#define PNG_LIBPNG_VER 10603 /* 1.6.3 */
+#define PNG_LIBPNG_VER 10620 /* 1.6.20 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
@@ -741,17 +380,22 @@
 
 /* This file is arranged in several sections:
  *
- * 1. Any configuration options that can be specified by for the application
+ * 1. [omitted]
+ * 2. Any configuration options that can be specified by for the application
  *    code when it is built.  (Build time configuration is in pnglibconf.h)
- * 2. Type definitions (base types are defined in pngconf.h), structure
+ * 3. Type definitions (base types are defined in pngconf.h), structure
  *    definitions.
- * 3. Exported library functions.
- * 4. Simplified API.
+ * 4. Exported library functions.
+ * 5. Simplified API.
+ * 6. Implementation options.
  *
  * The library source code has additional files (principally pngpriv.h) that
  * allow configuration of the library.
  */
-/* Section 1: run time configuration
+
+/* Section 1: [omitted] */
+
+/* Section 2: run time configuration
  * See pnglibconf.h for build time configuration
  *
  * Run time configuration allows the application to choose between
@@ -781,7 +425,7 @@
  * Otherwise the calls are mapped to png_error.
  */
 
-/* Section 2: type definitions, including structures and compile time
+/* Section 3: type definitions, including structures and compile time
  * constants.
  * See pngconf.h for base types that vary by machine/system
  */
@@ -789,7 +433,7 @@
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char* png_libpng_version_1_6_3;
+typedef char* png_libpng_version_1_6_20;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
@@ -971,7 +615,8 @@
 typedef const png_time * png_const_timep;
 typedef png_time * * png_timepp;
 
-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\
+   defined(PNG_USER_CHUNKS_SUPPORTED)
 /* png_unknown_chunk is a structure to hold queued chunks for which there is
  * no specific support.  The idea is that we can use this to queue
  * up private chunks for output even though the library doesn't actually
@@ -1104,7 +749,9 @@
 #define PNG_INFO_iCCP 0x1000   /* ESR, 1.0.6 */
 #define PNG_INFO_sPLT 0x2000   /* ESR, 1.0.6 */
 #define PNG_INFO_sCAL 0x4000   /* ESR, 1.0.6 */
+#if INT_MAX >= 0x8000 /* else this might break */
 #define PNG_INFO_IDAT 0x8000   /* ESR, 1.0.6 */
+#endif
 
 /* This is used for the transformation routines, as some of them
  * change these values for the row.  It also should enable using
@@ -1208,7 +855,9 @@
 #define PNG_TRANSFORM_GRAY_TO_RGB   0x2000      /* read only */
 /* Added to libpng-1.5.4 */
 #define PNG_TRANSFORM_EXPAND_16     0x4000      /* read only */
+#if INT_MAX >= 0x8000 /* else this might break */
 #define PNG_TRANSFORM_SCALE_16      0x8000      /* read only */
+#endif
 
 /* Flags for MNG supported features */
 #define PNG_FLAG_MNG_EMPTY_PLTE     0x01
@@ -1225,7 +874,7 @@
     png_alloc_size_t));
 typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
 
-/* Section 3: exported functions
+/* Section 4: exported functions
  * Here are the function definitions most commonly used.  This is not
  * the place to find out how to use libpng.  See libpng-manual.txt for the
  * full explanation, see example.c for the summary.  This just provides
@@ -1401,7 +1050,7 @@
 
 /* Convert from time_t to png_time.  Uses gmtime() */
 PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime));
-#endif /* PNG_CONVERT_tIME_SUPPORTED */
+#endif /* CONVERT_tIME */
 
 #ifdef PNG_READ_EXPAND_SUPPORTED
 /* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
@@ -1450,9 +1099,9 @@
 #endif
 
 #ifdef PNG_READ_ALPHA_MODE_SUPPORTED
-/* How the alpha channel is interpreted - this affects how the color channels of
- * a PNG file are returned when an alpha channel, or tRNS chunk in a palette
- * file, is present.
+/* How the alpha channel is interpreted - this affects how the color channels
+ * of a PNG file are returned to the calling application when an alpha channel,
+ * or a tRNS chunk in a palette file, is present.
  *
  * This has no effect on the way pixels are written into a PNG output
  * datastream. The color samples in a PNG datastream are never premultiplied
@@ -1460,33 +1109,19 @@
  *
  * The default is to return data according to the PNG specification: the alpha
  * channel is a linear measure of the contribution of the pixel to the
- * corresponding composited pixel.  The gamma encoded color channels must be
- * scaled according to the contribution and to do this it is necessary to undo
+ * corresponding composited pixel, and the color channels are unassociated
+ * (not premultiplied).  The gamma encoded color channels must be scaled
+ * according to the contribution and to do this it is necessary to undo
  * the encoding, scale the color values, perform the composition and reencode
  * the values.  This is the 'PNG' mode.
  *
  * The alternative is to 'associate' the alpha with the color information by
- * storing color channel values that have been scaled by the alpha.  The
- * advantage is that the color channels can be resampled (the image can be
- * scaled) in this form.  The disadvantage is that normal practice is to store
- * linear, not (gamma) encoded, values and this requires 16-bit channels for
- * still images rather than the 8-bit channels that are just about sufficient if
- * gamma encoding is used.  In addition all non-transparent pixel values,
- * including completely opaque ones, must be gamma encoded to produce the final
- * image.  This is the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' mode (the
- * latter being the two common names for associated alpha color channels.)
+ * storing color channel values that have been scaled by the alpha.
+ * image.  These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes
+ * (the latter being the two common names for associated alpha color channels).
  *
- * Since it is not necessary to perform arithmetic on opaque color values so
- * long as they are not to be resampled and are in the final color space it is
- * possible to optimize the handling of alpha by storing the opaque pixels in
- * the PNG format (adjusted for the output color space) while storing partially
- * opaque pixels in the standard, linear, format.  The accuracy required for
- * standard alpha composition is relatively low, because the pixels are
- * isolated, therefore typically the accuracy loss in storing 8-bit linear
- * values is acceptable.  (This is not true if the alpha channel is used to
- * simulate transparency over large areas - use 16 bits or the PNG mode in
- * this case!)  This is the 'OPTIMIZED' mode.  For this mode a pixel is
- * treated as opaque only if the alpha value is equal to the maximum value.
+ * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha
+ * value is equal to the maximum value.
  *
  * The final choice is to gamma encode the alpha channel as well.  This is
  * broken because, in practice, no implementation that uses this choice
@@ -1513,68 +1148,7 @@
 
 #if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED)
 /* The output_gamma value is a screen gamma in libpng terminology: it expresses
- * how to decode the output values, not how they are encoded.  The values used
- * correspond to the normal numbers used to describe the overall gamma of a
- * computer display system; for example 2.2 for an sRGB conformant system.  The
- * values are scaled by 100000 in the _fixed version of the API (so 220000 for
- * sRGB.)
- *
- * The inverse of the value is always used to provide a default for the PNG file
- * encoding if it has no gAMA chunk and if png_set_gamma() has not been called
- * to override the PNG gamma information.
- *
- * When the ALPHA_OPTIMIZED mode is selected the output gamma is used to encode
- * opaque pixels however pixels with lower alpha values are not encoded,
- * regardless of the output gamma setting.
- *
- * When the standard Porter Duff handling is requested with mode 1 the output
- * encoding is set to be linear and the output_gamma value is only relevant
- * as a default for input data that has no gamma information.  The linear output
- * encoding will be overridden if png_set_gamma() is called - the results may be
- * highly unexpected!
- *
- * The following numbers are derived from the sRGB standard and the research
- * behind it.  sRGB is defined to be approximated by a PNG gAMA chunk value of
- * 0.45455 (1/2.2) for PNG.  The value implicitly includes any viewing
- * correction required to take account of any differences in the color
- * environment of the original scene and the intended display environment; the
- * value expresses how to *decode* the image for display, not how the original
- * data was *encoded*.
- *
- * sRGB provides a peg for the PNG standard by defining a viewing environment.
- * sRGB itself, and earlier TV standards, actually use a more complex transform
- * (a linear portion then a gamma 2.4 power law) than PNG can express.  (PNG is
- * limited to simple power laws.)  By saying that an image for direct display on
- * an sRGB conformant system should be stored with a gAMA chunk value of 45455
- * (11.3.3.2 and 11.3.3.5 of the ISO PNG specification) the PNG specification
- * makes it possible to derive values for other display systems and
- * environments.
- *
- * The Mac value is deduced from the sRGB based on an assumption that the actual
- * extra viewing correction used in early Mac display systems was implemented as
- * a power 1.45 lookup table.
- *
- * Any system where a programmable lookup table is used or where the behavior of
- * the final display device characteristics can be changed requires system
- * specific code to obtain the current characteristic.  However this can be
- * difficult and most PNG gamma correction only requires an approximate value.
- *
- * By default, if png_set_alpha_mode() is not called, libpng assumes that all
- * values are unencoded, linear, values and that the output device also has a
- * linear characteristic.  This is only very rarely correct - it is invariably
- * better to call png_set_alpha_mode() with PNG_DEFAULT_sRGB than rely on the
- * default if you don't know what the right answer is!
- *
- * The special value PNG_GAMMA_MAC_18 indicates an older Mac system (pre Mac OS
- * 10.6) which used a correction table to implement a somewhat lower gamma on an
- * otherwise sRGB system.
- *
- * Both these values are reserved (not simple gamma values) in order to allow
- * more precise correction internally in the future.
- *
- * NOTE: the following values can be passed to either the fixed or floating
- * point APIs, but the floating point API will also accept floating point
- * values.
+ * how to decode the output values, not how they are encoded.
  */
 #define PNG_DEFAULT_sRGB -1       /* sRGB gamma and color space */
 #define PNG_GAMMA_MAC_18 -2       /* Old Mac '1.8' gamma and color space */
@@ -1673,16 +1247,16 @@
 #endif
 
 #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
-/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */
+/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
 PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler,
     int flags));
 /* The values of the PNG_FILLER_ defines should NOT be changed */
 #  define PNG_FILLER_BEFORE 0
 #  define PNG_FILLER_AFTER 1
-/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
 PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr,
     png_uint_32 filler, int flags));
-#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+#endif /* READ_FILLER || WRITE_FILLER */
 
 #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
 /* Swap bytes in 16-bit depth files. */
@@ -1872,6 +1446,7 @@
 #define PNG_CRC_QUIET_USE     4  /* quiet/use data      quiet/use data    */
 #define PNG_CRC_NO_CHANGE     5  /* use current value   use current value */
 
+#ifdef PNG_WRITE_SUPPORTED
 /* These functions give the user control over the scan-line filtering in
  * libpng and the compression methods used by zlib.  These functions are
  * mainly useful for testing, as the defaults should work with most users.
@@ -1885,6 +1460,7 @@
  */
 PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
     int filters));
+#endif /* WRITE */
 
 /* Flags for png_set_filter() to say which filters to use.  The flags
  * are chosen so that they don't conflict with real filter types
@@ -1910,35 +1486,8 @@
 #define PNG_FILTER_VALUE_PAETH 4
 #define PNG_FILTER_VALUE_LAST  5
 
-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */
-/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
- * defines, either the default (minimum-sum-of-absolute-differences), or
- * the experimental method (weighted-minimum-sum-of-absolute-differences).
- *
- * Weights are factors >= 1.0, indicating how important it is to keep the
- * filter type consistent between rows.  Larger numbers mean the current
- * filter is that many times as likely to be the same as the "num_weights"
- * previous filters.  This is cumulative for each previous row with a weight.
- * There needs to be "num_weights" values in "filter_weights", or it can be
- * NULL if the weights aren't being specified.  Weights have no influence on
- * the selection of the first row filter.  Well chosen weights can (in theory)
- * improve the compression for a given image.
- *
- * Costs are factors >= 1.0 indicating the relative decoding costs of a
- * filter type.  Higher costs indicate more decoding expense, and are
- * therefore less likely to be selected over a filter with lower computational
- * costs.  There needs to be a value in "filter_costs" for each valid filter
- * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
- * setting the costs.  Costs try to improve the speed of decompression without
- * unduly increasing the compressed image size.
- *
- * A negative weight or cost indicates the default value is to be used, and
- * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
- * The default values for both weights and costs are currently 1.0, but may
- * change if good general weighting/cost heuristics can be found.  If both
- * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
- * to the UNWEIGHTED method, but with added encoding time/computation.
- */
+#ifdef PNG_WRITE_SUPPORTED
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
 PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr,
     int heuristic_method, int num_weights, png_const_doublep filter_weights,
     png_const_doublep filter_costs))
@@ -1946,17 +1495,14 @@
     (png_structrp png_ptr, int heuristic_method, int num_weights,
     png_const_fixed_point_p filter_weights,
     png_const_fixed_point_p filter_costs))
-#endif /*  PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+#endif /* WRITE_WEIGHTED_FILTER */
 
-/* Heuristic used for row filter selection.  These defines should NOT be
- * changed.
- */
+/* The following are no longer used and will be removed from libpng-1.7: */
 #define PNG_FILTER_HEURISTIC_DEFAULT    0  /* Currently "UNWEIGHTED" */
 #define PNG_FILTER_HEURISTIC_UNWEIGHTED 1  /* Used by libpng < 0.95 */
 #define PNG_FILTER_HEURISTIC_WEIGHTED   2  /* Experimental feature */
 #define PNG_FILTER_HEURISTIC_LAST       3  /* Not a valid value */
 
-#ifdef PNG_WRITE_SUPPORTED
 /* Set the library compression level.  Currently, valid values range from
  * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
  * (0 - no compression, 9 - "maximal" compression).  Note that tests have
@@ -1964,6 +1510,7 @@
  * for PNG images, and do considerably fewer caclulations.  In the future,
  * these values may not correspond directly to the zlib compression levels.
  */
+#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
 PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr,
     int level));
 
@@ -1981,7 +1528,7 @@
 
 PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr,
     int method));
-#endif
+#endif /* WRITE_CUSTOMIZE_COMPRESSION */
 
 #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
 /* Also set zlib parameters for compressing non-IDAT chunks */
@@ -2002,7 +1549,8 @@
 
 PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
     int method));
-#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
+#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
+#endif /* WRITE */
 
 /* These next functions are called for input/output, memory, and error
  * handling.  They are in the file pngrio.c, pngwio.c, and pngerror.c,
@@ -2113,7 +1661,7 @@
  *
  * The integer return from the callback function is interpreted thus:
  *
- * negative: An error occured, png_chunk_error will be called.
+ * negative: An error occurred; png_chunk_error will be called.
  *     zero: The chunk was not handled, the chunk will be saved. 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.
@@ -2162,7 +1710,6 @@
  */
 PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp));
 
-#ifdef PNG_READ_INTERLACING_SUPPORTED
 /* Function that combines rows.  'new_row' is a flag that should come from
  * the callback and be non-NULL if anything needs to be done; the library
  * stores its own version of the new data internally and ignores the passed
@@ -2170,8 +1717,7 @@
  */
 PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr,
     png_bytep old_row, png_const_bytep new_row));
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+#endif /* PROGRESSIVE_READ */
 
 PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr,
     png_alloc_size_t size), PNG_ALLOCATED);
@@ -2197,8 +1743,8 @@
  * It is unlikely that this function works correctly as of 1.6.0 and using it
  * may result either in memory leaks or double free of allocated data.
  */
-PNG_EXPORTA(99, void, png_data_freer, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED);
+PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int freer, png_uint_32 mask));
 
 /* Assignments for png_data_freer */
 #define PNG_DESTROY_WILL_FREE_DATA 1
@@ -2240,6 +1786,8 @@
 #else
 /* Fatal error in PNG image of libpng - can't continue */
 PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN);
+#  define png_error(s1,s2) png_err(s1)
+#  define png_chunk_error(s1,s2) png_err(s1)
 #endif
 
 #ifdef PNG_WARNINGS_SUPPORTED
@@ -2250,6 +1798,9 @@
 /* Non-fatal error in libpng, chunk name is prepended to message. */
 PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
     png_const_charp warning_message));
+#else
+#  define png_warning(s1,s2) ((void)(s1))
+#  define png_chunk_warning(s1,s2) ((void)(s1))
 #endif
 
 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
@@ -2367,7 +1918,7 @@
 PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
     (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
-#endif /* PNG_EASY_ACCESS_SUPPORTED */
+#endif /* EASY_ACCESS */
 
 #ifdef PNG_READ_SUPPORTED
 /* Returns pointer to signature string read from PNG header */
@@ -2619,7 +2170,7 @@
 PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
     png_inforp info_ptr, int unit,
     png_const_charp swidth, png_const_charp sheight));
-#endif /* PNG_sCAL_SUPPORTED */
+#endif /* sCAL */
 
 #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
 /* Provide the default handling for all unknown chunks or, optionally, for
@@ -2760,11 +2311,15 @@
 
 #ifdef PNG_INFO_IMAGE_SUPPORTED
 /* The "params" pointer is currently not used and is for future expansion. */
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr,
     int transforms, png_voidp params));
+#endif
+#ifdef PNG_WRITE_SUPPORTED
 PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr,
     int transforms, png_voidp params));
 #endif
+#endif
 
 PNG_EXPORT(180, png_const_charp, png_get_copyright,
     (png_const_structrp png_ptr));
@@ -2843,8 +2398,8 @@
 PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr,
     png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
     int *unit_type));
-#  endif /* PNG_pHYs_SUPPORTED */
-#endif  /* PNG_INCH_CONVERSIONS_SUPPORTED */
+#  endif /* pHYs */
+#endif  /* INCH_CONVERSIONS */
 
 /* Added in libpng-1.4.0 */
 #ifdef PNG_IO_STATE_SUPPORTED
@@ -2867,7 +2422,7 @@
 #  define PNG_IO_CHUNK_CRC   0x0080   /* currently at the chunk crc */
 #  define PNG_IO_MASK_OP     0x000f   /* current operation: reading/writing */
 #  define PNG_IO_MASK_LOC    0x00f0   /* current location: sig/hdr/data/crc */
-#endif /* ?PNG_IO_STATE_SUPPORTED */
+#endif /* IO_STATE */
 
 /* Interlace support.  The following macros are always defined so that if
  * libpng interlace handling is turned off the macros may be used to handle
@@ -2951,27 +2506,29 @@
            * (png_uint_16)(alpha)                         \
            + (png_uint_16)(bg)*(png_uint_16)(255          \
            - (png_uint_16)(alpha)) + 128);                \
-       (composite) = (png_byte)((temp + (temp >> 8)) >> 8); }
+       (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); }
 
 #  define png_composite_16(composite, fg, alpha, bg)       \
      { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg)  \
            * (png_uint_32)(alpha)                          \
            + (png_uint_32)(bg)*(65535                      \
            - (png_uint_32)(alpha)) + 32768);               \
-       (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); }
+       (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); }
 
 #else  /* Standard method using integer division */
 
-#  define png_composite(composite, fg, alpha, bg)                          \
-     (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) +  \
-     (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) +       \
-     127) / 255)
+#  define png_composite(composite, fg, alpha, bg)                        \
+     (composite) =                                                       \
+         (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) +  \
+         (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \
+         127) / 255))
 
 #  define png_composite_16(composite, fg, alpha, bg)                         \
-     (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
-     (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) +         \
-     32767) / 65535)
-#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+     (composite) =                                                           \
+         (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+         (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) +     \
+         32767) / 65535))
+#endif /* READ_COMPOSITE_NODIV */
 
 #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
 PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
@@ -3021,7 +2578,7 @@
 
 #  define PNG_get_int_32(buf) \
      ((png_int_32)((*(buf) & 0x80) \
-      ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffffL) + 1)) \
+      ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \
       : (png_int_32)png_get_uint_32(buf)))
 
    /* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h,
@@ -3041,8 +2598,17 @@
 #  endif
 #endif
 
+#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+PNG_EXPORT(242, void, png_set_check_for_invalid_index,
+    (png_structrp png_ptr, int allowed));
+#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr,
+    png_const_infop info_ptr));
+#  endif
+#endif /* CHECK_FOR_INVALID_INDEX */
+
 /*******************************************************************************
- *  SIMPLIFIED API
+ * Section 5: SIMPLIFIED API
  *******************************************************************************
  *
  * Please read the documentation in libpng-manual.txt (TODO: write said
@@ -3058,8 +2624,9 @@
  *
  * To read a PNG file using the simplified API:
  *
- * 1) Declare a 'png_image' structure (see below) on the stack and set the
- *    version field to PNG_IMAGE_VERSION.
+ * 1) Declare a 'png_image' structure (see below) on the stack, set the
+ *    version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL
+ *    (this is REQUIRED, your program may crash if you don't do it.)
  * 2) Call the appropriate png_image_begin_read... function.
  * 3) Set the png_image 'format' member to the required sample format.
  * 4) Allocate a buffer for the image and, if required, the color-map.
@@ -3086,6 +2653,9 @@
  * when it is being read or defines the in-memory format of an image that you
  * need to write:
  */
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \
+    defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+
 #define PNG_IMAGE_VERSION 1
 
 typedef struct png_control *png_controlp;
@@ -3112,7 +2682,7 @@
 #  define PNG_IMAGE_WARNING 1
 #  define PNG_IMAGE_ERROR 2
    /*
-    * The result is a two bit code such that a value more than 1 indicates
+    * The result is a two-bit code such that a value more than 1 indicates
     * a failure in the API just called:
     *
     *    0 - no warning or error
@@ -3185,7 +2755,7 @@
  * called to read or write the color-map and set the format correctly for the
  * image data.  Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly!
  *
- * NOTE: libpng can be built with particular features disabled, if you see
+ * NOTE: libpng can be built with particular features disabled. If you see
  * compiler errors because the definition of one of the following flags has been
  * compiled out it is because libpng does not have the required support.  It is
  * possible, however, for the libpng configuration to enable the format on just
@@ -3197,7 +2767,7 @@
  */
 #define PNG_FORMAT_FLAG_ALPHA    0x01U /* format with an alpha channel */
 #define PNG_FORMAT_FLAG_COLOR    0x02U /* color format: otherwise grayscale */
-#define PNG_FORMAT_FLAG_LINEAR   0x04U /* 2 byte channels else 1 byte */
+#define PNG_FORMAT_FLAG_LINEAR   0x04U /* 2-byte channels else 1-byte */
 #define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */
 
 #ifdef PNG_FORMAT_BGR_SUPPORTED
@@ -3397,7 +2967,7 @@
 PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image,
    FILE* file));
    /* The PNG header is read from the stdio FILE object. */
-#endif /* PNG_STDIO_SUPPORTED */
+#endif /* STDIO */
 
 PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
    png_const_voidp memory, png_size_t size));
@@ -3442,7 +3012,7 @@
    /* Free any data allocated by libpng in image->opaque, setting the pointer to
     * NULL.  May be called at any time after the structure is initialized.
     */
-#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
+#endif /* SIMPLIFIED_READ */
 
 #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
 #ifdef PNG_STDIO_SUPPORTED
@@ -3484,27 +3054,21 @@
  *
  * With all APIs row_stride is handled as in the read APIs - it is the spacing
  * from one row to the next in component sized units (1 or 2 bytes) and if
- * negative indicates a bottom-up row layout in the buffer.
+ * negative indicates a bottom-up row layout in the buffer.  If row_stride is zero,
+ * libpng will calculate it for you from the image width and number of channels.
  *
- * Note that the write API does not support interlacing or sub-8-bit pixels.
+ * Note that the write API does not support interlacing, sub-8-bit pixels, indexed
+ * PNG (color_type 3) or most ancillary chunks.
  */
-#endif /* PNG_STDIO_SUPPORTED */
-#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */
+#endif /* STDIO */
+#endif /* SIMPLIFIED_WRITE */
 /*******************************************************************************
  *  END OF SIMPLIFIED API
  ******************************************************************************/
-
-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
-PNG_EXPORT(242, void, png_set_check_for_invalid_index,
-    (png_structrp png_ptr, int allowed));
-#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
-PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr,
-    png_const_infop info_ptr));
-#  endif
-#endif /* CHECK_FOR_INVALID_INDEX */
+#endif /* SIMPLIFIED_{READ|WRITE} */
 
 /*******************************************************************************
- *  IMPLEMENTATION OPTIONS
+ * Section 6: IMPLEMENTATION OPTIONS
  *******************************************************************************
  *
  * Support for arbitrary implementation-specific optimizations.  The API allows
@@ -3529,7 +3093,8 @@
 #  define PNG_ARM_NEON   0 /* HARDWARE: ARM Neon SIMD instructions supported */
 #endif
 #define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
-#define PNG_OPTION_NEXT  4 /* Next option - numbers must be even */
+#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */
+#define PNG_OPTION_NEXT  6 /* Next option - numbers must be even */
 
 /* Return values: NOTE: there are four values and 'off' is *not* zero */
 #define PNG_OPTION_UNSET   0 /* Unset - defaults to off */
@@ -3539,19 +3104,18 @@
 
 PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option,
    int onoff));
-#endif
+#endif /* SET_OPTION */
 
 /*******************************************************************************
- *  END OF HARDWARE OPTIONS
+ *  END OF HARDWARE AND SOFTWARE OPTIONS
  ******************************************************************************/
 
-/* Maintainer: Put new public prototypes here ^, in libpng.3, and project
- * defs, scripts/pnglibconf.h, and scripts/pnglibconf.h.prebuilt
+/* Maintainer: Put new public prototypes here ^, in libpng.3, in project
+ * defs, and in scripts/symbols.def.
  */
 
 /* The last ordinal number (this is the *last* one already used; the next
- * one to use is one more than this.)  Maintainer, remember to add an entry to
- * scripts/symbols.def as well.
+ * one to use is one more than this.)
  */
 #ifdef PNG_EXPORT_LAST_ORDINAL
   PNG_EXPORT_LAST_ORDINAL(244);
diff --git a/third_party/lpng_v163/pngconf.h b/third_party/libpng/pngconf.h
similarity index 91%
rename from third_party/lpng_v163/pngconf.h
rename to third_party/libpng/pngconf.h
index 02f74e2..92f2500 100644
--- a/third_party/lpng_v163/pngconf.h
+++ b/third_party/libpng/pngconf.h
@@ -1,617 +1,622 @@
-

-/* pngconf.h - machine configurable file for libpng

- *

- * libpng version 1.6.3 - July 18, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- */

-

-/* Any machine specific code is near the front of this file, so if you

- * are configuring libpng for a machine, you may want to read the section

- * starting here down to where it starts to typedef png_color, png_text,

- * and png_info.

- */

-

-#ifndef PNGCONF_H

-#define PNGCONF_H

-

-/* To do: Do all of this in scripts/pnglibconf.dfa */

-#ifdef PNG_SAFE_LIMITS_SUPPORTED

-#  ifdef PNG_USER_WIDTH_MAX

-#    undef PNG_USER_WIDTH_MAX

-#    define PNG_USER_WIDTH_MAX 1000000L

-#  endif

-#  ifdef PNG_USER_HEIGHT_MAX

-#    undef PNG_USER_HEIGHT_MAX

-#    define PNG_USER_HEIGHT_MAX 1000000L

-#  endif

-#  ifdef PNG_USER_CHUNK_MALLOC_MAX

-#    undef PNG_USER_CHUNK_MALLOC_MAX

-#    define PNG_USER_CHUNK_MALLOC_MAX 4000000L

-#  endif

-#  ifdef PNG_USER_CHUNK_CACHE_MAX

-#    undef PNG_USER_CHUNK_CACHE_MAX

-#    define PNG_USER_CHUNK_CACHE_MAX 128

-#  endif

-#endif

-

-#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */

-

-/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C

- * compiler for correct compilation.  The following header files are required by

- * the standard.  If your compiler doesn't provide these header files, or they

- * do not match the standard, you will need to provide/improve them.

- */

-#include <limits.h>

-#include <stddef.h>

-

-/* Library header files.  These header files are all defined by ISOC90; libpng

- * expects conformant implementations, however, an ISOC90 conformant system need

- * not provide these header files if the functionality cannot be implemented.

- * In this case it will be necessary to disable the relevant parts of libpng in

- * the build of pnglibconf.h.

- *

- * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not

- * include this unnecessary header file.

- */

-

-#ifdef PNG_STDIO_SUPPORTED

-   /* Required for the definition of FILE: */

-#  include <stdio.h>

-#endif

-

-#ifdef PNG_SETJMP_SUPPORTED

-   /* Required for the definition of jmp_buf and the declaration of longjmp: */

-#  include <setjmp.h>

-#endif

-

-#ifdef PNG_CONVERT_tIME_SUPPORTED

-   /* Required for struct tm: */

-#  include <time.h>

-#endif

-

-#endif /* PNG_BUILDING_SYMBOL_TABLE */

-

-/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using

- * PNG_NO_CONST; this is no longer supported except for data declarations which

- * apparently still cause problems in 2011 on some compilers.

- */

-#define PNG_CONST const /* backward compatibility only */

-

-/* This controls optimization of the reading of 16 and 32 bit values

- * from PNG files.  It can be set on a per-app-file basis - it

- * just changes whether a macro is used when the function is called.

- * The library builder sets the default; if read functions are not

- * built into the library the macro implementation is forced on.

- */

-#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED

-#  define PNG_USE_READ_MACROS

-#endif

-#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)

-#  if PNG_DEFAULT_READ_MACROS

-#    define PNG_USE_READ_MACROS

-#  endif

-#endif

-

-/* COMPILER SPECIFIC OPTIONS.

- *

- * These options are provided so that a variety of difficult compilers

- * can be used.  Some are fixed at build time (e.g. PNG_API_RULE

- * below) but still have compiler specific implementations, others

- * may be changed on a per-file basis when compiling against libpng.

- */

-

-/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect

- * against legacy (pre ISOC90) compilers that did not understand function

- * prototypes.  It is not required for modern C compilers.

- */

-#ifndef PNGARG

-#  define PNGARG(arglist) arglist

-#endif

-

-/* Function calling conventions.

- * =============================

- * Normally it is not necessary to specify to the compiler how to call

- * a function - it just does it - however on x86 systems derived from

- * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems

- * and some others) there are multiple ways to call a function and the

- * default can be changed on the compiler command line.  For this reason

- * libpng specifies the calling convention of every exported function and

- * every function called via a user supplied function pointer.  This is

- * done in this file by defining the following macros:

- *

- * PNGAPI    Calling convention for exported functions.

- * PNGCBAPI  Calling convention for user provided (callback) functions.

- * PNGCAPI   Calling convention used by the ANSI-C library (required

- *           for longjmp callbacks and sometimes used internally to

- *           specify the calling convention for zlib).

- *

- * These macros should never be overridden.  If it is necessary to

- * change calling convention in a private build this can be done

- * by setting PNG_API_RULE (which defaults to 0) to one of the values

- * below to select the correct 'API' variants.

- *

- * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.

- *                This is correct in every known environment.

- * PNG_API_RULE=1 Use the operating system convention for PNGAPI and

- *                the 'C' calling convention (from PNGCAPI) for

- *                callbacks (PNGCBAPI).  This is no longer required

- *                in any known environment - if it has to be used

- *                please post an explanation of the problem to the

- *                libpng mailing list.

- *

- * These cases only differ if the operating system does not use the C

- * calling convention, at present this just means the above cases

- * (x86 DOS/Windows sytems) and, even then, this does not apply to

- * Cygwin running on those systems.

- *

- * Note that the value must be defined in pnglibconf.h so that what

- * the application uses to call the library matches the conventions

- * set when building the library.

- */

-

-/* Symbol export

- * =============

- * When building a shared library it is almost always necessary to tell

- * the compiler which symbols to export.  The png.h macro 'PNG_EXPORT'

- * is used to mark the symbols.  On some systems these symbols can be

- * extracted at link time and need no special processing by the compiler,

- * on other systems the symbols are flagged by the compiler and just

- * the declaration requires a special tag applied (unfortunately) in a

- * compiler dependent way.  Some systems can do either.

- *

- * A small number of older systems also require a symbol from a DLL to

- * be flagged to the program that calls it.  This is a problem because

- * we do not know in the header file included by application code that

- * the symbol will come from a shared library, as opposed to a statically

- * linked one.  For this reason the application must tell us by setting

- * the magic flag PNG_USE_DLL to turn on the special processing before

- * it includes png.h.

- *

- * Four additional macros are used to make this happen:

- *

- * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from

- *            the build or imported if PNG_USE_DLL is set - compiler

- *            and system specific.

- *

- * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to

- *                       'type', compiler specific.

- *

- * PNG_DLL_EXPORT Set to the magic to use during a libpng build to

- *                make a symbol exported from the DLL.  Not used in the

- *                public header files; see pngpriv.h for how it is used

- *                in the libpng build.

- *

- * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come

- *                from a DLL - used to define PNG_IMPEXP when

- *                PNG_USE_DLL is set.

- */

-

-/* System specific discovery.

- * ==========================

- * This code is used at build time to find PNG_IMPEXP, the API settings

- * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL

- * import processing is possible.  On Windows systems it also sets

- * compiler-specific macros to the values required to change the calling

- * conventions of the various functions.

- */

-#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\

-    defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)

-  /* Windows system (DOS doesn't support DLLs).  Includes builds under Cygwin or

-   * MinGW on any architecture currently supported by Windows.  Also includes

-   * Watcom builds but these need special treatment because they are not

-   * compatible with GCC or Visual C because of different calling conventions.

-   */

-#  if PNG_API_RULE == 2

-    /* If this line results in an error, either because __watcall is not

-     * understood or because of a redefine just below you cannot use *this*

-     * build of the library with the compiler you are using.  *This* build was

-     * build using Watcom and applications must also be built using Watcom!

-     */

-#    define PNGCAPI __watcall

-#  endif

-

-#  if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))

-#    define PNGCAPI __cdecl

-#    if PNG_API_RULE == 1

-       /* If this line results in an error __stdcall is not understood and

-        * PNG_API_RULE should not have been set to '1'.

-        */

-#      define PNGAPI __stdcall

-#    endif

-#  else

-    /* An older compiler, or one not detected (erroneously) above,

-     * if necessary override on the command line to get the correct

-     * variants for the compiler.

-     */

-#    ifndef PNGCAPI

-#      define PNGCAPI _cdecl

-#    endif

-#    if PNG_API_RULE == 1 && !defined(PNGAPI)

-#      define PNGAPI _stdcall

-#    endif

-#  endif /* compiler/api */

-

-  /* NOTE: PNGCBAPI always defaults to PNGCAPI. */

-

-#  if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)

-#     error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed"

-#  endif

-

-#  if (defined(_MSC_VER) && _MSC_VER < 800) ||\

-      (defined(__BORLANDC__) && __BORLANDC__ < 0x500)

-    /* older Borland and MSC

-     * compilers used '__export' and required this to be after

-     * the type.

-     */

-#    ifndef PNG_EXPORT_TYPE

-#      define PNG_EXPORT_TYPE(type) type PNG_IMPEXP

-#    endif

-#    define PNG_DLL_EXPORT __export

-#  else /* newer compiler */

-#    define PNG_DLL_EXPORT __declspec(dllexport)

-#    ifndef PNG_DLL_IMPORT

-#      define PNG_DLL_IMPORT __declspec(dllimport)

-#    endif

-#  endif /* compiler */

-

-#else /* !Windows */

-#  if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)

-#    define PNGAPI _System

-#  else /* !Windows/x86 && !OS/2 */

-    /* Use the defaults, or define PNG*API on the command line (but

-     * this will have to be done for every compile!)

-     */

-#  endif /* other system, !OS/2 */

-#endif /* !Windows/x86 */

-

-/* Now do all the defaulting . */

-#ifndef PNGCAPI

-#  define PNGCAPI

-#endif

-#ifndef PNGCBAPI

-#  define PNGCBAPI PNGCAPI

-#endif

-#ifndef PNGAPI

-#  define PNGAPI PNGCAPI

-#endif

-

-/* PNG_IMPEXP may be set on the compilation system command line or (if not set)

- * then in an internal header file when building the library, otherwise (when

- * using the library) it is set here.

- */

-#ifndef PNG_IMPEXP

-#  if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)

-     /* This forces use of a DLL, disallowing static linking */

-#    define PNG_IMPEXP PNG_DLL_IMPORT

-#  endif

-

-#  ifndef PNG_IMPEXP

-#    define PNG_IMPEXP

-#  endif

-#endif

-

-/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat

- * 'attributes' as a storage class - the attributes go at the start of the

- * function definition, and attributes are always appended regardless of the

- * compiler.  This considerably simplifies these macros but may cause problems

- * if any compilers both need function attributes and fail to handle them as

- * a storage class (this is unlikely.)

- */

-#ifndef PNG_FUNCTION

-#  define PNG_FUNCTION(type, name, args, attributes) attributes type name args

-#endif

-

-#ifndef PNG_EXPORT_TYPE

-#  define PNG_EXPORT_TYPE(type) PNG_IMPEXP type

-#endif

-

-   /* The ordinal value is only relevant when preprocessing png.h for symbol

-    * table entries, so we discard it here.  See the .dfn files in the

-    * scripts directory.

-    */

-#ifndef PNG_EXPORTA

-

-#  define PNG_EXPORTA(ordinal, type, name, args, attributes)\

-      PNG_FUNCTION(PNG_EXPORT_TYPE(type),(PNGAPI name),PNGARG(args), \

-        extern attributes)

-#endif

-

-/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,

- * so make something non-empty to satisfy the requirement:

- */

-#define PNG_EMPTY /*empty list*/

-

-#define PNG_EXPORT(ordinal, type, name, args)\

-   PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)

-

-/* Use PNG_REMOVED to comment out a removed interface. */

-#ifndef PNG_REMOVED

-#  define PNG_REMOVED(ordinal, type, name, args, attributes)

-#endif

-

-#ifndef PNG_CALLBACK

-#  define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)

-#endif

-

-/* Support for compiler specific function attributes.  These are used

- * so that where compiler support is available incorrect use of API

- * functions in png.h will generate compiler warnings.

- *

- * Added at libpng-1.2.41.

- */

-

-#ifndef PNG_NO_PEDANTIC_WARNINGS

-#  ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED

-#    define PNG_PEDANTIC_WARNINGS_SUPPORTED

-#  endif

-#endif

-

-#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED

-  /* Support for compiler specific function attributes.  These are used

-   * so that where compiler support is available, incorrect use of API

-   * functions in png.h will generate compiler warnings.  Added at libpng

-   * version 1.2.41.  Disabling these removes the warnings but may also produce

-   * less efficient code.

-   */

-#  if defined(__GNUC__)

-#    ifndef PNG_USE_RESULT

-#      define PNG_USE_RESULT __attribute__((__warn_unused_result__))

-#    endif

-#    ifndef PNG_NORETURN

-#      define PNG_NORETURN   __attribute__((__noreturn__))

-#    endif

-#    if __GNUC__ >= 3

-#      ifndef PNG_ALLOCATED

-#        define PNG_ALLOCATED  __attribute__((__malloc__))

-#      endif

-#      ifndef PNG_DEPRECATED

-#        define PNG_DEPRECATED __attribute__((__deprecated__))

-#      endif

-#      ifndef PNG_PRIVATE

-#        if 0 /* Doesn't work so we use deprecated instead*/

-#          define PNG_PRIVATE \

-            __attribute__((warning("This function is not exported by libpng.")))

-#        else

-#          define PNG_PRIVATE \

-            __attribute__((__deprecated__))

-#        endif

-#      endif

-#      if ((__GNUC__ != 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1))

-#        ifndef PNG_RESTRICT

-#          define PNG_RESTRICT __restrict

-#        endif

-#      endif /*  __GNUC__ == 3.0 */

-#    endif /*  __GNUC__ >= 3 */

-

-#  elif defined(_MSC_VER)  && (_MSC_VER >= 1300)

-#    ifndef PNG_USE_RESULT

-#      define PNG_USE_RESULT /* not supported */

-#    endif

-#    ifndef PNG_NORETURN

-#      define PNG_NORETURN   __declspec(noreturn)

-#    endif

-#    ifndef PNG_ALLOCATED

-#      if (_MSC_VER >= 1400)

-#        define PNG_ALLOCATED __declspec(restrict)

-#      endif

-#    endif

-#    ifndef PNG_DEPRECATED

-#      define PNG_DEPRECATED __declspec(deprecated)

-#    endif

-#    ifndef PNG_PRIVATE

-#      define PNG_PRIVATE __declspec(deprecated)

-#    endif

-#    ifndef PNG_RESTRICT

-#      if (_MSC_VER >= 1400)

-#        define PNG_RESTRICT __restrict

-#      endif

-#    endif

-

-#  elif defined(__WATCOMC__)

-#    ifndef PNG_RESTRICT

-#      define PNG_RESTRICT __restrict

-#    endif

-#  endif /* _MSC_VER */

-#endif /* PNG_PEDANTIC_WARNINGS */

-

-#ifndef PNG_DEPRECATED

-#  define PNG_DEPRECATED  /* Use of this function is deprecated */

-#endif

-#ifndef PNG_USE_RESULT

-#  define PNG_USE_RESULT  /* The result of this function must be checked */

-#endif

-#ifndef PNG_NORETURN

-#  define PNG_NORETURN    /* This function does not return */

-#endif

-#ifndef PNG_ALLOCATED

-#  define PNG_ALLOCATED   /* The result of the function is new memory */

-#endif

-#ifndef PNG_PRIVATE

-#  define PNG_PRIVATE     /* This is a private libpng function */

-#endif

-#ifndef PNG_RESTRICT

-#  define PNG_RESTRICT    /* The C99 "restrict" feature */

-#endif

-#ifndef PNG_FP_EXPORT     /* A floating point API. */

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-#     define PNG_FP_EXPORT(ordinal, type, name, args)\

-         PNG_EXPORT(ordinal, type, name, args);

-#  else                   /* No floating point APIs */

-#     define PNG_FP_EXPORT(ordinal, type, name, args)

-#  endif

-#endif

-#ifndef PNG_FIXED_EXPORT  /* A fixed point API. */

-#  ifdef PNG_FIXED_POINT_SUPPORTED

-#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\

-         PNG_EXPORT(ordinal, type, name, args);

-#  else                   /* No fixed point APIs */

-#     define PNG_FIXED_EXPORT(ordinal, type, name, args)

-#  endif

-#endif

-

-#ifndef PNG_BUILDING_SYMBOL_TABLE

-/* Some typedefs to get us started.  These should be safe on most of the common

- * platforms.

- *

- * png_uint_32 and png_int_32 may, currently, be larger than required to hold a

- * 32-bit value however this is not normally advisable.

- *

- * png_uint_16 and png_int_16 should always be two bytes in size - this is

- * verified at library build time.

- *

- * png_byte must always be one byte in size.

- *

- * The checks below use constants from limits.h, as defined by the ISOC90

- * standard.

- */

-#if CHAR_BIT == 8 && UCHAR_MAX == 255

-   typedef unsigned char png_byte;

-#else

-#  error "libpng requires 8 bit bytes"

-#endif

-

-#if INT_MIN == -32768 && INT_MAX == 32767

-   typedef int png_int_16;

-#elif SHRT_MIN == -32768 && SHRT_MAX == 32767

-   typedef short png_int_16;

-#else

-#  error "libpng requires a signed 16 bit type"

-#endif

-

-#if UINT_MAX == 65535

-   typedef unsigned int png_uint_16;

-#elif USHRT_MAX == 65535

-   typedef unsigned short png_uint_16;

-#else

-#  error "libpng requires an unsigned 16 bit type"

-#endif

-

-#if INT_MIN < -2147483646 && INT_MAX > 2147483646

-   typedef int png_int_32;

-#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646

-   typedef long int png_int_32;

-#else

-#  error "libpng requires a signed 32 bit (or more) type"

-#endif

-

-#if UINT_MAX > 4294967294

-   typedef unsigned int png_uint_32;

-#elif ULONG_MAX > 4294967294

-   typedef unsigned long int png_uint_32;

-#else

-#  error "libpng requires an unsigned 32 bit (or more) type"

-#endif

-

-/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however,

- * requires an ISOC90 compiler and relies on consistent behavior of sizeof.

- */

-typedef size_t png_size_t;

-typedef ptrdiff_t png_ptrdiff_t;

-

-/* libpng needs to know the maximum value of 'size_t' and this controls the

- * definition of png_alloc_size_t, below.  This maximum value of size_t limits

- * but does not control the maximum allocations the library makes - there is

- * direct application control of this through png_set_user_limits().

- */

-#ifndef PNG_SMALL_SIZE_T

-   /* Compiler specific tests for systems where size_t is known to be less than

-    * 32 bits (some of these systems may no longer work because of the lack of

-    * 'far' support; see above.)

-    */

-#  if (defined(__TURBOC__) && !defined(__FLAT__)) ||\

-   (defined(_MSC_VER) && defined(MAXSEG_64K))

-#     define PNG_SMALL_SIZE_T

-#  endif

-#endif

-

-/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no

- * smaller than png_uint_32.  Casts from png_size_t or png_uint_32 to

- * png_alloc_size_t are not necessary; in fact, it is recommended not to use

- * them at all so that the compiler can complain when something turns out to be

- * problematic.

- *

- * Casts in the other direction (from png_alloc_size_t to png_size_t or

- * png_uint_32) should be explicitly applied; however, we do not expect to

- * encounter practical situations that require such conversions.

- *

- * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than

- * 4294967295 - i.e. less than the maximum value of png_uint_32.

- */

-#ifdef PNG_SMALL_SIZE_T

-   typedef png_uint_32 png_alloc_size_t;

-#else

-   typedef png_size_t png_alloc_size_t;

-#endif

-

-/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler

- * implementations of Intel CPU specific support of user-mode segmented address

- * spaces, where 16-bit pointers address more than 65536 bytes of memory using

- * separate 'segment' registers.  The implementation requires two different

- * types of pointer (only one of which includes the segment value.)

- *

- * If required this support is available in version 1.2 of libpng and may be

- * available in versions through 1.5, although the correctness of the code has

- * not been verified recently.

- */

-

-/* Typedef for floating-point numbers that are converted to fixed-point with a

- * multiple of 100,000, e.g., gamma

- */

-typedef png_int_32 png_fixed_point;

-

-/* Add typedefs for pointers */

-typedef void                  * png_voidp;

-typedef const void            * png_const_voidp;

-typedef png_byte              * png_bytep;

-typedef const png_byte        * png_const_bytep;

-typedef png_uint_32           * png_uint_32p;

-typedef const png_uint_32     * png_const_uint_32p;

-typedef png_int_32            * png_int_32p;

-typedef const png_int_32      * png_const_int_32p;

-typedef png_uint_16           * png_uint_16p;

-typedef const png_uint_16     * png_const_uint_16p;

-typedef png_int_16            * png_int_16p;

-typedef const png_int_16      * png_const_int_16p;

-typedef char                  * png_charp;

-typedef const char            * png_const_charp;

-typedef png_fixed_point       * png_fixed_point_p;

-typedef const png_fixed_point * png_const_fixed_point_p;

-typedef png_size_t            * png_size_tp;

-typedef const png_size_t      * png_const_size_tp;

-

-#ifdef PNG_STDIO_SUPPORTED

-typedef FILE            * png_FILE_p;

-#endif

-

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-typedef double       * png_doublep;

-typedef const double * png_const_doublep;

-#endif

-

-/* Pointers to pointers; i.e. arrays */

-typedef png_byte        * * png_bytepp;

-typedef png_uint_32     * * png_uint_32pp;

-typedef png_int_32      * * png_int_32pp;

-typedef png_uint_16     * * png_uint_16pp;

-typedef png_int_16      * * png_int_16pp;

-typedef const char      * * png_const_charpp;

-typedef char            * * png_charpp;

-typedef png_fixed_point * * png_fixed_point_pp;

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-typedef double          * * png_doublepp;

-#endif

-

-/* Pointers to pointers to pointers; i.e., pointer to array */

-typedef char            * * * png_charppp;

-

-#endif /* PNG_BUILDING_SYMBOL_TABLE */

-

-#endif /* PNGCONF_H */

+
+/* pngconf.h - machine configurable file for libpng
+ *
+ * libpng version 1.6.20, December 3, 2015
+ *
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Any machine specific code is near the front of this file, so if you
+ * are configuring libpng for a machine, you may want to read the section
+ * starting here down to where it starts to typedef png_color, png_text,
+ * and png_info.
+ */
+
+#ifndef PNGCONF_H
+#define PNGCONF_H
+
+#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */
+
+/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C
+ * compiler for correct compilation.  The following header files are required by
+ * the standard.  If your compiler doesn't provide these header files, or they
+ * do not match the standard, you will need to provide/improve them.
+ */
+#include <limits.h>
+#include <stddef.h>
+
+/* Library header files.  These header files are all defined by ISOC90; libpng
+ * expects conformant implementations, however, an ISOC90 conformant system need
+ * not provide these header files if the functionality cannot be implemented.
+ * In this case it will be necessary to disable the relevant parts of libpng in
+ * the build of pnglibconf.h.
+ *
+ * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not
+ * include this unnecessary header file.
+ */
+
+#ifdef PNG_STDIO_SUPPORTED
+   /* Required for the definition of FILE: */
+#  include <stdio.h>
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* Required for the definition of jmp_buf and the declaration of longjmp: */
+#  include <setjmp.h>
+#endif
+
+#ifdef PNG_CONVERT_tIME_SUPPORTED
+   /* Required for struct tm: */
+#  include <time.h>
+#endif
+
+#endif /* PNG_BUILDING_SYMBOL_TABLE */
+
+/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using
+ * PNG_NO_CONST; this is no longer supported except for data declarations which
+ * apparently still cause problems in 2011 on some compilers.
+ */
+#define PNG_CONST const /* backward compatibility only */
+
+/* This controls optimization of the reading of 16-bit and 32-bit values
+ * from PNG files.  It can be set on a per-app-file basis - it
+ * just changes whether a macro is used when the function is called.
+ * The library builder sets the default; if read functions are not
+ * built into the library the macro implementation is forced on.
+ */
+#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED
+#  define PNG_USE_READ_MACROS
+#endif
+#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)
+#  if PNG_DEFAULT_READ_MACROS
+#    define PNG_USE_READ_MACROS
+#  endif
+#endif
+
+/* COMPILER SPECIFIC OPTIONS.
+ *
+ * These options are provided so that a variety of difficult compilers
+ * can be used.  Some are fixed at build time (e.g. PNG_API_RULE
+ * below) but still have compiler specific implementations, others
+ * may be changed on a per-file basis when compiling against libpng.
+ */
+
+/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect
+ * against legacy (pre ISOC90) compilers that did not understand function
+ * prototypes.  It is not required for modern C compilers.
+ */
+#ifndef PNGARG
+#  define PNGARG(arglist) arglist
+#endif
+
+/* Function calling conventions.
+ * =============================
+ * Normally it is not necessary to specify to the compiler how to call
+ * a function - it just does it - however on x86 systems derived from
+ * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems
+ * and some others) there are multiple ways to call a function and the
+ * default can be changed on the compiler command line.  For this reason
+ * libpng specifies the calling convention of every exported function and
+ * every function called via a user supplied function pointer.  This is
+ * done in this file by defining the following macros:
+ *
+ * PNGAPI    Calling convention for exported functions.
+ * PNGCBAPI  Calling convention for user provided (callback) functions.
+ * PNGCAPI   Calling convention used by the ANSI-C library (required
+ *           for longjmp callbacks and sometimes used internally to
+ *           specify the calling convention for zlib).
+ *
+ * These macros should never be overridden.  If it is necessary to
+ * change calling convention in a private build this can be done
+ * by setting PNG_API_RULE (which defaults to 0) to one of the values
+ * below to select the correct 'API' variants.
+ *
+ * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.
+ *                This is correct in every known environment.
+ * PNG_API_RULE=1 Use the operating system convention for PNGAPI and
+ *                the 'C' calling convention (from PNGCAPI) for
+ *                callbacks (PNGCBAPI).  This is no longer required
+ *                in any known environment - if it has to be used
+ *                please post an explanation of the problem to the
+ *                libpng mailing list.
+ *
+ * These cases only differ if the operating system does not use the C
+ * calling convention, at present this just means the above cases
+ * (x86 DOS/Windows sytems) and, even then, this does not apply to
+ * Cygwin running on those systems.
+ *
+ * Note that the value must be defined in pnglibconf.h so that what
+ * the application uses to call the library matches the conventions
+ * set when building the library.
+ */
+
+/* Symbol export
+ * =============
+ * When building a shared library it is almost always necessary to tell
+ * the compiler which symbols to export.  The png.h macro 'PNG_EXPORT'
+ * is used to mark the symbols.  On some systems these symbols can be
+ * extracted at link time and need no special processing by the compiler,
+ * on other systems the symbols are flagged by the compiler and just
+ * the declaration requires a special tag applied (unfortunately) in a
+ * compiler dependent way.  Some systems can do either.
+ *
+ * A small number of older systems also require a symbol from a DLL to
+ * be flagged to the program that calls it.  This is a problem because
+ * we do not know in the header file included by application code that
+ * the symbol will come from a shared library, as opposed to a statically
+ * linked one.  For this reason the application must tell us by setting
+ * the magic flag PNG_USE_DLL to turn on the special processing before
+ * it includes png.h.
+ *
+ * Four additional macros are used to make this happen:
+ *
+ * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from
+ *            the build or imported if PNG_USE_DLL is set - compiler
+ *            and system specific.
+ *
+ * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to
+ *                       'type', compiler specific.
+ *
+ * PNG_DLL_EXPORT Set to the magic to use during a libpng build to
+ *                make a symbol exported from the DLL.  Not used in the
+ *                public header files; see pngpriv.h for how it is used
+ *                in the libpng build.
+ *
+ * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come
+ *                from a DLL - used to define PNG_IMPEXP when
+ *                PNG_USE_DLL is set.
+ */
+
+/* System specific discovery.
+ * ==========================
+ * This code is used at build time to find PNG_IMPEXP, the API settings
+ * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL
+ * import processing is possible.  On Windows systems it also sets
+ * compiler-specific macros to the values required to change the calling
+ * conventions of the various functions.
+ */
+#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\
+    defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+  /* Windows system (DOS doesn't support DLLs).  Includes builds under Cygwin or
+   * MinGW on any architecture currently supported by Windows.  Also includes
+   * Watcom builds but these need special treatment because they are not
+   * compatible with GCC or Visual C because of different calling conventions.
+   */
+#  if PNG_API_RULE == 2
+    /* If this line results in an error, either because __watcall is not
+     * understood or because of a redefine just below you cannot use *this*
+     * build of the library with the compiler you are using.  *This* build was
+     * build using Watcom and applications must also be built using Watcom!
+     */
+#    define PNGCAPI __watcall
+#  endif
+
+#  if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
+#    define PNGCAPI __cdecl
+#    if PNG_API_RULE == 1
+       /* If this line results in an error __stdcall is not understood and
+        * PNG_API_RULE should not have been set to '1'.
+        */
+#      define PNGAPI __stdcall
+#    endif
+#  else
+    /* An older compiler, or one not detected (erroneously) above,
+     * if necessary override on the command line to get the correct
+     * variants for the compiler.
+     */
+#    ifndef PNGCAPI
+#      define PNGCAPI _cdecl
+#    endif
+#    if PNG_API_RULE == 1 && !defined(PNGAPI)
+#      define PNGAPI _stdcall
+#    endif
+#  endif /* compiler/api */
+
+  /* NOTE: PNGCBAPI always defaults to PNGCAPI. */
+
+#  if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
+#     error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed"
+#  endif
+
+#  if (defined(_MSC_VER) && _MSC_VER < 800) ||\
+      (defined(__BORLANDC__) && __BORLANDC__ < 0x500)
+    /* older Borland and MSC
+     * compilers used '__export' and required this to be after
+     * the type.
+     */
+#    ifndef PNG_EXPORT_TYPE
+#      define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
+#    endif
+#    define PNG_DLL_EXPORT __export
+#  else /* newer compiler */
+#    define PNG_DLL_EXPORT __declspec(dllexport)
+#    ifndef PNG_DLL_IMPORT
+#      define PNG_DLL_IMPORT __declspec(dllimport)
+#    endif
+#  endif /* compiler */
+
+#else /* !Windows */
+#  if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
+#    define PNGAPI _System
+#  else /* !Windows/x86 && !OS/2 */
+    /* Use the defaults, or define PNG*API on the command line (but
+     * this will have to be done for every compile!)
+     */
+#  endif /* other system, !OS/2 */
+#endif /* !Windows/x86 */
+
+/* Now do all the defaulting . */
+#ifndef PNGCAPI
+#  define PNGCAPI
+#endif
+#ifndef PNGCBAPI
+#  define PNGCBAPI PNGCAPI
+#endif
+#ifndef PNGAPI
+#  define PNGAPI PNGCAPI
+#endif
+
+/* PNG_IMPEXP may be set on the compilation system command line or (if not set)
+ * then in an internal header file when building the library, otherwise (when
+ * using the library) it is set here.
+ */
+#ifndef PNG_IMPEXP
+#  if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
+     /* This forces use of a DLL, disallowing static linking */
+#    define PNG_IMPEXP PNG_DLL_IMPORT
+#  endif
+
+#  ifndef PNG_IMPEXP
+#    define PNG_IMPEXP
+#  endif
+#endif
+
+/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat
+ * 'attributes' as a storage class - the attributes go at the start of the
+ * function definition, and attributes are always appended regardless of the
+ * compiler.  This considerably simplifies these macros but may cause problems
+ * if any compilers both need function attributes and fail to handle them as
+ * a storage class (this is unlikely.)
+ */
+#ifndef PNG_FUNCTION
+#  define PNG_FUNCTION(type, name, args, attributes) attributes type name args
+#endif
+
+#ifndef PNG_EXPORT_TYPE
+#  define PNG_EXPORT_TYPE(type) PNG_IMPEXP type
+#endif
+
+   /* The ordinal value is only relevant when preprocessing png.h for symbol
+    * table entries, so we discard it here.  See the .dfn files in the
+    * scripts directory.
+    */
+
+#ifndef PNG_EXPORTA
+#  define PNG_EXPORTA(ordinal, type, name, args, attributes) \
+      PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \
+      PNG_LINKAGE_API attributes)
+#endif
+
+/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,
+ * so make something non-empty to satisfy the requirement:
+ */
+#define PNG_EMPTY /*empty list*/
+
+#define PNG_EXPORT(ordinal, type, name, args) \
+   PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)
+
+/* Use PNG_REMOVED to comment out a removed interface. */
+#ifndef PNG_REMOVED
+#  define PNG_REMOVED(ordinal, type, name, args, attributes)
+#endif
+
+#ifndef PNG_CALLBACK
+#  define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)
+#endif
+
+/* Support for compiler specific function attributes.  These are used
+ * so that where compiler support is available incorrect use of API
+ * functions in png.h will generate compiler warnings.
+ *
+ * Added at libpng-1.2.41.
+ */
+
+#ifndef PNG_NO_PEDANTIC_WARNINGS
+#  ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED
+#    define PNG_PEDANTIC_WARNINGS_SUPPORTED
+#  endif
+#endif
+
+#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED
+  /* Support for compiler specific function attributes.  These are used
+   * so that where compiler support is available, incorrect use of API
+   * functions in png.h will generate compiler warnings.  Added at libpng
+   * version 1.2.41.  Disabling these removes the warnings but may also produce
+   * less efficient code.
+   */
+#  if defined(__clang__) && defined(__has_attribute)
+     /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
+#    if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__)
+#      define PNG_USE_RESULT __attribute__((__warn_unused_result__))
+#    endif
+#    if !defined(PNG_NORETURN) && __has_attribute(__noreturn__)
+#      define PNG_NORETURN __attribute__((__noreturn__))
+#    endif
+#    if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__)
+#      define PNG_ALLOCATED __attribute__((__malloc__))
+#    endif
+#    if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__)
+#      define PNG_DEPRECATED __attribute__((__deprecated__))
+#    endif
+#    if !defined(PNG_PRIVATE)
+#      ifdef __has_extension
+#        if __has_extension(attribute_unavailable_with_message)
+#          define PNG_PRIVATE __attribute__((__unavailable__(\
+             "This function is not exported by libpng.")))
+#        endif
+#      endif
+#    endif
+#    ifndef PNG_RESTRICT
+#      define PNG_RESTRICT __restrict
+#    endif
+
+#  elif defined(__GNUC__)
+#    ifndef PNG_USE_RESULT
+#      define PNG_USE_RESULT __attribute__((__warn_unused_result__))
+#    endif
+#    ifndef PNG_NORETURN
+#      define PNG_NORETURN   __attribute__((__noreturn__))
+#    endif
+#    if __GNUC__ >= 3
+#      ifndef PNG_ALLOCATED
+#        define PNG_ALLOCATED  __attribute__((__malloc__))
+#      endif
+#      ifndef PNG_DEPRECATED
+#        define PNG_DEPRECATED __attribute__((__deprecated__))
+#      endif
+#      ifndef PNG_PRIVATE
+#        if 0 /* Doesn't work so we use deprecated instead*/
+#          define PNG_PRIVATE \
+            __attribute__((warning("This function is not exported by libpng.")))
+#        else
+#          define PNG_PRIVATE \
+            __attribute__((__deprecated__))
+#        endif
+#      endif
+#      if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1))
+#        ifndef PNG_RESTRICT
+#          define PNG_RESTRICT __restrict
+#        endif
+#      endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */
+#    endif /* __GNUC__ >= 3 */
+
+#  elif defined(_MSC_VER)  && (_MSC_VER >= 1300)
+#    ifndef PNG_USE_RESULT
+#      define PNG_USE_RESULT /* not supported */
+#    endif
+#    ifndef PNG_NORETURN
+#      define PNG_NORETURN   __declspec(noreturn)
+#    endif
+#    ifndef PNG_ALLOCATED
+#      if (_MSC_VER >= 1400)
+#        define PNG_ALLOCATED __declspec(restrict)
+#      endif
+#    endif
+#    ifndef PNG_DEPRECATED
+#      define PNG_DEPRECATED __declspec(deprecated)
+#    endif
+#    ifndef PNG_PRIVATE
+#      define PNG_PRIVATE __declspec(deprecated)
+#    endif
+#    ifndef PNG_RESTRICT
+#      if (_MSC_VER >= 1400)
+#        define PNG_RESTRICT __restrict
+#      endif
+#    endif
+
+#  elif defined(__WATCOMC__)
+#    ifndef PNG_RESTRICT
+#      define PNG_RESTRICT __restrict
+#    endif
+#  endif
+#endif /* PNG_PEDANTIC_WARNINGS */
+
+#ifndef PNG_DEPRECATED
+#  define PNG_DEPRECATED  /* Use of this function is deprecated */
+#endif
+#ifndef PNG_USE_RESULT
+#  define PNG_USE_RESULT  /* The result of this function must be checked */
+#endif
+#ifndef PNG_NORETURN
+#  define PNG_NORETURN    /* This function does not return */
+#endif
+#ifndef PNG_ALLOCATED
+#  define PNG_ALLOCATED   /* The result of the function is new memory */
+#endif
+#ifndef PNG_PRIVATE
+#  define PNG_PRIVATE     /* This is a private libpng function */
+#endif
+#ifndef PNG_RESTRICT
+#  define PNG_RESTRICT    /* The C99 "restrict" feature */
+#endif
+
+#ifndef PNG_FP_EXPORT     /* A floating point API. */
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+#     define PNG_FP_EXPORT(ordinal, type, name, args)\
+         PNG_EXPORT(ordinal, type, name, args);
+#  else                   /* No floating point APIs */
+#     define PNG_FP_EXPORT(ordinal, type, name, args)
+#  endif
+#endif
+#ifndef PNG_FIXED_EXPORT  /* A fixed point API. */
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\
+         PNG_EXPORT(ordinal, type, name, args);
+#  else                   /* No fixed point APIs */
+#     define PNG_FIXED_EXPORT(ordinal, type, name, args)
+#  endif
+#endif
+
+#ifndef PNG_BUILDING_SYMBOL_TABLE
+/* Some typedefs to get us started.  These should be safe on most of the common
+ * platforms.
+ *
+ * png_uint_32 and png_int_32 may, currently, be larger than required to hold a
+ * 32-bit value however this is not normally advisable.
+ *
+ * png_uint_16 and png_int_16 should always be two bytes in size - this is
+ * verified at library build time.
+ *
+ * png_byte must always be one byte in size.
+ *
+ * The checks below use constants from limits.h, as defined by the ISOC90
+ * standard.
+ */
+#if CHAR_BIT == 8 && UCHAR_MAX == 255
+   typedef unsigned char png_byte;
+#else
+#  error "libpng requires 8-bit bytes"
+#endif
+
+#if INT_MIN == -32768 && INT_MAX == 32767
+   typedef int png_int_16;
+#elif SHRT_MIN == -32768 && SHRT_MAX == 32767
+   typedef short png_int_16;
+#else
+#  error "libpng requires a signed 16-bit type"
+#endif
+
+#if UINT_MAX == 65535
+   typedef unsigned int png_uint_16;
+#elif USHRT_MAX == 65535
+   typedef unsigned short png_uint_16;
+#else
+#  error "libpng requires an unsigned 16-bit type"
+#endif
+
+#if INT_MIN < -2147483646 && INT_MAX > 2147483646
+   typedef int png_int_32;
+#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646
+   typedef long int png_int_32;
+#else
+#  error "libpng requires a signed 32-bit (or more) type"
+#endif
+
+#if UINT_MAX > 4294967294
+   typedef unsigned int png_uint_32;
+#elif ULONG_MAX > 4294967294
+   typedef unsigned long int png_uint_32;
+#else
+#  error "libpng requires an unsigned 32-bit (or more) type"
+#endif
+
+/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however,
+ * requires an ISOC90 compiler and relies on consistent behavior of sizeof.
+ */
+typedef size_t png_size_t;
+typedef ptrdiff_t png_ptrdiff_t;
+
+/* libpng needs to know the maximum value of 'size_t' and this controls the
+ * definition of png_alloc_size_t, below.  This maximum value of size_t limits
+ * but does not control the maximum allocations the library makes - there is
+ * direct application control of this through png_set_user_limits().
+ */
+#ifndef PNG_SMALL_SIZE_T
+   /* Compiler specific tests for systems where size_t is known to be less than
+    * 32 bits (some of these systems may no longer work because of the lack of
+    * 'far' support; see above.)
+    */
+#  if (defined(__TURBOC__) && !defined(__FLAT__)) ||\
+   (defined(_MSC_VER) && defined(MAXSEG_64K))
+#     define PNG_SMALL_SIZE_T
+#  endif
+#endif
+
+/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no
+ * smaller than png_uint_32.  Casts from png_size_t or png_uint_32 to
+ * png_alloc_size_t are not necessary; in fact, it is recommended not to use
+ * them at all so that the compiler can complain when something turns out to be
+ * problematic.
+ *
+ * Casts in the other direction (from png_alloc_size_t to png_size_t or
+ * png_uint_32) should be explicitly applied; however, we do not expect to
+ * encounter practical situations that require such conversions.
+ *
+ * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than
+ * 4294967295 - i.e. less than the maximum value of png_uint_32.
+ */
+#ifdef PNG_SMALL_SIZE_T
+   typedef png_uint_32 png_alloc_size_t;
+#else
+   typedef png_size_t png_alloc_size_t;
+#endif
+
+/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler
+ * implementations of Intel CPU specific support of user-mode segmented address
+ * spaces, where 16-bit pointers address more than 65536 bytes of memory using
+ * separate 'segment' registers.  The implementation requires two different
+ * types of pointer (only one of which includes the segment value.)
+ *
+ * If required this support is available in version 1.2 of libpng and may be
+ * available in versions through 1.5, although the correctness of the code has
+ * not been verified recently.
+ */
+
+/* Typedef for floating-point numbers that are converted to fixed-point with a
+ * multiple of 100,000, e.g., gamma
+ */
+typedef png_int_32 png_fixed_point;
+
+/* Add typedefs for pointers */
+typedef void                  * png_voidp;
+typedef const void            * png_const_voidp;
+typedef png_byte              * png_bytep;
+typedef const png_byte        * png_const_bytep;
+typedef png_uint_32           * png_uint_32p;
+typedef const png_uint_32     * png_const_uint_32p;
+typedef png_int_32            * png_int_32p;
+typedef const png_int_32      * png_const_int_32p;
+typedef png_uint_16           * png_uint_16p;
+typedef const png_uint_16     * png_const_uint_16p;
+typedef png_int_16            * png_int_16p;
+typedef const png_int_16      * png_const_int_16p;
+typedef char                  * png_charp;
+typedef const char            * png_const_charp;
+typedef png_fixed_point       * png_fixed_point_p;
+typedef const png_fixed_point * png_const_fixed_point_p;
+typedef png_size_t            * png_size_tp;
+typedef const png_size_t      * png_const_size_tp;
+
+#ifdef PNG_STDIO_SUPPORTED
+typedef FILE            * png_FILE_p;
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double       * png_doublep;
+typedef const double * png_const_doublep;
+#endif
+
+/* Pointers to pointers; i.e. arrays */
+typedef png_byte        * * png_bytepp;
+typedef png_uint_32     * * png_uint_32pp;
+typedef png_int_32      * * png_int_32pp;
+typedef png_uint_16     * * png_uint_16pp;
+typedef png_int_16      * * png_int_16pp;
+typedef const char      * * png_const_charpp;
+typedef char            * * png_charpp;
+typedef png_fixed_point * * png_fixed_point_pp;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double          * * png_doublepp;
+#endif
+
+/* Pointers to pointers to pointers; i.e., pointer to array */
+typedef char            * * * png_charppp;
+
+#endif /* PNG_BUILDING_SYMBOL_TABLE */
+
+#endif /* PNGCONF_H */
diff --git a/third_party/lpng_v163/pngdebug.h b/third_party/libpng/pngdebug.h
similarity index 86%
rename from third_party/lpng_v163/pngdebug.h
rename to third_party/libpng/pngdebug.h
index 96c1ea4..6a01b10 100644
--- a/third_party/lpng_v163/pngdebug.h
+++ b/third_party/libpng/pngdebug.h
@@ -1,157 +1,153 @@
-

-/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c

- *

- * Copyright (c) 1998-2011 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.)

- *

- * Last changed in libpng 1.5.0 [January 6, 2011]

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

-/* Define PNG_DEBUG at compile time for debugging information.  Higher

- * numbers for PNG_DEBUG mean more debugging information.  This has

- * only been added since version 0.95 so it is not implemented throughout

- * libpng yet, but more support will be added as needed.

- *

- * png_debug[1-2]?(level, message ,arg{0-2})

- *   Expands to a statement (either a simple expression or a compound

- *   do..while(0) statement) that outputs a message with parameter

- *   substitution if PNG_DEBUG is defined to 2 or more.  If PNG_DEBUG

- *   is undefined, 0 or 1 every png_debug expands to a simple expression

- *   (actually ((void)0)).

- *

- *   level: level of detail of message, starting at 0.  A level 'n'

- *          message is preceded by 'n' tab characters (not implemented

- *          on Microsoft compilers unless PNG_DEBUG_FILE is also

- *          defined, to allow debug DLL compilation with no standard IO).

- *   message: a printf(3) style text string.  A trailing '\n' is added

- *            to the message.

- *   arg: 0 to 2 arguments for printf(3) style substitution in message.

- */

-#ifndef PNGDEBUG_H

-#define PNGDEBUG_H

-/* These settings control the formatting of messages in png.c and pngerror.c */

-/* Moved to pngdebug.h at 1.5.0 */

-#  ifndef PNG_LITERAL_SHARP

-#    define PNG_LITERAL_SHARP 0x23

-#  endif

-#  ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET

-#    define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b

-#  endif

-#  ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET

-#    define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d

-#  endif

-#  ifndef PNG_STRING_NEWLINE

-#    define PNG_STRING_NEWLINE "\n"

-#  endif

-

-#ifdef PNG_DEBUG

-#  if (PNG_DEBUG > 0)

-#    if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)

-#      include <crtdbg.h>

-#      if (PNG_DEBUG > 1)

-#        ifndef _DEBUG

-#          define _DEBUG

-#        endif

-#        ifndef png_debug

-#          define png_debug(l,m)  _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)

-#        endif

-#        ifndef png_debug1

-#          define png_debug1(l,m,p1)  _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)

-#        endif

-#        ifndef png_debug2

-#          define png_debug2(l,m,p1,p2) \

-             _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)

-#        endif

-#      endif

-#    else /* PNG_DEBUG_FILE || !_MSC_VER */

-#      ifndef PNG_STDIO_SUPPORTED

-#        include <stdio.h> /* not included yet */

-#      endif

-#      ifndef PNG_DEBUG_FILE

-#        define PNG_DEBUG_FILE stderr

-#      endif /* PNG_DEBUG_FILE */

-

-#      if (PNG_DEBUG > 1)

-/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on

- * non-ISO compilers

- */

-#        ifdef __STDC__

-#          ifndef png_debug

-#            define png_debug(l,m) \

-       do { \

-       int num_tabs=l; \

-       fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \

-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \

-       } while (0)

-#          endif

-#          ifndef png_debug1

-#            define png_debug1(l,m,p1) \

-       do { \

-       int num_tabs=l; \

-       fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \

-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \

-       } while (0)

-#          endif

-#          ifndef png_debug2

-#            define png_debug2(l,m,p1,p2) \

-       do { \

-       int num_tabs=l; \

-       fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \

-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \

-       } while (0)

-#          endif

-#        else /* __STDC __ */

-#          ifndef png_debug

-#            define png_debug(l,m) \

-       do { \

-       int num_tabs=l; \

-       char format[256]; \

-       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \

-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \

-         m,PNG_STRING_NEWLINE); \

-       fprintf(PNG_DEBUG_FILE,format); \

-       } while (0)

-#          endif

-#          ifndef png_debug1

-#            define png_debug1(l,m,p1) \

-       do { \

-       int num_tabs=l; \

-       char format[256]; \

-       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \

-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \

-         m,PNG_STRING_NEWLINE); \

-       fprintf(PNG_DEBUG_FILE,format,p1); \

-       } while (0)

-#          endif

-#          ifndef png_debug2

-#            define png_debug2(l,m,p1,p2) \

-       do { \

-       int num_tabs=l; \

-       char format[256]; \

-       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \

-         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \

-         m,PNG_STRING_NEWLINE); \

-       fprintf(PNG_DEBUG_FILE,format,p1,p2); \

-       } while (0)

-#          endif

-#        endif /* __STDC __ */

-#      endif /* (PNG_DEBUG > 1) */

-

-#    endif /* _MSC_VER */

-#  endif /* (PNG_DEBUG > 0) */

-#endif /* PNG_DEBUG */

-#ifndef png_debug

-#  define png_debug(l, m) ((void)0)

-#endif

-#ifndef png_debug1

-#  define png_debug1(l, m, p1) ((void)0)

-#endif

-#ifndef png_debug2

-#  define png_debug2(l, m, p1, p2) ((void)0)

-#endif

-#endif /* PNGDEBUG_H */

+
+/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
+ *
+ * Last changed in libpng 1.6.8 [December 19, 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* Define PNG_DEBUG at compile time for debugging information.  Higher
+ * numbers for PNG_DEBUG mean more debugging information.  This has
+ * only been added since version 0.95 so it is not implemented throughout
+ * libpng yet, but more support will be added as needed.
+ *
+ * png_debug[1-2]?(level, message ,arg{0-2})
+ *   Expands to a statement (either a simple expression or a compound
+ *   do..while(0) statement) that outputs a message with parameter
+ *   substitution if PNG_DEBUG is defined to 2 or more.  If PNG_DEBUG
+ *   is undefined, 0 or 1 every png_debug expands to a simple expression
+ *   (actually ((void)0)).
+ *
+ *   level: level of detail of message, starting at 0.  A level 'n'
+ *          message is preceded by 'n' 3-space indentations (not implemented
+ *          on Microsoft compilers unless PNG_DEBUG_FILE is also
+ *          defined, to allow debug DLL compilation with no standard IO).
+ *   message: a printf(3) style text string.  A trailing '\n' is added
+ *            to the message.
+ *   arg: 0 to 2 arguments for printf(3) style substitution in message.
+ */
+#ifndef PNGDEBUG_H
+#define PNGDEBUG_H
+/* These settings control the formatting of messages in png.c and pngerror.c */
+/* Moved to pngdebug.h at 1.5.0 */
+#  ifndef PNG_LITERAL_SHARP
+#    define PNG_LITERAL_SHARP 0x23
+#  endif
+#  ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
+#    define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
+#  endif
+#  ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
+#    define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
+#  endif
+#  ifndef PNG_STRING_NEWLINE
+#    define PNG_STRING_NEWLINE "\n"
+#  endif
+
+#ifdef PNG_DEBUG
+#  if (PNG_DEBUG > 0)
+#    if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
+#      include <crtdbg.h>
+#      if (PNG_DEBUG > 1)
+#        ifndef _DEBUG
+#          define _DEBUG
+#        endif
+#        ifndef png_debug
+#          define png_debug(l,m)  _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)
+#        endif
+#        ifndef png_debug1
+#          define png_debug1(l,m,p1)  _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)
+#        endif
+#        ifndef png_debug2
+#          define png_debug2(l,m,p1,p2) \
+             _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)
+#        endif
+#      endif
+#    else /* PNG_DEBUG_FILE || !_MSC_VER */
+#      ifndef PNG_STDIO_SUPPORTED
+#        include <stdio.h> /* not included yet */
+#      endif
+#      ifndef PNG_DEBUG_FILE
+#        define PNG_DEBUG_FILE stderr
+#      endif /* PNG_DEBUG_FILE */
+
+#      if (PNG_DEBUG > 1)
+#        ifdef __STDC__
+#          ifndef png_debug
+#            define png_debug(l,m) \
+       do { \
+       int num_tabs=l; \
+       fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "   " : \
+         (num_tabs==2 ? "      " : (num_tabs>2 ? "         " : "")))); \
+       } while (0)
+#          endif
+#          ifndef png_debug1
+#            define png_debug1(l,m,p1) \
+       do { \
+       int num_tabs=l; \
+       fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "   " : \
+         (num_tabs==2 ? "      " : (num_tabs>2 ? "         " : ""))),p1); \
+       } while (0)
+#          endif
+#          ifndef png_debug2
+#            define png_debug2(l,m,p1,p2) \
+       do { \
+       int num_tabs=l; \
+       fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "   " : \
+         (num_tabs==2 ? "      " : (num_tabs>2 ? "         " : ""))),p1,p2);\
+       } while (0)
+#          endif
+#        else /* __STDC __ */
+#          ifndef png_debug
+#            define png_debug(l,m) \
+       do { \
+       int num_tabs=l; \
+       char format[256]; \
+       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+         m,PNG_STRING_NEWLINE); \
+       fprintf(PNG_DEBUG_FILE,format); \
+       } while (0)
+#          endif
+#          ifndef png_debug1
+#            define png_debug1(l,m,p1) \
+       do { \
+       int num_tabs=l; \
+       char format[256]; \
+       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+         m,PNG_STRING_NEWLINE); \
+       fprintf(PNG_DEBUG_FILE,format,p1); \
+       } while (0)
+#          endif
+#          ifndef png_debug2
+#            define png_debug2(l,m,p1,p2) \
+       do { \
+       int num_tabs=l; \
+       char format[256]; \
+       snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+         (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+         m,PNG_STRING_NEWLINE); \
+       fprintf(PNG_DEBUG_FILE,format,p1,p2); \
+       } while (0)
+#          endif
+#        endif /* __STDC __ */
+#      endif /* (PNG_DEBUG > 1) */
+
+#    endif /* _MSC_VER */
+#  endif /* (PNG_DEBUG > 0) */
+#endif /* PNG_DEBUG */
+#ifndef png_debug
+#  define png_debug(l, m) ((void)0)
+#endif
+#ifndef png_debug1
+#  define png_debug1(l, m, p1) ((void)0)
+#endif
+#ifndef png_debug2
+#  define png_debug2(l, m, p1, p2) ((void)0)
+#endif
+#endif /* PNGDEBUG_H */
diff --git a/third_party/lpng_v163/pngerror.c b/third_party/libpng/pngerror.c
similarity index 90%
rename from third_party/lpng_v163/pngerror.c
rename to third_party/libpng/pngerror.c
index 2125e4c..bdb959e 100644
--- a/third_party/lpng_v163/pngerror.c
+++ b/third_party/libpng/pngerror.c
@@ -1,931 +1,963 @@
-/* pngerror.c - stub functions for i/o and memory allocation

- *

- * Last changed in libpng 1.6.1 [March 28, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file provides a location for all error handling.  Users who

- * need special error handling are expected to write replacement functions

- * and use png_set_error_fn() to use those functions.  See the instructions

- * at each function.

- */

-

-#include "pngpriv.h"

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-

-static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr,

-    png_const_charp error_message)),PNG_NORETURN);

-

-#ifdef PNG_WARNINGS_SUPPORTED

-static void /* PRIVATE */

-png_default_warning PNGARG((png_const_structrp png_ptr,

-   png_const_charp warning_message));

-#endif /* PNG_WARNINGS_SUPPORTED */

-

-/* This function is called whenever there is a fatal error.  This function

- * should not be changed.  If there is a need to handle errors differently,

- * you should supply a replacement error function and use png_set_error_fn()

- * to replace the error function at run-time.

- */

-#ifdef PNG_ERROR_TEXT_SUPPORTED

-PNG_FUNCTION(void,PNGAPI

-png_error,(png_const_structrp png_ptr, png_const_charp error_message),

-   PNG_NORETURN)

-{

-#ifdef PNG_ERROR_NUMBERS_SUPPORTED

-   char msg[16];

-   if (png_ptr != NULL)

-   {

-      if (png_ptr->flags&

-         (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))

-      {

-         if (*error_message == PNG_LITERAL_SHARP)

-         {

-            /* Strip "#nnnn " from beginning of error message. */

-            int offset;

-            for (offset = 1; offset<15; offset++)

-               if (error_message[offset] == ' ')

-                  break;

-

-            if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)

-            {

-               int i;

-               for (i = 0; i < offset - 1; i++)

-                  msg[i] = error_message[i + 1];

-               msg[i - 1] = '\0';

-               error_message = msg;

-            }

-

-            else

-               error_message += offset;

-      }

-

-      else

-      {

-         if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)

-         {

-            msg[0] = '0';

-            msg[1] = '\0';

-            error_message = msg;

-         }

-       }

-     }

-   }

-#endif

-   if (png_ptr != NULL && png_ptr->error_fn != NULL)

-      (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr),

-          error_message);

-

-   /* If the custom handler doesn't exist, or if it returns,

-      use the default handler, which will not return. */

-   png_default_error(png_ptr, error_message);

-}

-#else

-PNG_FUNCTION(void,PNGAPI

-png_err,(png_const_structrp png_ptr),PNG_NORETURN)

-{

-   /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed

-    * erroneously as '\0', instead of the empty string "".  This was

-    * apparently an error, introduced in libpng-1.2.20, and png_default_error

-    * will crash in this case.

-    */

-   if (png_ptr != NULL && png_ptr->error_fn != NULL)

-      (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), "");

-

-   /* If the custom handler doesn't exist, or if it returns,

-      use the default handler, which will not return. */

-   png_default_error(png_ptr, "");

-}

-#endif /* PNG_ERROR_TEXT_SUPPORTED */

-

-/* Utility to safely appends strings to a buffer.  This never errors out so

- * error checking is not required in the caller.

- */

-size_t

-png_safecat(png_charp buffer, size_t bufsize, size_t pos,

-   png_const_charp string)

-{

-   if (buffer != NULL && pos < bufsize)

-   {

-      if (string != NULL)

-         while (*string != '\0' && pos < bufsize-1)

-           buffer[pos++] = *string++;

-

-      buffer[pos] = '\0';

-   }

-

-   return pos;

-}

-

-#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)

-/* Utility to dump an unsigned value into a buffer, given a start pointer and

- * and end pointer (which should point just *beyond* the end of the buffer!)

- * Returns the pointer to the start of the formatted string.

- */

-png_charp

-png_format_number(png_const_charp start, png_charp end, int format,

-   png_alloc_size_t number)

-{

-   int count = 0;    /* number of digits output */

-   int mincount = 1; /* minimum number required */

-   int output = 0;   /* digit output (for the fixed point format) */

-

-   *--end = '\0';

-

-   /* This is written so that the loop always runs at least once, even with

-    * number zero.

-    */

-   while (end > start && (number != 0 || count < mincount))

-   {

-

-      static const char digits[] = "0123456789ABCDEF";

-

-      switch (format)

-      {

-         case PNG_NUMBER_FORMAT_fixed:

-            /* Needs five digits (the fraction) */

-            mincount = 5;

-            if (output || number % 10 != 0)

-            {

-               *--end = digits[number % 10];

-               output = 1;

-            }

-            number /= 10;

-            break;

-

-         case PNG_NUMBER_FORMAT_02u:

-            /* Expects at least 2 digits. */

-            mincount = 2;

-            /* FALL THROUGH */

-

-         case PNG_NUMBER_FORMAT_u:

-            *--end = digits[number % 10];

-            number /= 10;

-            break;

-

-         case PNG_NUMBER_FORMAT_02x:

-            /* This format expects at least two digits */

-            mincount = 2;

-            /* FALL THROUGH */

-

-         case PNG_NUMBER_FORMAT_x:

-            *--end = digits[number & 0xf];

-            number >>= 4;

-            break;

-

-         default: /* an error */

-            number = 0;

-            break;

-      }

-

-      /* Keep track of the number of digits added */

-      ++count;

-

-      /* Float a fixed number here: */

-      if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start)

-      {

-         /* End of the fraction, but maybe nothing was output?  In that case

-          * drop the decimal point.  If the number is a true zero handle that

-          * here.

-          */

-         if (output)

-            *--end = '.';

-         else if (number == 0) /* and !output */

-            *--end = '0';

-      }

-   }

-

-   return end;

-}

-#endif

-

-#ifdef PNG_WARNINGS_SUPPORTED

-/* This function is called whenever there is a non-fatal error.  This function

- * should not be changed.  If there is a need to handle warnings differently,

- * you should supply a replacement warning function and use

- * png_set_error_fn() to replace the warning function at run-time.

- */

-void PNGAPI

-png_warning(png_const_structrp png_ptr, png_const_charp warning_message)

-{

-   int offset = 0;

-   if (png_ptr != NULL)

-   {

-#ifdef PNG_ERROR_NUMBERS_SUPPORTED

-   if (png_ptr->flags&

-       (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))

-#endif

-      {

-         if (*warning_message == PNG_LITERAL_SHARP)

-         {

-            for (offset = 1; offset < 15; offset++)

-               if (warning_message[offset] == ' ')

-                  break;

-         }

-      }

-   }

-   if (png_ptr != NULL && png_ptr->warning_fn != NULL)

-      (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),

-         warning_message + offset);

-   else

-      png_default_warning(png_ptr, warning_message + offset);

-}

-

-/* These functions support 'formatted' warning messages with up to

- * PNG_WARNING_PARAMETER_COUNT parameters.  In the format string the parameter

- * is introduced by @<number>, where 'number' starts at 1.  This follows the

- * standard established by X/Open for internationalizable error messages.

- */

-void

-png_warning_parameter(png_warning_parameters p, int number,

-   png_const_charp string)

-{

-   if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)

-      (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);

-}

-

-void

-png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,

-   png_alloc_size_t value)

-{

-   char buffer[PNG_NUMBER_BUFFER_SIZE];

-   png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));

-}

-

-void

-png_warning_parameter_signed(png_warning_parameters p, int number, int format,

-   png_int_32 value)

-{

-   png_alloc_size_t u;

-   png_charp str;

-   char buffer[PNG_NUMBER_BUFFER_SIZE];

-

-   /* Avoid overflow by doing the negate in a png_alloc_size_t: */

-   u = (png_alloc_size_t)value;

-   if (value < 0)

-      u = ~u + 1;

-

-   str = PNG_FORMAT_NUMBER(buffer, format, u);

-

-   if (value < 0 && str > buffer)

-      *--str = '-';

-

-   png_warning_parameter(p, number, str);

-}

-

-void

-png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,

-   png_const_charp message)

-{

-   /* The internal buffer is just 192 bytes - enough for all our messages,

-    * overflow doesn't happen because this code checks!  If someone figures

-    * out how to send us a message longer than 192 bytes, all that will

-    * happen is that the message will be truncated appropriately.

-    */

-   size_t i = 0; /* Index in the msg[] buffer: */

-   char msg[192];

-

-   /* Each iteration through the following loop writes at most one character

-    * to msg[i++] then returns here to validate that there is still space for

-    * the trailing '\0'.  It may (in the case of a parameter) read more than

-    * one character from message[]; it must check for '\0' and continue to the

-    * test if it finds the end of string.

-    */

-   while (i<(sizeof msg)-1 && *message != '\0')

-   {

-      /* '@' at end of string is now just printed (previously it was skipped);

-       * it is an error in the calling code to terminate the string with @.

-       */

-      if (p != NULL && *message == '@' && message[1] != '\0')

-      {

-         int parameter_char = *++message; /* Consume the '@' */

-         static const char valid_parameters[] = "123456789";

-         int parameter = 0;

-

-         /* Search for the parameter digit, the index in the string is the

-          * parameter to use.

-          */

-         while (valid_parameters[parameter] != parameter_char &&

-            valid_parameters[parameter] != '\0')

-            ++parameter;

-

-         /* If the parameter digit is out of range it will just get printed. */

-         if (parameter < PNG_WARNING_PARAMETER_COUNT)

-         {

-            /* Append this parameter */

-            png_const_charp parm = p[parameter];

-            png_const_charp pend = p[parameter] + (sizeof p[parameter]);

-

-            /* No need to copy the trailing '\0' here, but there is no guarantee

-             * that parm[] has been initialized, so there is no guarantee of a

-             * trailing '\0':

-             */

-            while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend)

-               msg[i++] = *parm++;

-

-            /* Consume the parameter digit too: */

-            ++message;

-            continue;

-         }

-

-         /* else not a parameter and there is a character after the @ sign; just

-          * copy that.  This is known not to be '\0' because of the test above.

-          */

-      }

-

-      /* At this point *message can't be '\0', even in the bad parameter case

-       * above where there is a lone '@' at the end of the message string.

-       */

-      msg[i++] = *message++;

-   }

-

-   /* i is always less than (sizeof msg), so: */

-   msg[i] = '\0';

-

-   /* And this is the formatted message. It may be larger than

-    * PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these

-    * are not (currently) formatted.

-    */

-   png_warning(png_ptr, msg);

-}

-#endif /* PNG_WARNINGS_SUPPORTED */

-

-#ifdef PNG_BENIGN_ERRORS_SUPPORTED

-void PNGAPI

-png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)

-{

-   if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN)

-   {

-#     ifdef PNG_READ_SUPPORTED

-         if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&

-            png_ptr->chunk_name != 0)

-            png_chunk_warning(png_ptr, error_message);

-         else

-#     endif

-      png_warning(png_ptr, error_message);

-   }

-

-   else

-   {

-#     ifdef PNG_READ_SUPPORTED

-         if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&

-            png_ptr->chunk_name != 0)

-            png_chunk_error(png_ptr, error_message);

-         else

-#     endif

-      png_error(png_ptr, error_message);

-   }

-}

-

-void /* PRIVATE */

-png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)

-{

-  if (png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN)

-     png_warning(png_ptr, error_message);

-  else

-     png_error(png_ptr, error_message);

-}

-

-void /* PRIVATE */

-png_app_error(png_const_structrp png_ptr, png_const_charp error_message)

-{

-  if (png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN)

-     png_warning(png_ptr, error_message);

-  else

-     png_error(png_ptr, error_message);

-}

-#endif /* BENIGN_ERRORS */

-

-/* These utilities are used internally to build an error message that relates

- * to the current chunk.  The chunk name comes from png_ptr->chunk_name,

- * this is used to prefix the message.  The message is limited in length

- * to 63 bytes, the name characters are output as hex digits wrapped in []

- * if the character is invalid.

- */

-#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))

-static PNG_CONST char png_digit[16] = {

-   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

-   'A', 'B', 'C', 'D', 'E', 'F'

-};

-

-#define PNG_MAX_ERROR_TEXT 196 /* Currently limited be profile_error in png.c */

-#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED)

-static void /* PRIVATE */

-png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp

-    error_message)

-{

-   png_uint_32 chunk_name = png_ptr->chunk_name;

-   int iout = 0, ishift = 24;

-

-   while (ishift >= 0)

-   {

-      int c = (int)(chunk_name >> ishift) & 0xff;

-

-      ishift -= 8;

-      if (isnonalpha(c))

-      {

-         buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;

-         buffer[iout++] = png_digit[(c & 0xf0) >> 4];

-         buffer[iout++] = png_digit[c & 0x0f];

-         buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET;

-      }

-

-      else

-      {

-         buffer[iout++] = (char)c;

-      }

-   }

-

-   if (error_message == NULL)

-      buffer[iout] = '\0';

-

-   else

-   {

-      int iin = 0;

-

-      buffer[iout++] = ':';

-      buffer[iout++] = ' ';

-

-      while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0')

-         buffer[iout++] = error_message[iin++];

-

-      /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */

-      buffer[iout] = '\0';

-   }

-}

-#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */

-

-#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)

-PNG_FUNCTION(void,PNGAPI

-png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),

-   PNG_NORETURN)

-{

-   char msg[18+PNG_MAX_ERROR_TEXT];

-   if (png_ptr == NULL)

-      png_error(png_ptr, error_message);

-

-   else

-   {

-      png_format_buffer(png_ptr, msg, error_message);

-      png_error(png_ptr, msg);

-   }

-}

-#endif /* PNG_READ_SUPPORTED && PNG_ERROR_TEXT_SUPPORTED */

-

-#ifdef PNG_WARNINGS_SUPPORTED

-void PNGAPI

-png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)

-{

-   char msg[18+PNG_MAX_ERROR_TEXT];

-   if (png_ptr == NULL)

-      png_warning(png_ptr, warning_message);

-

-   else

-   {

-      png_format_buffer(png_ptr, msg, warning_message);

-      png_warning(png_ptr, msg);

-   }

-}

-#endif /* PNG_WARNINGS_SUPPORTED */

-

-#ifdef PNG_READ_SUPPORTED

-#ifdef PNG_BENIGN_ERRORS_SUPPORTED

-void PNGAPI

-png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp

-    error_message)

-{

-   if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN)

-      png_chunk_warning(png_ptr, error_message);

-

-   else

-      png_chunk_error(png_ptr, error_message);

-}

-#endif

-#endif /* PNG_READ_SUPPORTED */

-

-void /* PRIVATE */

-png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)

-{

-   /* This is always supported, but for just read or just write it

-    * unconditionally does the right thing.

-    */

-#  if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)

-      if (png_ptr->mode & PNG_IS_READ_STRUCT)

-#  endif

-

-#  ifdef PNG_READ_SUPPORTED

-      {

-         if (error < PNG_CHUNK_ERROR)

-            png_chunk_warning(png_ptr, message);

-

-         else

-            png_chunk_benign_error(png_ptr, message);

-      }

-#  endif

-

-#  if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)

-      else if (!(png_ptr->mode & PNG_IS_READ_STRUCT))

-#  endif

-

-#  ifdef PNG_WRITE_SUPPORTED

-      {

-         if (error < PNG_CHUNK_WRITE_ERROR)

-            png_app_warning(png_ptr, message);

-

-         else

-            png_app_error(png_ptr, message);

-      }

-#  endif

-}

-

-#ifdef PNG_ERROR_TEXT_SUPPORTED

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-PNG_FUNCTION(void,

-png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)

-{

-#  define fixed_message "fixed point overflow in "

-#  define fixed_message_ln ((sizeof fixed_message)-1)

-   int  iin;

-   char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];

-   memcpy(msg, fixed_message, fixed_message_ln);

-   iin = 0;

-   if (name != NULL) while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)

-   {

-      msg[fixed_message_ln + iin] = name[iin];

-      ++iin;

-   }

-   msg[fixed_message_ln + iin] = 0;

-   png_error(png_ptr, msg);

-}

-#endif

-#endif

-

-#ifdef PNG_SETJMP_SUPPORTED

-/* This API only exists if ANSI-C style error handling is used,

- * otherwise it is necessary for png_default_error to be overridden.

- */

-jmp_buf* PNGAPI

-png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,

-    size_t jmp_buf_size)

-{

-   /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value

-    * and it must not change after that.  Libpng doesn't care how big the

-    * buffer is, just that it doesn't change.

-    *

-    * If the buffer size is no *larger* than the size of jmp_buf when libpng is

-    * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0

-    * semantics that this call will not fail.  If the size is larger, however,

-    * the buffer is allocated and this may fail, causing the function to return

-    * NULL.

-    */

-   if (png_ptr == NULL)

-      return NULL;

-

-   if (png_ptr->jmp_buf_ptr == NULL)

-   {

-      png_ptr->jmp_buf_size = 0; /* not allocated */

-

-      if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local))

-         png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local;

-

-      else

-      {

-         png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,

-            png_malloc_warn(png_ptr, jmp_buf_size));

-

-         if (png_ptr->jmp_buf_ptr == NULL)

-            return NULL; /* new NULL return on OOM */

-

-         png_ptr->jmp_buf_size = jmp_buf_size;

-      }

-   }

-

-   else /* Already allocated: check the size */

-   {

-      size_t size = png_ptr->jmp_buf_size;

-

-      if (size == 0)

-      {

-         size = (sizeof png_ptr->jmp_buf_local);

-         if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local)

-         {

-            /* This is an internal error in libpng: somehow we have been left

-             * with a stack allocated jmp_buf when the application regained

-             * control.  It's always possible to fix this up, but for the moment

-             * this is a png_error because that makes it easy to detect.

-             */

-            png_error(png_ptr, "Libpng jmp_buf still allocated");

-            /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */

-         }

-      }

-

-      if (size != jmp_buf_size)

-      {

-         png_warning(png_ptr, "Application jmp_buf size changed");

-         return NULL; /* caller will probably crash: no choice here */

-      }

-   }

-

-   /* Finally fill in the function, now we have a satisfactory buffer. It is

-    * valid to change the function on every call.

-    */

-   png_ptr->longjmp_fn = longjmp_fn;

-   return png_ptr->jmp_buf_ptr;

-}

-

-void /* PRIVATE */

-png_free_jmpbuf(png_structrp png_ptr)

-{

-   if (png_ptr != NULL)

-   {

-      jmp_buf *jb = png_ptr->jmp_buf_ptr;

-

-      /* A size of 0 is used to indicate a local, stack, allocation of the

-       * pointer; used here and in png.c

-       */

-      if (jb != NULL && png_ptr->jmp_buf_size > 0)

-      {

-

-         /* This stuff is so that a failure to free the error control structure

-          * does not leave libpng in a state with no valid error handling: the

-          * free always succeeds, if there is an error it gets ignored.

-          */

-         if (jb != &png_ptr->jmp_buf_local)

-         {

-            /* Make an internal, libpng, jmp_buf to return here */

-            jmp_buf free_jmp_buf;

-

-            if (!setjmp(free_jmp_buf))

-            {

-               png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */

-               png_ptr->jmp_buf_size = 0; /* stack allocation */

-               png_ptr->longjmp_fn = longjmp;

-               png_free(png_ptr, jb); /* Return to setjmp on error */

-            }

-         }

-      }

-

-      /* *Always* cancel everything out: */

-      png_ptr->jmp_buf_size = 0;

-      png_ptr->jmp_buf_ptr = NULL;

-      png_ptr->longjmp_fn = 0;

-   }

-}

-#endif

-

-/* This is the default error handling function.  Note that replacements for

- * this function MUST NOT RETURN, or the program will likely crash.  This

- * function is used by default, or if the program supplies NULL for the

- * error function pointer in png_set_error_fn().

- */

-static PNG_FUNCTION(void /* PRIVATE */,

-png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),

-   PNG_NORETURN)

-{

-#ifdef PNG_CONSOLE_IO_SUPPORTED

-#ifdef PNG_ERROR_NUMBERS_SUPPORTED

-   /* Check on NULL only added in 1.5.4 */

-   if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)

-   {

-      /* Strip "#nnnn " from beginning of error message. */

-      int offset;

-      char error_number[16];

-      for (offset = 0; offset<15; offset++)

-      {

-         error_number[offset] = error_message[offset + 1];

-         if (error_message[offset] == ' ')

-            break;

-      }

-

-      if ((offset > 1) && (offset < 15))

-      {

-         error_number[offset - 1] = '\0';

-         fprintf(stderr, "libpng error no. %s: %s",

-             error_number, error_message + offset + 1);

-         fprintf(stderr, PNG_STRING_NEWLINE);

-      }

-

-      else

-      {

-         fprintf(stderr, "libpng error: %s, offset=%d",

-             error_message, offset);

-         fprintf(stderr, PNG_STRING_NEWLINE);

-      }

-   }

-   else

-#endif

-   {

-      fprintf(stderr, "libpng error: %s", error_message ? error_message :

-         "undefined");

-      fprintf(stderr, PNG_STRING_NEWLINE);

-   }

-#else

-   PNG_UNUSED(error_message) /* Make compiler happy */

-#endif

-   png_longjmp(png_ptr, 1);

-}

-

-PNG_FUNCTION(void,PNGAPI

-png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)

-{

-#ifdef PNG_SETJMP_SUPPORTED

-   if (png_ptr && png_ptr->longjmp_fn && png_ptr->jmp_buf_ptr)

-      png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val);

-#endif

-

-   /* Here if not setjmp support or if png_ptr is null. */

-   PNG_ABORT();

-}

-

-#ifdef PNG_WARNINGS_SUPPORTED

-/* This function is called when there is a warning, but the library thinks

- * it can continue anyway.  Replacement functions don't have to do anything

- * here if you don't want them to.  In the default configuration, png_ptr is

- * not used, but it is passed in case it may be useful.

- */

-static void /* PRIVATE */

-png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)

-{

-#ifdef PNG_CONSOLE_IO_SUPPORTED

-#  ifdef PNG_ERROR_NUMBERS_SUPPORTED

-   if (*warning_message == PNG_LITERAL_SHARP)

-   {

-      int offset;

-      char warning_number[16];

-      for (offset = 0; offset < 15; offset++)

-      {

-         warning_number[offset] = warning_message[offset + 1];

-         if (warning_message[offset] == ' ')

-            break;

-      }

-

-      if ((offset > 1) && (offset < 15))

-      {

-         warning_number[offset + 1] = '\0';

-         fprintf(stderr, "libpng warning no. %s: %s",

-             warning_number, warning_message + offset);

-         fprintf(stderr, PNG_STRING_NEWLINE);

-      }

-

-      else

-      {

-         fprintf(stderr, "libpng warning: %s",

-             warning_message);

-         fprintf(stderr, PNG_STRING_NEWLINE);

-      }

-   }

-   else

-#  endif

-

-   {

-      fprintf(stderr, "libpng warning: %s", warning_message);

-      fprintf(stderr, PNG_STRING_NEWLINE);

-   }

-#else

-   PNG_UNUSED(warning_message) /* Make compiler happy */

-#endif

-   PNG_UNUSED(png_ptr) /* Make compiler happy */

-}

-#endif /* PNG_WARNINGS_SUPPORTED */

-

-/* This function is called when the application wants to use another method

- * of handling errors and warnings.  Note that the error function MUST NOT

- * return to the calling routine or serious problems will occur.  The return

- * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1)

- */

-void PNGAPI

-png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr,

-    png_error_ptr error_fn, png_error_ptr warning_fn)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->error_ptr = error_ptr;

-   png_ptr->error_fn = error_fn;

-#ifdef PNG_WARNINGS_SUPPORTED

-   png_ptr->warning_fn = warning_fn;

-#else

-   PNG_UNUSED(warning_fn)

-#endif

-}

-

-

-/* This function returns a pointer to the error_ptr associated with the user

- * functions.  The application should free any memory associated with this

- * pointer before png_write_destroy and png_read_destroy are called.

- */

-png_voidp PNGAPI

-png_get_error_ptr(png_const_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return NULL;

-

-   return ((png_voidp)png_ptr->error_ptr);

-}

-

-

-#ifdef PNG_ERROR_NUMBERS_SUPPORTED

-void PNGAPI

-png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)

-{

-   if (png_ptr != NULL)

-   {

-      png_ptr->flags &=

-         ((~(PNG_FLAG_STRIP_ERROR_NUMBERS |

-         PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);

-   }

-}

-#endif

-

-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\

-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)

-   /* Currently the above both depend on SETJMP_SUPPORTED, however it would be

-    * possible to implement without setjmp support just so long as there is some

-    * way to handle the error return here:

-    */

-PNG_FUNCTION(void /* PRIVATE */,

-png_safe_error,(png_structp png_nonconst_ptr, png_const_charp error_message),

-   PNG_NORETURN)

-{

-   const png_const_structrp png_ptr = png_nonconst_ptr;

-   png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);

-

-   /* An error is always logged here, overwriting anything (typically a warning)

-    * that is already there:

-    */

-   if (image != NULL)

-   {

-      png_safecat(image->message, (sizeof image->message), 0, error_message);

-      image->warning_or_error |= PNG_IMAGE_ERROR;

-

-      /* Retrieve the jmp_buf from within the png_control, making this work for

-       * C++ compilation too is pretty tricky: C++ wants a pointer to the first

-       * element of a jmp_buf, but C doesn't tell us the type of that.

-       */

-      if (image->opaque != NULL && image->opaque->error_buf != NULL)

-         longjmp(png_control_jmp_buf(image->opaque), 1);

-

-      /* Missing longjmp buffer, the following is to help debugging: */

-      {

-         size_t pos = png_safecat(image->message, (sizeof image->message), 0,

-            "bad longjmp: ");

-         png_safecat(image->message, (sizeof image->message), pos,

-             error_message);

-      }

-   }

-

-   /* Here on an internal programming error. */

-   abort();

-}

-

-#ifdef PNG_WARNINGS_SUPPORTED

-void /* PRIVATE */

-png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)

-{

-   const png_const_structrp png_ptr = png_nonconst_ptr;

-   png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);

-

-   /* A warning is only logged if there is no prior warning or error. */

-   if (image->warning_or_error == 0)

-   {

-      png_safecat(image->message, (sizeof image->message), 0, warning_message);

-      image->warning_or_error |= PNG_IMAGE_WARNING;

-   }

-}

-#endif

-

-int /* PRIVATE */

-png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)

-{

-   volatile png_imagep image = image_in;

-   volatile int result;

-   volatile png_voidp saved_error_buf;

-   jmp_buf safe_jmpbuf;

-

-   /* Safely execute function(arg) with png_error returning to this function. */

-   saved_error_buf = image->opaque->error_buf;

-   result = setjmp(safe_jmpbuf) == 0;

-

-   if (result)

-   {

-

-      image->opaque->error_buf = safe_jmpbuf;

-      result = function(arg);

-   }

-

-   image->opaque->error_buf = saved_error_buf;

-

-   /* And do the cleanup prior to any failure return. */

-   if (!result)

-      png_image_free(image);

-

-   return result;

-}

-#endif /* SIMPLIFIED READ/WRITE */

-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */

+
+/* pngerror.c - stub functions for i/o and memory allocation
+ *
+ * Last changed in libpng 1.6.15 [November 20, 2014]
+ * Copyright (c) 1998-2014 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all error handling.  Users who
+ * need special error handling are expected to write replacement functions
+ * and use png_set_error_fn() to use those functions.  See the instructions
+ * at each function.
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr,
+    png_const_charp error_message)),PNG_NORETURN);
+
+#ifdef PNG_WARNINGS_SUPPORTED
+static void /* PRIVATE */
+png_default_warning PNGARG((png_const_structrp png_ptr,
+   png_const_charp warning_message));
+#endif /* WARNINGS */
+
+/* This function is called whenever there is a fatal error.  This function
+ * should not be changed.  If there is a need to handle errors differently,
+ * you should supply a replacement error function and use png_set_error_fn()
+ * to replace the error function at run-time.
+ */
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+PNG_FUNCTION(void,PNGAPI
+png_error,(png_const_structrp png_ptr, png_const_charp error_message),
+   PNG_NORETURN)
+{
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   char msg[16];
+   if (png_ptr != NULL)
+   {
+      if ((png_ptr->flags &
+         (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0
+      {
+         if (*error_message == PNG_LITERAL_SHARP)
+         {
+            /* Strip "#nnnn " from beginning of error message. */
+            int offset;
+            for (offset = 1; offset<15; offset++)
+               if (error_message[offset] == ' ')
+                  break;
+
+            if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
+            {
+               int i;
+               for (i = 0; i < offset - 1; i++)
+                  msg[i] = error_message[i + 1];
+               msg[i - 1] = '\0';
+               error_message = msg;
+            }
+
+            else
+               error_message += offset;
+      }
+
+      else
+      {
+         if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
+         {
+            msg[0] = '0';
+            msg[1] = '\0';
+            error_message = msg;
+         }
+       }
+     }
+   }
+#endif
+   if (png_ptr != NULL && png_ptr->error_fn != NULL)
+      (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr),
+          error_message);
+
+   /* If the custom handler doesn't exist, or if it returns,
+      use the default handler, which will not return. */
+   png_default_error(png_ptr, error_message);
+}
+#else
+PNG_FUNCTION(void,PNGAPI
+png_err,(png_const_structrp png_ptr),PNG_NORETURN)
+{
+   /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
+    * erroneously as '\0', instead of the empty string "".  This was
+    * apparently an error, introduced in libpng-1.2.20, and png_default_error
+    * will crash in this case.
+    */
+   if (png_ptr != NULL && png_ptr->error_fn != NULL)
+      (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), "");
+
+   /* If the custom handler doesn't exist, or if it returns,
+      use the default handler, which will not return. */
+   png_default_error(png_ptr, "");
+}
+#endif /* ERROR_TEXT */
+
+/* Utility to safely appends strings to a buffer.  This never errors out so
+ * error checking is not required in the caller.
+ */
+size_t
+png_safecat(png_charp buffer, size_t bufsize, size_t pos,
+   png_const_charp string)
+{
+   if (buffer != NULL && pos < bufsize)
+   {
+      if (string != NULL)
+         while (*string != '\0' && pos < bufsize-1)
+           buffer[pos++] = *string++;
+
+      buffer[pos] = '\0';
+   }
+
+   return pos;
+}
+
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Utility to dump an unsigned value into a buffer, given a start pointer and
+ * and end pointer (which should point just *beyond* the end of the buffer!)
+ * Returns the pointer to the start of the formatted string.
+ */
+png_charp
+png_format_number(png_const_charp start, png_charp end, int format,
+   png_alloc_size_t number)
+{
+   int count = 0;    /* number of digits output */
+   int mincount = 1; /* minimum number required */
+   int output = 0;   /* digit output (for the fixed point format) */
+
+   *--end = '\0';
+
+   /* This is written so that the loop always runs at least once, even with
+    * number zero.
+    */
+   while (end > start && (number != 0 || count < mincount))
+   {
+
+      static const char digits[] = "0123456789ABCDEF";
+
+      switch (format)
+      {
+         case PNG_NUMBER_FORMAT_fixed:
+            /* Needs five digits (the fraction) */
+            mincount = 5;
+            if (output != 0 || number % 10 != 0)
+            {
+               *--end = digits[number % 10];
+               output = 1;
+            }
+            number /= 10;
+            break;
+
+         case PNG_NUMBER_FORMAT_02u:
+            /* Expects at least 2 digits. */
+            mincount = 2;
+            /* FALL THROUGH */
+
+         case PNG_NUMBER_FORMAT_u:
+            *--end = digits[number % 10];
+            number /= 10;
+            break;
+
+         case PNG_NUMBER_FORMAT_02x:
+            /* This format expects at least two digits */
+            mincount = 2;
+            /* FALL THROUGH */
+
+         case PNG_NUMBER_FORMAT_x:
+            *--end = digits[number & 0xf];
+            number >>= 4;
+            break;
+
+         default: /* an error */
+            number = 0;
+            break;
+      }
+
+      /* Keep track of the number of digits added */
+      ++count;
+
+      /* Float a fixed number here: */
+      if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start))
+      {
+         /* End of the fraction, but maybe nothing was output?  In that case
+          * drop the decimal point.  If the number is a true zero handle that
+          * here.
+          */
+         if (output != 0)
+            *--end = '.';
+         else if (number == 0) /* and !output */
+            *--end = '0';
+      }
+   }
+
+   return end;
+}
+#endif
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* This function is called whenever there is a non-fatal error.  This function
+ * should not be changed.  If there is a need to handle warnings differently,
+ * you should supply a replacement warning function and use
+ * png_set_error_fn() to replace the warning function at run-time.
+ */
+void PNGAPI
+png_warning(png_const_structrp png_ptr, png_const_charp warning_message)
+{
+   int offset = 0;
+   if (png_ptr != NULL)
+   {
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if ((png_ptr->flags &
+       (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
+#endif
+      {
+         if (*warning_message == PNG_LITERAL_SHARP)
+         {
+            for (offset = 1; offset < 15; offset++)
+               if (warning_message[offset] == ' ')
+                  break;
+         }
+      }
+   }
+   if (png_ptr != NULL && png_ptr->warning_fn != NULL)
+      (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),
+         warning_message + offset);
+   else
+      png_default_warning(png_ptr, warning_message + offset);
+}
+
+/* These functions support 'formatted' warning messages with up to
+ * PNG_WARNING_PARAMETER_COUNT parameters.  In the format string the parameter
+ * is introduced by @<number>, where 'number' starts at 1.  This follows the
+ * standard established by X/Open for internationalizable error messages.
+ */
+void
+png_warning_parameter(png_warning_parameters p, int number,
+   png_const_charp string)
+{
+   if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
+      (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
+}
+
+void
+png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
+   png_alloc_size_t value)
+{
+   char buffer[PNG_NUMBER_BUFFER_SIZE];
+   png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
+}
+
+void
+png_warning_parameter_signed(png_warning_parameters p, int number, int format,
+   png_int_32 value)
+{
+   png_alloc_size_t u;
+   png_charp str;
+   char buffer[PNG_NUMBER_BUFFER_SIZE];
+
+   /* Avoid overflow by doing the negate in a png_alloc_size_t: */
+   u = (png_alloc_size_t)value;
+   if (value < 0)
+      u = ~u + 1;
+
+   str = PNG_FORMAT_NUMBER(buffer, format, u);
+
+   if (value < 0 && str > buffer)
+      *--str = '-';
+
+   png_warning_parameter(p, number, str);
+}
+
+void
+png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
+   png_const_charp message)
+{
+   /* The internal buffer is just 192 bytes - enough for all our messages,
+    * overflow doesn't happen because this code checks!  If someone figures
+    * out how to send us a message longer than 192 bytes, all that will
+    * happen is that the message will be truncated appropriately.
+    */
+   size_t i = 0; /* Index in the msg[] buffer: */
+   char msg[192];
+
+   /* Each iteration through the following loop writes at most one character
+    * to msg[i++] then returns here to validate that there is still space for
+    * the trailing '\0'.  It may (in the case of a parameter) read more than
+    * one character from message[]; it must check for '\0' and continue to the
+    * test if it finds the end of string.
+    */
+   while (i<(sizeof msg)-1 && *message != '\0')
+   {
+      /* '@' at end of string is now just printed (previously it was skipped);
+       * it is an error in the calling code to terminate the string with @.
+       */
+      if (p != NULL && *message == '@' && message[1] != '\0')
+      {
+         int parameter_char = *++message; /* Consume the '@' */
+         static const char valid_parameters[] = "123456789";
+         int parameter = 0;
+
+         /* Search for the parameter digit, the index in the string is the
+          * parameter to use.
+          */
+         while (valid_parameters[parameter] != parameter_char &&
+            valid_parameters[parameter] != '\0')
+            ++parameter;
+
+         /* If the parameter digit is out of range it will just get printed. */
+         if (parameter < PNG_WARNING_PARAMETER_COUNT)
+         {
+            /* Append this parameter */
+            png_const_charp parm = p[parameter];
+            png_const_charp pend = p[parameter] + (sizeof p[parameter]);
+
+            /* No need to copy the trailing '\0' here, but there is no guarantee
+             * that parm[] has been initialized, so there is no guarantee of a
+             * trailing '\0':
+             */
+            while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend)
+               msg[i++] = *parm++;
+
+            /* Consume the parameter digit too: */
+            ++message;
+            continue;
+         }
+
+         /* else not a parameter and there is a character after the @ sign; just
+          * copy that.  This is known not to be '\0' because of the test above.
+          */
+      }
+
+      /* At this point *message can't be '\0', even in the bad parameter case
+       * above where there is a lone '@' at the end of the message string.
+       */
+      msg[i++] = *message++;
+   }
+
+   /* i is always less than (sizeof msg), so: */
+   msg[i] = '\0';
+
+   /* And this is the formatted message. It may be larger than
+    * PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these
+    * are not (currently) formatted.
+    */
+   png_warning(png_ptr, msg);
+}
+#endif /* WARNINGS */
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+void PNGAPI
+png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)
+{
+   if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
+   {
+#     ifdef PNG_READ_SUPPORTED
+         if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
+            png_ptr->chunk_name != 0)
+            png_chunk_warning(png_ptr, error_message);
+         else
+#     endif
+      png_warning(png_ptr, error_message);
+   }
+
+   else
+   {
+#     ifdef PNG_READ_SUPPORTED
+         if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
+            png_ptr->chunk_name != 0)
+            png_chunk_error(png_ptr, error_message);
+         else
+#     endif
+      png_error(png_ptr, error_message);
+   }
+
+#  ifndef PNG_ERROR_TEXT_SUPPORTED
+      PNG_UNUSED(error_message)
+#  endif
+}
+
+void /* PRIVATE */
+png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)
+{
+  if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0)
+     png_warning(png_ptr, error_message);
+  else
+     png_error(png_ptr, error_message);
+
+#  ifndef PNG_ERROR_TEXT_SUPPORTED
+      PNG_UNUSED(error_message)
+#  endif
+}
+
+void /* PRIVATE */
+png_app_error(png_const_structrp png_ptr, png_const_charp error_message)
+{
+  if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0)
+     png_warning(png_ptr, error_message);
+  else
+     png_error(png_ptr, error_message);
+
+#  ifndef PNG_ERROR_TEXT_SUPPORTED
+      PNG_UNUSED(error_message)
+#  endif
+}
+#endif /* BENIGN_ERRORS */
+
+#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */
+#if defined(PNG_WARNINGS_SUPPORTED) || \
+   (defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED))
+/* These utilities are used internally to build an error message that relates
+ * to the current chunk.  The chunk name comes from png_ptr->chunk_name,
+ * which is used to prefix the message.  The message is limited in length
+ * to 63 bytes. The name characters are output as hex digits wrapped in []
+ * if the character is invalid.
+ */
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+static PNG_CONST char png_digit[16] = {
+   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+   'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static void /* PRIVATE */
+png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
+    error_message)
+{
+   png_uint_32 chunk_name = png_ptr->chunk_name;
+   int iout = 0, ishift = 24;
+
+   while (ishift >= 0)
+   {
+      int c = (int)(chunk_name >> ishift) & 0xff;
+
+      ishift -= 8;
+      if (isnonalpha(c) != 0)
+      {
+         buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;
+         buffer[iout++] = png_digit[(c & 0xf0) >> 4];
+         buffer[iout++] = png_digit[c & 0x0f];
+         buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET;
+      }
+
+      else
+      {
+         buffer[iout++] = (char)c;
+      }
+   }
+
+   if (error_message == NULL)
+      buffer[iout] = '\0';
+
+   else
+   {
+      int iin = 0;
+
+      buffer[iout++] = ':';
+      buffer[iout++] = ' ';
+
+      while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0')
+         buffer[iout++] = error_message[iin++];
+
+      /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */
+      buffer[iout] = '\0';
+   }
+}
+#endif /* WARNINGS || ERROR_TEXT */
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
+PNG_FUNCTION(void,PNGAPI
+png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
+   PNG_NORETURN)
+{
+   char msg[18+PNG_MAX_ERROR_TEXT];
+   if (png_ptr == NULL)
+      png_error(png_ptr, error_message);
+
+   else
+   {
+      png_format_buffer(png_ptr, msg, error_message);
+      png_error(png_ptr, msg);
+   }
+}
+#endif /* READ && ERROR_TEXT */
+
+#ifdef PNG_WARNINGS_SUPPORTED
+void PNGAPI
+png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
+{
+   char msg[18+PNG_MAX_ERROR_TEXT];
+   if (png_ptr == NULL)
+      png_warning(png_ptr, warning_message);
+
+   else
+   {
+      png_format_buffer(png_ptr, msg, warning_message);
+      png_warning(png_ptr, msg);
+   }
+}
+#endif /* WARNINGS */
+
+#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+void PNGAPI
+png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
+    error_message)
+{
+   if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
+      png_chunk_warning(png_ptr, error_message);
+
+   else
+      png_chunk_error(png_ptr, error_message);
+
+#  ifndef PNG_ERROR_TEXT_SUPPORTED
+      PNG_UNUSED(error_message)
+#  endif
+}
+#endif
+#endif /* READ */
+
+void /* PRIVATE */
+png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
+{
+#  ifndef PNG_WARNINGS_SUPPORTED
+      PNG_UNUSED(message)
+#  endif
+
+   /* This is always supported, but for just read or just write it
+    * unconditionally does the right thing.
+    */
+#  if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
+      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
+#  endif
+
+#  ifdef PNG_READ_SUPPORTED
+      {
+         if (error < PNG_CHUNK_ERROR)
+            png_chunk_warning(png_ptr, message);
+
+         else
+            png_chunk_benign_error(png_ptr, message);
+      }
+#  endif
+
+#  if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
+      else if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
+#  endif
+
+#  ifdef PNG_WRITE_SUPPORTED
+      {
+         if (error < PNG_CHUNK_WRITE_ERROR)
+            png_app_warning(png_ptr, message);
+
+         else
+            png_app_error(png_ptr, message);
+      }
+#  endif
+}
+
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_FUNCTION(void,
+png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
+{
+#  define fixed_message "fixed point overflow in "
+#  define fixed_message_ln ((sizeof fixed_message)-1)
+   int  iin;
+   char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
+   memcpy(msg, fixed_message, fixed_message_ln);
+   iin = 0;
+   if (name != NULL)
+      while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
+      {
+         msg[fixed_message_ln + iin] = name[iin];
+         ++iin;
+      }
+   msg[fixed_message_ln + iin] = 0;
+   png_error(png_ptr, msg);
+}
+#endif
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* This API only exists if ANSI-C style error handling is used,
+ * otherwise it is necessary for png_default_error to be overridden.
+ */
+jmp_buf* PNGAPI
+png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,
+    size_t jmp_buf_size)
+{
+   /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value
+    * and it must not change after that.  Libpng doesn't care how big the
+    * buffer is, just that it doesn't change.
+    *
+    * If the buffer size is no *larger* than the size of jmp_buf when libpng is
+    * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0
+    * semantics that this call will not fail.  If the size is larger, however,
+    * the buffer is allocated and this may fail, causing the function to return
+    * NULL.
+    */
+   if (png_ptr == NULL)
+      return NULL;
+
+   if (png_ptr->jmp_buf_ptr == NULL)
+   {
+      png_ptr->jmp_buf_size = 0; /* not allocated */
+
+      if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local))
+         png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local;
+
+      else
+      {
+         png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,
+            png_malloc_warn(png_ptr, jmp_buf_size));
+
+         if (png_ptr->jmp_buf_ptr == NULL)
+            return NULL; /* new NULL return on OOM */
+
+         png_ptr->jmp_buf_size = jmp_buf_size;
+      }
+   }
+
+   else /* Already allocated: check the size */
+   {
+      size_t size = png_ptr->jmp_buf_size;
+
+      if (size == 0)
+      {
+         size = (sizeof png_ptr->jmp_buf_local);
+         if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local)
+         {
+            /* This is an internal error in libpng: somehow we have been left
+             * with a stack allocated jmp_buf when the application regained
+             * control.  It's always possible to fix this up, but for the moment
+             * this is a png_error because that makes it easy to detect.
+             */
+            png_error(png_ptr, "Libpng jmp_buf still allocated");
+            /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */
+         }
+      }
+
+      if (size != jmp_buf_size)
+      {
+         png_warning(png_ptr, "Application jmp_buf size changed");
+         return NULL; /* caller will probably crash: no choice here */
+      }
+   }
+
+   /* Finally fill in the function, now we have a satisfactory buffer. It is
+    * valid to change the function on every call.
+    */
+   png_ptr->longjmp_fn = longjmp_fn;
+   return png_ptr->jmp_buf_ptr;
+}
+
+void /* PRIVATE */
+png_free_jmpbuf(png_structrp png_ptr)
+{
+   if (png_ptr != NULL)
+   {
+      jmp_buf *jb = png_ptr->jmp_buf_ptr;
+
+      /* A size of 0 is used to indicate a local, stack, allocation of the
+       * pointer; used here and in png.c
+       */
+      if (jb != NULL && png_ptr->jmp_buf_size > 0)
+      {
+
+         /* This stuff is so that a failure to free the error control structure
+          * does not leave libpng in a state with no valid error handling: the
+          * free always succeeds, if there is an error it gets ignored.
+          */
+         if (jb != &png_ptr->jmp_buf_local)
+         {
+            /* Make an internal, libpng, jmp_buf to return here */
+            jmp_buf free_jmp_buf;
+
+            if (!setjmp(free_jmp_buf))
+            {
+               png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */
+               png_ptr->jmp_buf_size = 0; /* stack allocation */
+               png_ptr->longjmp_fn = longjmp;
+               png_free(png_ptr, jb); /* Return to setjmp on error */
+            }
+         }
+      }
+
+      /* *Always* cancel everything out: */
+      png_ptr->jmp_buf_size = 0;
+      png_ptr->jmp_buf_ptr = NULL;
+      png_ptr->longjmp_fn = 0;
+   }
+}
+#endif
+
+/* This is the default error handling function.  Note that replacements for
+ * this function MUST NOT RETURN, or the program will likely crash.  This
+ * function is used by default, or if the program supplies NULL for the
+ * error function pointer in png_set_error_fn().
+ */
+static PNG_FUNCTION(void /* PRIVATE */,
+png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
+   PNG_NORETURN)
+{
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   /* Check on NULL only added in 1.5.4 */
+   if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
+   {
+      /* Strip "#nnnn " from beginning of error message. */
+      int offset;
+      char error_number[16];
+      for (offset = 0; offset<15; offset++)
+      {
+         error_number[offset] = error_message[offset + 1];
+         if (error_message[offset] == ' ')
+            break;
+      }
+
+      if ((offset > 1) && (offset < 15))
+      {
+         error_number[offset - 1] = '\0';
+         fprintf(stderr, "libpng error no. %s: %s",
+             error_number, error_message + offset + 1);
+         fprintf(stderr, PNG_STRING_NEWLINE);
+      }
+
+      else
+      {
+         fprintf(stderr, "libpng error: %s, offset=%d",
+             error_message, offset);
+         fprintf(stderr, PNG_STRING_NEWLINE);
+      }
+   }
+   else
+#endif
+   {
+      fprintf(stderr, "libpng error: %s", error_message ? error_message :
+         "undefined");
+      fprintf(stderr, PNG_STRING_NEWLINE);
+   }
+#else
+   PNG_UNUSED(error_message) /* Make compiler happy */
+#endif
+   png_longjmp(png_ptr, 1);
+}
+
+PNG_FUNCTION(void,PNGAPI
+png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
+       png_ptr->jmp_buf_ptr != NULL)
+      png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val);
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(val)
+#endif
+
+   /* If control reaches this point, png_longjmp() must not return. The only
+    * choice is to terminate the whole process (or maybe the thread); to do
+    * this the ANSI-C abort() function is used unless a different method is
+    * implemented by overriding the default configuration setting for
+    * PNG_ABORT().
+    */
+   PNG_ABORT();
+}
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* This function is called when there is a warning, but the library thinks
+ * it can continue anyway.  Replacement functions don't have to do anything
+ * here if you don't want them to.  In the default configuration, png_ptr is
+ * not used, but it is passed in case it may be useful.
+ */
+static void /* PRIVATE */
+png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)
+{
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+#  ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (*warning_message == PNG_LITERAL_SHARP)
+   {
+      int offset;
+      char warning_number[16];
+      for (offset = 0; offset < 15; offset++)
+      {
+         warning_number[offset] = warning_message[offset + 1];
+         if (warning_message[offset] == ' ')
+            break;
+      }
+
+      if ((offset > 1) && (offset < 15))
+      {
+         warning_number[offset + 1] = '\0';
+         fprintf(stderr, "libpng warning no. %s: %s",
+             warning_number, warning_message + offset);
+         fprintf(stderr, PNG_STRING_NEWLINE);
+      }
+
+      else
+      {
+         fprintf(stderr, "libpng warning: %s",
+             warning_message);
+         fprintf(stderr, PNG_STRING_NEWLINE);
+      }
+   }
+   else
+#  endif
+
+   {
+      fprintf(stderr, "libpng warning: %s", warning_message);
+      fprintf(stderr, PNG_STRING_NEWLINE);
+   }
+#else
+   PNG_UNUSED(warning_message) /* Make compiler happy */
+#endif
+   PNG_UNUSED(png_ptr) /* Make compiler happy */
+}
+#endif /* WARNINGS */
+
+/* This function is called when the application wants to use another method
+ * of handling errors and warnings.  Note that the error function MUST NOT
+ * return to the calling routine or serious problems will occur.  The return
+ * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1)
+ */
+void PNGAPI
+png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warning_fn)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->error_ptr = error_ptr;
+   png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
+   png_ptr->warning_fn = warning_fn;
+#else
+   PNG_UNUSED(warning_fn)
+#endif
+}
+
+
+/* This function returns a pointer to the error_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_error_ptr(png_const_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return NULL;
+
+   return ((png_voidp)png_ptr->error_ptr);
+}
+
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+void PNGAPI
+png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)
+{
+   if (png_ptr != NULL)
+   {
+      png_ptr->flags &=
+         ((~(PNG_FLAG_STRIP_ERROR_NUMBERS |
+         PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
+   }
+}
+#endif
+
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
+   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+   /* Currently the above both depend on SETJMP_SUPPORTED, however it would be
+    * possible to implement without setjmp support just so long as there is some
+    * way to handle the error return here:
+    */
+PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI
+png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
+   PNG_NORETURN)
+{
+   const png_const_structrp png_ptr = png_nonconst_ptr;
+   png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
+
+   /* An error is always logged here, overwriting anything (typically a warning)
+    * that is already there:
+    */
+   if (image != NULL)
+   {
+      png_safecat(image->message, (sizeof image->message), 0, error_message);
+      image->warning_or_error |= PNG_IMAGE_ERROR;
+
+      /* Retrieve the jmp_buf from within the png_control, making this work for
+       * C++ compilation too is pretty tricky: C++ wants a pointer to the first
+       * element of a jmp_buf, but C doesn't tell us the type of that.
+       */
+      if (image->opaque != NULL && image->opaque->error_buf != NULL)
+         longjmp(png_control_jmp_buf(image->opaque), 1);
+
+      /* Missing longjmp buffer, the following is to help debugging: */
+      {
+         size_t pos = png_safecat(image->message, (sizeof image->message), 0,
+            "bad longjmp: ");
+         png_safecat(image->message, (sizeof image->message), pos,
+             error_message);
+      }
+   }
+
+   /* Here on an internal programming error. */
+   abort();
+}
+
+#ifdef PNG_WARNINGS_SUPPORTED
+void /* PRIVATE */ PNGCBAPI
+png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
+{
+   const png_const_structrp png_ptr = png_nonconst_ptr;
+   png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
+
+   /* A warning is only logged if there is no prior warning or error. */
+   if (image->warning_or_error == 0)
+   {
+      png_safecat(image->message, (sizeof image->message), 0, warning_message);
+      image->warning_or_error |= PNG_IMAGE_WARNING;
+   }
+}
+#endif
+
+int /* PRIVATE */
+png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)
+{
+   volatile png_imagep image = image_in;
+   volatile int result;
+   volatile png_voidp saved_error_buf;
+   jmp_buf safe_jmpbuf;
+
+   /* Safely execute function(arg) with png_error returning to this function. */
+   saved_error_buf = image->opaque->error_buf;
+   result = setjmp(safe_jmpbuf) == 0;
+
+   if (result != 0)
+   {
+
+      image->opaque->error_buf = safe_jmpbuf;
+      result = function(arg);
+   }
+
+   image->opaque->error_buf = saved_error_buf;
+
+   /* And do the cleanup prior to any failure return. */
+   if (result == 0)
+      png_image_free(image);
+
+   return result;
+}
+#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
+#endif /* READ || WRITE */
diff --git a/third_party/lpng_v163/pngget.c b/third_party/libpng/pngget.c
similarity index 87%
rename from third_party/lpng_v163/pngget.c
rename to third_party/libpng/pngget.c
index 6839f6b..743a6a9 100644
--- a/third_party/lpng_v163/pngget.c
+++ b/third_party/libpng/pngget.c
@@ -1,1176 +1,1219 @@
-/* pngget.c - retrieval of values from info struct

- *

- * Last changed in libpng 1.6.1 [March 28, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- */

-

-#include "pngpriv.h"

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-

-png_uint_32 PNGAPI

-png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_uint_32 flag)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return(info_ptr->valid & flag);

-

-   return(0);

-}

-

-png_size_t PNGAPI

-png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return(info_ptr->rowbytes);

-

-   return(0);

-}

-

-#ifdef PNG_INFO_IMAGE_SUPPORTED

-png_bytepp PNGAPI

-png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return(info_ptr->row_pointers);

-

-   return(0);

-}

-#endif

-

-#ifdef PNG_EASY_ACCESS_SUPPORTED

-/* Easy access to info, added in libpng-0.99 */

-png_uint_32 PNGAPI

-png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->width;

-

-   return (0);

-}

-

-png_uint_32 PNGAPI

-png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->height;

-

-   return (0);

-}

-

-png_byte PNGAPI

-png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->bit_depth;

-

-   return (0);

-}

-

-png_byte PNGAPI

-png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->color_type;

-

-   return (0);

-}

-

-png_byte PNGAPI

-png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->filter_type;

-

-   return (0);

-}

-

-png_byte PNGAPI

-png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->interlace_type;

-

-   return (0);

-}

-

-png_byte PNGAPI

-png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return info_ptr->compression_type;

-

-   return (0);

-}

-

-png_uint_32 PNGAPI

-png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp

-   info_ptr)

-{

-#ifdef PNG_pHYs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))

-      {

-         png_debug1(1, "in %s retrieval function",

-             "png_get_x_pixels_per_meter");

-

-         if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)

-            return (info_ptr->x_pixels_per_unit);

-      }

-#endif

-

-   return (0);

-}

-

-png_uint_32 PNGAPI

-png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp

-    info_ptr)

-{

-#ifdef PNG_pHYs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))

-   {

-      png_debug1(1, "in %s retrieval function",

-          "png_get_y_pixels_per_meter");

-

-      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)

-         return (info_ptr->y_pixels_per_unit);

-   }

-#endif

-

-   return (0);

-}

-

-png_uint_32 PNGAPI

-png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-#ifdef PNG_pHYs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))

-   {

-      png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter");

-

-      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER &&

-          info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit)

-         return (info_ptr->x_pixels_per_unit);

-   }

-#endif

-

-   return (0);

-}

-

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-float PNGAPI

-png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp

-   info_ptr)

-{

-#ifdef PNG_READ_pHYs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))

-   {

-      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio");

-

-      if (info_ptr->x_pixels_per_unit != 0)

-         return ((float)((float)info_ptr->y_pixels_per_unit

-             /(float)info_ptr->x_pixels_per_unit));

-   }

-#else

-   PNG_UNUSED(png_ptr)

-   PNG_UNUSED(info_ptr)

-#endif

-

-   return ((float)0.0);

-}

-#endif

-

-#ifdef PNG_FIXED_POINT_SUPPORTED

-png_fixed_point PNGAPI

-png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,

-    png_const_inforp info_ptr)

-{

-#ifdef PNG_READ_pHYs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)

-       && info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0

-       && info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX

-       && info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX)

-   {

-      png_fixed_point res;

-

-      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed");

-

-      /* The following casts work because a PNG 4 byte integer only has a valid

-       * range of 0..2^31-1; otherwise the cast might overflow.

-       */

-      if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1,

-          (png_int_32)info_ptr->x_pixels_per_unit))

-         return res;

-   }

-#else

-   PNG_UNUSED(png_ptr)

-   PNG_UNUSED(info_ptr)

-#endif

-

-   return 0;

-}

-#endif

-

-png_int_32 PNGAPI

-png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-#ifdef PNG_oFFs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))

-   {

-      png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns");

-

-      if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)

-         return (info_ptr->x_offset);

-   }

-#endif

-

-   return (0);

-}

-

-png_int_32 PNGAPI

-png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-#ifdef PNG_oFFs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))

-   {

-      png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns");

-

-      if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)

-         return (info_ptr->y_offset);

-   }

-#endif

-

-   return (0);

-}

-

-png_int_32 PNGAPI

-png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-#ifdef PNG_oFFs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))

-   {

-      png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels");

-

-      if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)

-         return (info_ptr->x_offset);

-   }

-#endif

-

-   return (0);

-}

-

-png_int_32 PNGAPI

-png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-#ifdef PNG_oFFs_SUPPORTED

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))

-   {

-      png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels");

-

-      if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)

-         return (info_ptr->y_offset);

-   }

-#endif

-

-   return (0);

-}

-

-#ifdef PNG_INCH_CONVERSIONS_SUPPORTED

-static png_uint_32

-ppi_from_ppm(png_uint_32 ppm)

-{

-#if 0

-   /* The conversion is *(2.54/100), in binary (32 digits):

-    * .00000110100000001001110101001001

-    */

-   png_uint_32 t1001, t1101;

-   ppm >>= 1;                  /* .1 */

-   t1001 = ppm + (ppm >> 3);   /* .1001 */

-   t1101 = t1001 + (ppm >> 1); /* .1101 */

-   ppm >>= 20;                 /* .000000000000000000001 */

-   t1101 += t1101 >> 15;       /* .1101000000000001101 */

-   t1001 >>= 11;               /* .000000000001001 */

-   t1001 += t1001 >> 12;       /* .000000000001001000000001001 */

-   ppm += t1001;               /* .000000000001001000001001001 */

-   ppm += t1101;               /* .110100000001001110101001001 */

-   return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */

-#else

-   /* The argument is a PNG unsigned integer, so it is not permitted

-    * to be bigger than 2^31.

-    */

-   png_fixed_point result;

-   if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127,

-       5000))

-      return result;

-

-   /* Overflow. */

-   return 0;

-#endif

-}

-

-png_uint_32 PNGAPI

-png_get_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr));

-}

-

-png_uint_32 PNGAPI

-png_get_x_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr));

-}

-

-png_uint_32 PNGAPI

-png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr));

-}

-

-#ifdef PNG_FIXED_POINT_SUPPORTED

-static png_fixed_point

-png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns)

-{

-   /* Convert from metres * 1,000,000 to inches * 100,000, meters to

-    * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127.

-    * Notice that this can overflow - a warning is output and 0 is

-    * returned.

-    */

-   return png_muldiv_warn(png_ptr, microns, 500, 127);

-}

-

-png_fixed_point PNGAPI

-png_get_x_offset_inches_fixed(png_const_structrp png_ptr,

-    png_const_inforp info_ptr)

-{

-   return png_fixed_inches_from_microns(png_ptr,

-       png_get_x_offset_microns(png_ptr, info_ptr));

-}

-#endif

-

-#ifdef PNG_FIXED_POINT_SUPPORTED

-png_fixed_point PNGAPI

-png_get_y_offset_inches_fixed(png_const_structrp png_ptr,

-    png_const_inforp info_ptr)

-{

-   return png_fixed_inches_from_microns(png_ptr,

-       png_get_y_offset_microns(png_ptr, info_ptr));

-}

-#endif

-

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-float PNGAPI

-png_get_x_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   /* To avoid the overflow do the conversion directly in floating

-    * point.

-    */

-   return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937);

-}

-#endif

-

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-float PNGAPI

-png_get_y_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   /* To avoid the overflow do the conversion directly in floating

-    * point.

-    */

-   return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937);

-}

-#endif

-

-#ifdef PNG_pHYs_SUPPORTED

-png_uint_32 PNGAPI

-png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)

-{

-   png_uint_32 retval = 0;

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))

-   {

-      png_debug1(1, "in %s retrieval function", "pHYs");

-

-      if (res_x != NULL)

-      {

-         *res_x = info_ptr->x_pixels_per_unit;

-         retval |= PNG_INFO_pHYs;

-      }

-

-      if (res_y != NULL)

-      {

-         *res_y = info_ptr->y_pixels_per_unit;

-         retval |= PNG_INFO_pHYs;

-      }

-

-      if (unit_type != NULL)

-      {

-         *unit_type = (int)info_ptr->phys_unit_type;

-         retval |= PNG_INFO_pHYs;

-

-         if (*unit_type == 1)

-         {

-            if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50);

-            if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50);

-         }

-      }

-   }

-

-   return (retval);

-}

-#endif /* PNG_pHYs_SUPPORTED */

-#endif  /* PNG_INCH_CONVERSIONS_SUPPORTED */

-

-/* png_get_channels really belongs in here, too, but it's been around longer */

-

-#endif  /* PNG_EASY_ACCESS_SUPPORTED */

-

-

-png_byte PNGAPI

-png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return(info_ptr->channels);

-

-   return (0);

-}

-

-#ifdef PNG_READ_SUPPORTED

-png_const_bytep PNGAPI

-png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return(info_ptr->signature);

-

-   return (NULL);

-}

-#endif

-

-#ifdef PNG_bKGD_SUPPORTED

-png_uint_32 PNGAPI

-png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,

-   png_color_16p *background)

-{

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)

-       && background != NULL)

-   {

-      png_debug1(1, "in %s retrieval function", "bKGD");

-

-      *background = &(info_ptr->background);

-      return (PNG_INFO_bKGD);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_cHRM_SUPPORTED

-/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the

- * same time to correct the rgb grayscale coefficient defaults obtained from the

- * cHRM chunk in 1.5.4

- */

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-png_uint_32 PNGAPI

-png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    double *white_x, double *white_y, double *red_x, double *red_y,

-    double *green_x, double *green_y, double *blue_x, double *blue_y)

-{

-   /* Quiet API change: this code used to only return the end points if a cHRM

-    * chunk was present, but the end points can also come from iCCP or sRGB

-    * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and

-    * the png_set_ APIs merely check that set end points are mutually

-    * consistent.

-    */

-   if (png_ptr != NULL && info_ptr != NULL &&

-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))

-   {

-      png_debug1(1, "in %s retrieval function", "cHRM");

-

-      if (white_x != NULL)

-         *white_x = png_float(png_ptr,

-            info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");

-      if (white_y != NULL)

-         *white_y = png_float(png_ptr,

-            info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");

-      if (red_x != NULL)

-         *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx,

-            "cHRM red X");

-      if (red_y != NULL)

-         *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy,

-            "cHRM red Y");

-      if (green_x != NULL)

-         *green_x = png_float(png_ptr,

-            info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");

-      if (green_y != NULL)

-         *green_y = png_float(png_ptr,

-            info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");

-      if (blue_x != NULL)

-         *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex,

-            "cHRM blue X");

-      if (blue_y != NULL)

-         *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,

-            "cHRM blue Y");

-      return (PNG_INFO_cHRM);

-   }

-

-   return (0);

-}

-

-png_uint_32 PNGAPI

-png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,

-   double *red_X, double *red_Y, double *red_Z, double *green_X,

-   double *green_Y, double *green_Z, double *blue_X, double *blue_Y,

-   double *blue_Z)

-{

-   if (png_ptr != NULL && info_ptr != NULL &&

-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))

-   {

-      png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");

-

-      if (red_X != NULL)

-         *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,

-            "cHRM red X");

-      if (red_Y != NULL)

-         *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y,

-            "cHRM red Y");

-      if (red_Z != NULL)

-         *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z,

-            "cHRM red Z");

-      if (green_X != NULL)

-         *green_X = png_float(png_ptr,

-            info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");

-      if (green_Y != NULL)

-         *green_Y = png_float(png_ptr,

-            info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");

-      if (green_Z != NULL)

-         *green_Z = png_float(png_ptr,

-            info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");

-      if (blue_X != NULL)

-         *blue_X = png_float(png_ptr,

-            info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");

-      if (blue_Y != NULL)

-         *blue_Y = png_float(png_ptr,

-            info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");

-      if (blue_Z != NULL)

-         *blue_Z = png_float(png_ptr,

-            info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");

-      return (PNG_INFO_cHRM);

-   }

-

-   return (0);

-}

-#  endif

-

-#  ifdef PNG_FIXED_POINT_SUPPORTED

-png_uint_32 PNGAPI

-png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_fixed_point *int_red_X, png_fixed_point *int_red_Y,

-    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,

-    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,

-    png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,

-    png_fixed_point *int_blue_Z)

-{

-   if (png_ptr != NULL && info_ptr != NULL &&

-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))

-   {

-      png_debug1(1, "in %s retrieval function", "cHRM_XYZ");

-

-      if (int_red_X != NULL)

-         *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X;

-      if (int_red_Y != NULL)

-         *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y;

-      if (int_red_Z != NULL)

-         *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z;

-      if (int_green_X != NULL)

-         *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X;

-      if (int_green_Y != NULL)

-         *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y;

-      if (int_green_Z != NULL)

-         *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z;

-      if (int_blue_X != NULL)

-         *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X;

-      if (int_blue_Y != NULL)

-         *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y;

-      if (int_blue_Z != NULL)

-         *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z;

-      return (PNG_INFO_cHRM);

-   }

-

-   return (0);

-}

-

-png_uint_32 PNGAPI

-png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,

-    png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,

-    png_fixed_point *blue_x, png_fixed_point *blue_y)

-{

-   png_debug1(1, "in %s retrieval function", "cHRM");

-

-   if (png_ptr != NULL && info_ptr != NULL &&

-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS))

-   {

-      if (white_x != NULL)

-         *white_x = info_ptr->colorspace.end_points_xy.whitex;

-      if (white_y != NULL)

-         *white_y = info_ptr->colorspace.end_points_xy.whitey;

-      if (red_x != NULL)

-         *red_x = info_ptr->colorspace.end_points_xy.redx;

-      if (red_y != NULL)

-         *red_y = info_ptr->colorspace.end_points_xy.redy;

-      if (green_x != NULL)

-         *green_x = info_ptr->colorspace.end_points_xy.greenx;

-      if (green_y != NULL)

-         *green_y = info_ptr->colorspace.end_points_xy.greeny;

-      if (blue_x != NULL)

-         *blue_x = info_ptr->colorspace.end_points_xy.bluex;

-      if (blue_y != NULL)

-         *blue_y = info_ptr->colorspace.end_points_xy.bluey;

-      return (PNG_INFO_cHRM);

-   }

-

-   return (0);

-}

-#  endif

-#endif

-

-#ifdef PNG_gAMA_SUPPORTED

-#  ifdef PNG_FIXED_POINT_SUPPORTED

-png_uint_32 PNGAPI

-png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_fixed_point *file_gamma)

-{

-   png_debug1(1, "in %s retrieval function", "gAMA");

-

-   if (png_ptr != NULL && info_ptr != NULL &&

-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) &&

-      file_gamma != NULL)

-   {

-      *file_gamma = info_ptr->colorspace.gamma;

-      return (PNG_INFO_gAMA);

-   }

-

-   return (0);

-}

-#  endif

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-png_uint_32 PNGAPI

-png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    double *file_gamma)

-{

-   png_debug1(1, "in %s retrieval function", "gAMA(float)");

-

-   if (png_ptr != NULL && info_ptr != NULL &&

-      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) &&

-      file_gamma != NULL)

-   {

-      *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,

-         "png_get_gAMA");

-      return (PNG_INFO_gAMA);

-   }

-

-   return (0);

-}

-#  endif

-#endif

-

-#ifdef PNG_sRGB_SUPPORTED

-png_uint_32 PNGAPI

-png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    int *file_srgb_intent)

-{

-   png_debug1(1, "in %s retrieval function", "sRGB");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)

-       && file_srgb_intent != NULL)

-   {

-      *file_srgb_intent = info_ptr->colorspace.rendering_intent;

-      return (PNG_INFO_sRGB);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_iCCP_SUPPORTED

-png_uint_32 PNGAPI

-png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_charpp name, int *compression_type,

-    png_bytepp profile, png_uint_32 *proflen)

-{

-   png_debug1(1, "in %s retrieval function", "iCCP");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)

-       && name != NULL && compression_type != NULL && profile != NULL &&

-		 proflen != NULL)

-   {

-      *name = info_ptr->iccp_name;

-      *profile = info_ptr->iccp_profile;

-      *proflen = png_get_uint_32(info_ptr->iccp_profile);

-      /* This is somewhat irrelevant since the profile data returned has

-       * actually been uncompressed.

-       */

-      *compression_type = PNG_COMPRESSION_TYPE_BASE;

-      return (PNG_INFO_iCCP);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_sPLT_SUPPORTED

-int PNGAPI

-png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_sPLT_tpp spalettes)

-{

-   if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)

-   {

-      *spalettes = info_ptr->splt_palettes;

-      return info_ptr->splt_palettes_num;

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_hIST_SUPPORTED

-png_uint_32 PNGAPI

-png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_uint_16p *hist)

-{

-   png_debug1(1, "in %s retrieval function", "hIST");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)

-       && hist != NULL)

-   {

-      *hist = info_ptr->hist;

-      return (PNG_INFO_hIST);

-   }

-

-   return (0);

-}

-#endif

-

-png_uint_32 PNGAPI

-png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_uint_32 *width, png_uint_32 *height, int *bit_depth,

-    int *color_type, int *interlace_type, int *compression_type,

-    int *filter_type)

-{

-   png_debug1(1, "in %s retrieval function", "IHDR");

-

-   if (png_ptr == NULL || info_ptr == NULL || width == NULL ||

-       height == NULL || bit_depth == NULL || color_type == NULL)

-      return (0);

-

-   *width = info_ptr->width;

-   *height = info_ptr->height;

-   *bit_depth = info_ptr->bit_depth;

-   *color_type = info_ptr->color_type;

-

-   if (compression_type != NULL)

-      *compression_type = info_ptr->compression_type;

-

-   if (filter_type != NULL)

-      *filter_type = info_ptr->filter_type;

-

-   if (interlace_type != NULL)

-      *interlace_type = info_ptr->interlace_type;

-

-   /* This is redundant if we can be sure that the info_ptr values were all

-    * assigned in png_set_IHDR().  We do the check anyhow in case an

-    * application has ignored our advice not to mess with the members

-    * of info_ptr directly.

-    */

-   png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height,

-       info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,

-       info_ptr->compression_type, info_ptr->filter_type);

-

-   return (1);

-}

-

-#ifdef PNG_oFFs_SUPPORTED

-png_uint_32 PNGAPI

-png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)

-{

-   png_debug1(1, "in %s retrieval function", "oFFs");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)

-       && offset_x != NULL && offset_y != NULL && unit_type != NULL)

-   {

-      *offset_x = info_ptr->x_offset;

-      *offset_y = info_ptr->y_offset;

-      *unit_type = (int)info_ptr->offset_unit_type;

-      return (PNG_INFO_oFFs);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_pCAL_SUPPORTED

-png_uint_32 PNGAPI

-png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,

-    png_charp *units, png_charpp *params)

-{

-   png_debug1(1, "in %s retrieval function", "pCAL");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)

-       && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&

-       nparams != NULL && units != NULL && params != NULL)

-   {

-      *purpose = info_ptr->pcal_purpose;

-      *X0 = info_ptr->pcal_X0;

-      *X1 = info_ptr->pcal_X1;

-      *type = (int)info_ptr->pcal_type;

-      *nparams = (int)info_ptr->pcal_nparams;

-      *units = info_ptr->pcal_units;

-      *params = info_ptr->pcal_params;

-      return (PNG_INFO_pCAL);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_sCAL_SUPPORTED

-#  ifdef PNG_FIXED_POINT_SUPPORTED

-#    if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \

-         defined(PNG_FLOATING_POINT_SUPPORTED)

-png_uint_32 PNGAPI

-png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    int *unit, png_fixed_point *width, png_fixed_point *height)

-{

-   if (png_ptr != NULL && info_ptr != NULL &&

-       (info_ptr->valid & PNG_INFO_sCAL))

-   {

-      *unit = info_ptr->scal_unit;

-      /*TODO: make this work without FP support; the API is currently eliminated

-       * if neither floating point APIs nor internal floating point arithmetic

-       * are enabled.

-       */

-      *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");

-      *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),

-         "sCAL height");

-      return (PNG_INFO_sCAL);

-   }

-

-   return(0);

-}

-#    endif /* FLOATING_ARITHMETIC */

-#  endif /* FIXED_POINT */

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-png_uint_32 PNGAPI

-png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    int *unit, double *width, double *height)

-{

-   if (png_ptr != NULL && info_ptr != NULL &&

-       (info_ptr->valid & PNG_INFO_sCAL))

-   {

-      *unit = info_ptr->scal_unit;

-      *width = atof(info_ptr->scal_s_width);

-      *height = atof(info_ptr->scal_s_height);

-      return (PNG_INFO_sCAL);

-   }

-

-   return(0);

-}

-#  endif /* FLOATING POINT */

-png_uint_32 PNGAPI

-png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    int *unit, png_charpp width, png_charpp height)

-{

-   if (png_ptr != NULL && info_ptr != NULL &&

-       (info_ptr->valid & PNG_INFO_sCAL))

-   {

-      *unit = info_ptr->scal_unit;

-      *width = info_ptr->scal_s_width;

-      *height = info_ptr->scal_s_height;

-      return (PNG_INFO_sCAL);

-   }

-

-   return(0);

-}

-#endif /* sCAL */

-

-#ifdef PNG_pHYs_SUPPORTED

-png_uint_32 PNGAPI

-png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr,

-    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)

-{

-   png_uint_32 retval = 0;

-

-   png_debug1(1, "in %s retrieval function", "pHYs");

-

-   if (png_ptr != NULL && info_ptr != NULL &&

-       (info_ptr->valid & PNG_INFO_pHYs))

-   {

-      if (res_x != NULL)

-      {

-         *res_x = info_ptr->x_pixels_per_unit;

-         retval |= PNG_INFO_pHYs;

-      }

-

-      if (res_y != NULL)

-      {

-         *res_y = info_ptr->y_pixels_per_unit;

-         retval |= PNG_INFO_pHYs;

-      }

-

-      if (unit_type != NULL)

-      {

-         *unit_type = (int)info_ptr->phys_unit_type;

-         retval |= PNG_INFO_pHYs;

-      }

-   }

-

-   return (retval);

-}

-#endif /* pHYs */

-

-png_uint_32 PNGAPI

-png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_colorp *palette, int *num_palette)

-{

-   png_debug1(1, "in %s retrieval function", "PLTE");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE)

-       && palette != NULL)

-   {

-      *palette = info_ptr->palette;

-      *num_palette = info_ptr->num_palette;

-      png_debug1(3, "num_palette = %d", *num_palette);

-      return (PNG_INFO_PLTE);

-   }

-

-   return (0);

-}

-

-#ifdef PNG_sBIT_SUPPORTED

-png_uint_32 PNGAPI

-png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_color_8p *sig_bit)

-{

-   png_debug1(1, "in %s retrieval function", "sBIT");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)

-       && sig_bit != NULL)

-   {

-      *sig_bit = &(info_ptr->sig_bit);

-      return (PNG_INFO_sBIT);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_TEXT_SUPPORTED

-int PNGAPI

-png_get_text(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_textp *text_ptr, int *num_text)

-{

-   if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)

-   {

-      png_debug1(1, "in 0x%lx retrieval function",

-         (unsigned long)png_ptr->chunk_name);

-

-      if (text_ptr != NULL)

-         *text_ptr = info_ptr->text;

-

-      if (num_text != NULL)

-         *num_text = info_ptr->num_text;

-

-      return info_ptr->num_text;

-   }

-

-   if (num_text != NULL)

-      *num_text = 0;

-

-   return(0);

-}

-#endif

-

-#ifdef PNG_tIME_SUPPORTED

-png_uint_32 PNGAPI

-png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_timep *mod_time)

-{

-   png_debug1(1, "in %s retrieval function", "tIME");

-

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)

-       && mod_time != NULL)

-   {

-      *mod_time = &(info_ptr->mod_time);

-      return (PNG_INFO_tIME);

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_tRNS_SUPPORTED

-png_uint_32 PNGAPI

-png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)

-{

-   png_uint_32 retval = 0;

-   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))

-   {

-      png_debug1(1, "in %s retrieval function", "tRNS");

-

-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         if (trans_alpha != NULL)

-         {

-            *trans_alpha = info_ptr->trans_alpha;

-            retval |= PNG_INFO_tRNS;

-         }

-

-         if (trans_color != NULL)

-            *trans_color = &(info_ptr->trans_color);

-      }

-

-      else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */

-      {

-         if (trans_color != NULL)

-         {

-            *trans_color = &(info_ptr->trans_color);

-            retval |= PNG_INFO_tRNS;

-         }

-

-         if (trans_alpha != NULL)

-            *trans_alpha = NULL;

-      }

-

-      if (num_trans != NULL)

-      {

-         *num_trans = info_ptr->num_trans;

-         retval |= PNG_INFO_tRNS;

-      }

-   }

-

-   return (retval);

-}

-#endif

-

-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED

-int PNGAPI

-png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_unknown_chunkpp unknowns)

-{

-   if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL)

-   {

-      *unknowns = info_ptr->unknown_chunks;

-      return info_ptr->unknown_chunks_num;

-   }

-

-   return (0);

-}

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-png_byte PNGAPI

-png_get_rgb_to_gray_status (png_const_structrp png_ptr)

-{

-   return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0);

-}

-#endif

-

-#ifdef PNG_USER_CHUNKS_SUPPORTED

-png_voidp PNGAPI

-png_get_user_chunk_ptr(png_const_structrp png_ptr)

-{

-   return (png_ptr ? png_ptr->user_chunk_ptr : NULL);

-}

-#endif

-

-png_size_t PNGAPI

-png_get_compression_buffer_size(png_const_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return 0;

-

-#  ifdef PNG_WRITE_SUPPORTED

-      if (png_ptr->mode & PNG_IS_READ_STRUCT)

-#  endif

-   {

-#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-         return png_ptr->IDAT_read_size;

-#     else

-         return PNG_IDAT_READ_SIZE;

-#     endif

-   }

-

-#  ifdef PNG_WRITE_SUPPORTED

-      else

-         return png_ptr->zbuffer_size;

-#  endif

-}

-

-#ifdef PNG_SET_USER_LIMITS_SUPPORTED

-/* These functions were added to libpng 1.2.6 and were enabled

- * by default in libpng-1.4.0 */

-png_uint_32 PNGAPI

-png_get_user_width_max (png_const_structrp png_ptr)

-{

-   return (png_ptr ? png_ptr->user_width_max : 0);

-}

-

-png_uint_32 PNGAPI

-png_get_user_height_max (png_const_structrp png_ptr)

-{

-   return (png_ptr ? png_ptr->user_height_max : 0);

-}

-

-/* This function was added to libpng 1.4.0 */

-png_uint_32 PNGAPI

-png_get_chunk_cache_max (png_const_structrp png_ptr)

-{

-   return (png_ptr ? png_ptr->user_chunk_cache_max : 0);

-}

-

-/* This function was added to libpng 1.4.1 */

-png_alloc_size_t PNGAPI

-png_get_chunk_malloc_max (png_const_structrp png_ptr)

-{

-   return (png_ptr ? png_ptr->user_chunk_malloc_max : 0);

-}

-#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */

-

-/* These functions were added to libpng 1.4.0 */

-#ifdef PNG_IO_STATE_SUPPORTED

-png_uint_32 PNGAPI

-png_get_io_state (png_const_structrp png_ptr)

-{

-   return png_ptr->io_state;

-}

-

-png_uint_32 PNGAPI

-png_get_io_chunk_type (png_const_structrp png_ptr)

-{

-   return png_ptr->chunk_name;

-}

-#endif /* ?PNG_IO_STATE_SUPPORTED */

-

-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED

-#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED

-int PNGAPI

-png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr)

-{

-   if (png_ptr != NULL && info_ptr != NULL)

-      return png_ptr->num_palette_max;

-

-   return (-1);

-}

-#  endif

-#endif

-

-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */

+
+/* pngget.c - retrieval of values from info struct
+ *
+ * Last changed in libpng 1.6.17 [March 26, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+png_uint_32 PNGAPI
+png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 flag)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->valid & flag);
+
+   return(0);
+}
+
+png_size_t PNGAPI
+png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->rowbytes);
+
+   return(0);
+}
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+png_bytepp PNGAPI
+png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->row_pointers);
+
+   return(0);
+}
+#endif
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Easy access to info, added in libpng-0.99 */
+png_uint_32 PNGAPI
+png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->width;
+
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->height;
+
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->bit_depth;
+
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->color_type;
+
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->filter_type;
+
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->interlace_type;
+
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return info_ptr->compression_type;
+
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
+   info_ptr)
+{
+#ifdef PNG_pHYs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0)
+      {
+         png_debug1(1, "in %s retrieval function",
+             "png_get_x_pixels_per_meter");
+
+         if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
+            return (info_ptr->x_pixels_per_unit);
+      }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
+    info_ptr)
+{
+#ifdef PNG_pHYs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function",
+          "png_get_y_pixels_per_meter");
+
+      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
+         return (info_ptr->y_pixels_per_unit);
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+#ifdef PNG_pHYs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter");
+
+      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER &&
+          info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit)
+         return (info_ptr->x_pixels_per_unit);
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp
+   info_ptr)
+{
+#ifdef PNG_READ_pHYs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio");
+
+      if (info_ptr->x_pixels_per_unit != 0)
+         return ((float)((float)info_ptr->y_pixels_per_unit
+             /(float)info_ptr->x_pixels_per_unit));
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return ((float)0.0);
+}
+#endif
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_fixed_point PNGAPI
+png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,
+    png_const_inforp info_ptr)
+{
+#ifdef PNG_READ_pHYs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0 &&
+       info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 &&
+       info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX &&
+       info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX)
+   {
+      png_fixed_point res;
+
+      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed");
+
+      /* The following casts work because a PNG 4 byte integer only has a valid
+       * range of 0..2^31-1; otherwise the cast might overflow.
+       */
+      if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1,
+          (png_int_32)info_ptr->x_pixels_per_unit) != 0)
+         return res;
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return 0;
+}
+#endif
+
+png_int_32 PNGAPI
+png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_oFFs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns");
+
+      if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
+         return (info_ptr->x_offset);
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_oFFs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns");
+
+      if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
+         return (info_ptr->y_offset);
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_oFFs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels");
+
+      if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
+         return (info_ptr->x_offset);
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_oFFs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels");
+
+      if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
+         return (info_ptr->y_offset);
+   }
+#else
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(info_ptr)
+#endif
+
+   return (0);
+}
+
+#ifdef PNG_INCH_CONVERSIONS_SUPPORTED
+static png_uint_32
+ppi_from_ppm(png_uint_32 ppm)
+{
+#if 0
+   /* The conversion is *(2.54/100), in binary (32 digits):
+    * .00000110100000001001110101001001
+    */
+   png_uint_32 t1001, t1101;
+   ppm >>= 1;                  /* .1 */
+   t1001 = ppm + (ppm >> 3);   /* .1001 */
+   t1101 = t1001 + (ppm >> 1); /* .1101 */
+   ppm >>= 20;                 /* .000000000000000000001 */
+   t1101 += t1101 >> 15;       /* .1101000000000001101 */
+   t1001 >>= 11;               /* .000000000001001 */
+   t1001 += t1001 >> 12;       /* .000000000001001000000001001 */
+   ppm += t1001;               /* .000000000001001000001001001 */
+   ppm += t1101;               /* .110100000001001110101001001 */
+   return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */
+#else
+   /* The argument is a PNG unsigned integer, so it is not permitted
+    * to be bigger than 2^31.
+    */
+   png_fixed_point result;
+   if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127,
+       5000) != 0)
+      return result;
+
+   /* Overflow. */
+   return 0;
+#endif
+}
+
+png_uint_32 PNGAPI
+png_get_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr));
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr));
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr));
+}
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+static png_fixed_point
+png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns)
+{
+   /* Convert from metres * 1,000,000 to inches * 100,000, meters to
+    * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127.
+    * Notice that this can overflow - a warning is output and 0 is
+    * returned.
+    */
+   return png_muldiv_warn(png_ptr, microns, 500, 127);
+}
+
+png_fixed_point PNGAPI
+png_get_x_offset_inches_fixed(png_const_structrp png_ptr,
+    png_const_inforp info_ptr)
+{
+   return png_fixed_inches_from_microns(png_ptr,
+       png_get_x_offset_microns(png_ptr, info_ptr));
+}
+#endif
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_fixed_point PNGAPI
+png_get_y_offset_inches_fixed(png_const_structrp png_ptr,
+    png_const_inforp info_ptr)
+{
+   return png_fixed_inches_from_microns(png_ptr,
+       png_get_y_offset_microns(png_ptr, info_ptr));
+}
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_x_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   /* To avoid the overflow do the conversion directly in floating
+    * point.
+    */
+   return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937);
+}
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_y_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   /* To avoid the overflow do the conversion directly in floating
+    * point.
+    */
+   return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937);
+}
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+png_uint_32 PNGAPI
+png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "pHYs");
+
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+
+         if (*unit_type == 1)
+         {
+            if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50);
+            if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50);
+         }
+      }
+   }
+
+   return (retval);
+}
+#endif /* pHYs */
+#endif  /* INCH_CONVERSIONS */
+
+/* png_get_channels really belongs in here, too, but it's been around longer */
+
+#endif  /* EASY_ACCESS */
+
+
+png_byte PNGAPI
+png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->channels);
+
+   return (0);
+}
+
+#ifdef PNG_READ_SUPPORTED
+png_const_bytep PNGAPI
+png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->signature);
+
+   return (NULL);
+}
+#endif
+
+#ifdef PNG_bKGD_SUPPORTED
+png_uint_32 PNGAPI
+png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
+   png_color_16p *background)
+{
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_bKGD) != 0 &&
+       background != NULL)
+   {
+      png_debug1(1, "in %s retrieval function", "bKGD");
+
+      *background = &(info_ptr->background);
+      return (PNG_INFO_bKGD);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the
+ * same time to correct the rgb grayscale coefficient defaults obtained from the
+ * cHRM chunk in 1.5.4
+ */
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    double *white_x, double *white_y, double *red_x, double *red_y,
+    double *green_x, double *green_y, double *blue_x, double *blue_y)
+{
+   /* Quiet API change: this code used to only return the end points if a cHRM
+    * chunk was present, but the end points can also come from iCCP or sRGB
+    * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and
+    * the png_set_ APIs merely check that set end points are mutually
+    * consistent.
+    */
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "cHRM");
+
+      if (white_x != NULL)
+         *white_x = png_float(png_ptr,
+            info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
+      if (white_y != NULL)
+         *white_y = png_float(png_ptr,
+            info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y");
+      if (red_x != NULL)
+         *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx,
+            "cHRM red X");
+      if (red_y != NULL)
+         *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy,
+            "cHRM red Y");
+      if (green_x != NULL)
+         *green_x = png_float(png_ptr,
+            info_ptr->colorspace.end_points_xy.greenx, "cHRM green X");
+      if (green_y != NULL)
+         *green_y = png_float(png_ptr,
+            info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y");
+      if (blue_x != NULL)
+         *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex,
+            "cHRM blue X");
+      if (blue_y != NULL)
+         *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,
+            "cHRM blue Y");
+      return (PNG_INFO_cHRM);
+   }
+
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
+   double *red_X, double *red_Y, double *red_Z, double *green_X,
+   double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
+   double *blue_Z)
+{
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
+
+      if (red_X != NULL)
+         *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,
+            "cHRM red X");
+      if (red_Y != NULL)
+         *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y,
+            "cHRM red Y");
+      if (red_Z != NULL)
+         *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z,
+            "cHRM red Z");
+      if (green_X != NULL)
+         *green_X = png_float(png_ptr,
+            info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X");
+      if (green_Y != NULL)
+         *green_Y = png_float(png_ptr,
+            info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y");
+      if (green_Z != NULL)
+         *green_Z = png_float(png_ptr,
+            info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z");
+      if (blue_X != NULL)
+         *blue_X = png_float(png_ptr,
+            info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X");
+      if (blue_Y != NULL)
+         *blue_Y = png_float(png_ptr,
+            info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y");
+      if (blue_Z != NULL)
+         *blue_Z = png_float(png_ptr,
+            info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
+      return (PNG_INFO_cHRM);
+   }
+
+   return (0);
+}
+#  endif
+
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
+    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
+    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
+    png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
+    png_fixed_point *int_blue_Z)
+{
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
+
+      if (int_red_X != NULL)
+         *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X;
+      if (int_red_Y != NULL)
+         *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y;
+      if (int_red_Z != NULL)
+         *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z;
+      if (int_green_X != NULL)
+         *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X;
+      if (int_green_Y != NULL)
+         *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y;
+      if (int_green_Z != NULL)
+         *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z;
+      if (int_blue_X != NULL)
+         *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X;
+      if (int_blue_Y != NULL)
+         *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y;
+      if (int_blue_Z != NULL)
+         *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z;
+      return (PNG_INFO_cHRM);
+   }
+
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,
+    png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,
+    png_fixed_point *blue_x, png_fixed_point *blue_y)
+{
+   png_debug1(1, "in %s retrieval function", "cHRM");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
+   {
+      if (white_x != NULL)
+         *white_x = info_ptr->colorspace.end_points_xy.whitex;
+      if (white_y != NULL)
+         *white_y = info_ptr->colorspace.end_points_xy.whitey;
+      if (red_x != NULL)
+         *red_x = info_ptr->colorspace.end_points_xy.redx;
+      if (red_y != NULL)
+         *red_y = info_ptr->colorspace.end_points_xy.redy;
+      if (green_x != NULL)
+         *green_x = info_ptr->colorspace.end_points_xy.greenx;
+      if (green_y != NULL)
+         *green_y = info_ptr->colorspace.end_points_xy.greeny;
+      if (blue_x != NULL)
+         *blue_x = info_ptr->colorspace.end_points_xy.bluex;
+      if (blue_y != NULL)
+         *blue_y = info_ptr->colorspace.end_points_xy.bluey;
+      return (PNG_INFO_cHRM);
+   }
+
+   return (0);
+}
+#  endif
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_fixed_point *file_gamma)
+{
+   png_debug1(1, "in %s retrieval function", "gAMA");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
+      file_gamma != NULL)
+   {
+      *file_gamma = info_ptr->colorspace.gamma;
+      return (PNG_INFO_gAMA);
+   }
+
+   return (0);
+}
+#  endif
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    double *file_gamma)
+{
+   png_debug1(1, "in %s retrieval function", "gAMA(float)");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 &&
+      file_gamma != NULL)
+   {
+      *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,
+         "png_get_gAMA");
+      return (PNG_INFO_gAMA);
+   }
+
+   return (0);
+}
+#  endif
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *file_srgb_intent)
+{
+   png_debug1(1, "in %s retrieval function", "sRGB");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL)
+   {
+      *file_srgb_intent = info_ptr->colorspace.rendering_intent;
+      return (PNG_INFO_sRGB);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+png_uint_32 PNGAPI
+png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_charpp name, int *compression_type,
+    png_bytepp profile, png_uint_32 *proflen)
+{
+   png_debug1(1, "in %s retrieval function", "iCCP");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_iCCP) != 0 &&
+       name != NULL && compression_type != NULL && profile != NULL &&
+           proflen != NULL)
+   {
+      *name = info_ptr->iccp_name;
+      *profile = info_ptr->iccp_profile;
+      *proflen = png_get_uint_32(info_ptr->iccp_profile);
+      /* This is somewhat irrelevant since the profile data returned has
+       * actually been uncompressed.
+       */
+      *compression_type = PNG_COMPRESSION_TYPE_BASE;
+      return (PNG_INFO_iCCP);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+int PNGAPI
+png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_sPLT_tpp spalettes)
+{
+   if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)
+   {
+      *spalettes = info_ptr->splt_palettes;
+      return info_ptr->splt_palettes_num;
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+png_uint_32 PNGAPI
+png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_16p *hist)
+{
+   png_debug1(1, "in %s retrieval function", "hIST");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_hIST) != 0 && hist != NULL)
+   {
+      *hist = info_ptr->hist;
+      return (PNG_INFO_hIST);
+   }
+
+   return (0);
+}
+#endif
+
+png_uint_32 PNGAPI
+png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *width, png_uint_32 *height, int *bit_depth,
+    int *color_type, int *interlace_type, int *compression_type,
+    int *filter_type)
+{
+   png_debug1(1, "in %s retrieval function", "IHDR");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return (0);
+
+   if (width != NULL)
+       *width = info_ptr->width;
+
+   if (height != NULL)
+       *height = info_ptr->height;
+
+   if (bit_depth != NULL)
+       *bit_depth = info_ptr->bit_depth;
+
+   if (color_type != NULL)
+       *color_type = info_ptr->color_type;
+
+   if (compression_type != NULL)
+      *compression_type = info_ptr->compression_type;
+
+   if (filter_type != NULL)
+      *filter_type = info_ptr->filter_type;
+
+   if (interlace_type != NULL)
+      *interlace_type = info_ptr->interlace_type;
+
+   /* This is redundant if we can be sure that the info_ptr values were all
+    * assigned in png_set_IHDR().  We do the check anyhow in case an
+    * application has ignored our advice not to mess with the members
+    * of info_ptr directly.
+    */
+   png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+       info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
+       info_ptr->compression_type, info_ptr->filter_type);
+
+   return (1);
+}
+
+#ifdef PNG_oFFs_SUPPORTED
+png_uint_32 PNGAPI
+png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)
+{
+   png_debug1(1, "in %s retrieval function", "oFFs");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_oFFs) != 0 &&
+       offset_x != NULL && offset_y != NULL && unit_type != NULL)
+   {
+      *offset_x = info_ptr->x_offset;
+      *offset_y = info_ptr->y_offset;
+      *unit_type = (int)info_ptr->offset_unit_type;
+      return (PNG_INFO_oFFs);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+png_uint_32 PNGAPI
+png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,
+    png_charp *units, png_charpp *params)
+{
+   png_debug1(1, "in %s retrieval function", "pCAL");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pCAL) != 0 &&
+       purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
+       nparams != NULL && units != NULL && params != NULL)
+   {
+      *purpose = info_ptr->pcal_purpose;
+      *X0 = info_ptr->pcal_X0;
+      *X1 = info_ptr->pcal_X1;
+      *type = (int)info_ptr->pcal_type;
+      *nparams = (int)info_ptr->pcal_nparams;
+      *units = info_ptr->pcal_units;
+      *params = info_ptr->pcal_params;
+      return (PNG_INFO_pCAL);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+#    if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \
+         defined(PNG_FLOATING_POINT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *unit, png_fixed_point *width, png_fixed_point *height)
+{
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL) != 0)
+   {
+      *unit = info_ptr->scal_unit;
+      /*TODO: make this work without FP support; the API is currently eliminated
+       * if neither floating point APIs nor internal floating point arithmetic
+       * are enabled.
+       */
+      *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");
+      *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),
+         "sCAL height");
+      return (PNG_INFO_sCAL);
+   }
+
+   return(0);
+}
+#    endif /* FLOATING_ARITHMETIC */
+#  endif /* FIXED_POINT */
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *unit, double *width, double *height)
+{
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL) != 0)
+   {
+      *unit = info_ptr->scal_unit;
+      *width = atof(info_ptr->scal_s_width);
+      *height = atof(info_ptr->scal_s_height);
+      return (PNG_INFO_sCAL);
+   }
+
+   return(0);
+}
+#  endif /* FLOATING POINT */
+png_uint_32 PNGAPI
+png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *unit, png_charpp width, png_charpp height)
+{
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL) != 0)
+   {
+      *unit = info_ptr->scal_unit;
+      *width = info_ptr->scal_s_width;
+      *height = info_ptr->scal_s_height;
+      return (PNG_INFO_sCAL);
+   }
+
+   return(0);
+}
+#endif /* sCAL */
+
+#ifdef PNG_pHYs_SUPPORTED
+png_uint_32 PNGAPI
+png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   png_debug1(1, "in %s retrieval function", "pHYs");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_pHYs) != 0)
+   {
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+      }
+   }
+
+   return (retval);
+}
+#endif /* pHYs */
+
+png_uint_32 PNGAPI
+png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_colorp *palette, int *num_palette)
+{
+   png_debug1(1, "in %s retrieval function", "PLTE");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_PLTE) != 0 && palette != NULL)
+   {
+      *palette = info_ptr->palette;
+      *num_palette = info_ptr->num_palette;
+      png_debug1(3, "num_palette = %d", *num_palette);
+      return (PNG_INFO_PLTE);
+   }
+
+   return (0);
+}
+
+#ifdef PNG_sBIT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_color_8p *sig_bit)
+{
+   png_debug1(1, "in %s retrieval function", "sBIT");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sBIT) != 0 && sig_bit != NULL)
+   {
+      *sig_bit = &(info_ptr->sig_bit);
+      return (PNG_INFO_sBIT);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+int PNGAPI
+png_get_text(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_textp *text_ptr, int *num_text)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
+   {
+      png_debug1(1, "in 0x%lx retrieval function",
+         (unsigned long)png_ptr->chunk_name);
+
+      if (text_ptr != NULL)
+         *text_ptr = info_ptr->text;
+
+      if (num_text != NULL)
+         *num_text = info_ptr->num_text;
+
+      return info_ptr->num_text;
+   }
+
+   if (num_text != NULL)
+      *num_text = 0;
+
+   return(0);
+}
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+png_uint_32 PNGAPI
+png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_timep *mod_time)
+{
+   png_debug1(1, "in %s retrieval function", "tIME");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_tIME) != 0 && mod_time != NULL)
+   {
+      *mod_time = &(info_ptr->mod_time);
+      return (PNG_INFO_tIME);
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+png_uint_32 PNGAPI
+png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)
+{
+   png_uint_32 retval = 0;
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_tRNS) != 0)
+   {
+      png_debug1(1, "in %s retrieval function", "tRNS");
+
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (trans_alpha != NULL)
+         {
+            *trans_alpha = info_ptr->trans_alpha;
+            retval |= PNG_INFO_tRNS;
+         }
+
+         if (trans_color != NULL)
+            *trans_color = &(info_ptr->trans_color);
+      }
+
+      else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
+      {
+         if (trans_color != NULL)
+         {
+            *trans_color = &(info_ptr->trans_color);
+            retval |= PNG_INFO_tRNS;
+         }
+
+         if (trans_alpha != NULL)
+            *trans_alpha = NULL;
+      }
+
+      if (num_trans != NULL)
+      {
+         *num_trans = info_ptr->num_trans;
+         retval |= PNG_INFO_tRNS;
+      }
+   }
+
+   return (retval);
+}
+#endif
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+int PNGAPI
+png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_unknown_chunkpp unknowns)
+{
+   if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL)
+   {
+      *unknowns = info_ptr->unknown_chunks;
+      return info_ptr->unknown_chunks_num;
+   }
+
+   return (0);
+}
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+png_byte PNGAPI
+png_get_rgb_to_gray_status (png_const_structrp png_ptr)
+{
+   return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0);
+}
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+png_voidp PNGAPI
+png_get_user_chunk_ptr(png_const_structrp png_ptr)
+{
+   return (png_ptr ? png_ptr->user_chunk_ptr : NULL);
+}
+#endif
+
+png_size_t PNGAPI
+png_get_compression_buffer_size(png_const_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return 0;
+
+#ifdef PNG_WRITE_SUPPORTED
+      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
+#endif
+   {
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+         return png_ptr->IDAT_read_size;
+#else
+         return PNG_IDAT_READ_SIZE;
+#endif
+   }
+
+#ifdef PNG_WRITE_SUPPORTED
+      else
+         return png_ptr->zbuffer_size;
+#endif
+}
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* These functions were added to libpng 1.2.6 and were enabled
+ * by default in libpng-1.4.0 */
+png_uint_32 PNGAPI
+png_get_user_width_max (png_const_structrp png_ptr)
+{
+   return (png_ptr ? png_ptr->user_width_max : 0);
+}
+
+png_uint_32 PNGAPI
+png_get_user_height_max (png_const_structrp png_ptr)
+{
+   return (png_ptr ? png_ptr->user_height_max : 0);
+}
+
+/* This function was added to libpng 1.4.0 */
+png_uint_32 PNGAPI
+png_get_chunk_cache_max (png_const_structrp png_ptr)
+{
+   return (png_ptr ? png_ptr->user_chunk_cache_max : 0);
+}
+
+/* This function was added to libpng 1.4.1 */
+png_alloc_size_t PNGAPI
+png_get_chunk_malloc_max (png_const_structrp png_ptr)
+{
+   return (png_ptr ? png_ptr->user_chunk_malloc_max : 0);
+}
+#endif /* SET_USER_LIMITS */
+
+/* These functions were added to libpng 1.4.0 */
+#ifdef PNG_IO_STATE_SUPPORTED
+png_uint_32 PNGAPI
+png_get_io_state (png_const_structrp png_ptr)
+{
+   return png_ptr->io_state;
+}
+
+png_uint_32 PNGAPI
+png_get_io_chunk_type (png_const_structrp png_ptr)
+{
+   return png_ptr->chunk_name;
+}
+#endif /* IO_STATE */
+
+#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+int PNGAPI
+png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return png_ptr->num_palette_max;
+
+   return (-1);
+}
+#  endif
+#endif
+
+#endif /* READ || WRITE */
diff --git a/third_party/lpng_v163/pnginfo.h b/third_party/libpng/pnginfo.h
similarity index 98%
rename from third_party/lpng_v163/pnginfo.h
rename to third_party/libpng/pnginfo.h
index 683b7ea..4bd264b 100644
--- a/third_party/lpng_v163/pnginfo.h
+++ b/third_party/libpng/pnginfo.h
@@ -1,260 +1,259 @@
-

-/* pnginfo.h - header file for PNG reference library

- *

- * 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.)

- *

- * Last changed in libpng 1.6.1 [March 28, 2013]

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

- /* png_info is a structure that holds the information in a PNG file so

- * that the application can find out the characteristics of the image.

- * If you are reading the file, this structure will tell you what is

- * in the PNG file.  If you are writing the file, fill in the information

- * you want to put into the PNG file, using png_set_*() functions, then

- * call png_write_info().

- *

- * The names chosen should be very close to the PNG specification, so

- * consult that document for information about the meaning of each field.

- *

- * With libpng < 0.95, it was only possible to directly set and read the

- * the values in the png_info_struct, which meant that the contents and

- * order of the values had to remain fixed.  With libpng 0.95 and later,

- * however, there are now functions that abstract the contents of

- * png_info_struct from the application, so this makes it easier to use

- * libpng with dynamic libraries, and even makes it possible to use

- * libraries that don't have all of the libpng ancillary chunk-handing

- * functionality.  In libpng-1.5.0 this was moved into a separate private

- * file that is not visible to applications.

- *

- * The following members may have allocated storage attached that should be

- * cleaned up before the structure is discarded: palette, trans, text,

- * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,

- * splt_palettes, scal_unit, row_pointers, and unknowns.   By default, these

- * are automatically freed when the info structure is deallocated, if they were

- * allocated internally by libpng.  This behavior can be changed by means

- * of the png_data_freer() function.

- *

- * More allocation details: all the chunk-reading functions that

- * change these members go through the corresponding png_set_*

- * functions.  A function to clear these members is available: see

- * png_free_data().  The png_set_* functions do not depend on being

- * able to point info structure members to any of the storage they are

- * passed (they make their own copies), EXCEPT that the png_set_text

- * functions use the same storage passed to them in the text_ptr or

- * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns

- * functions do not make their own copies.

- */

-#ifndef PNGINFO_H

-#define PNGINFO_H

-

-struct png_info_def

-{

-   /* The following are necessary for every PNG file */

-   png_uint_32 width;  /* width of image in pixels (from IHDR) */

-   png_uint_32 height; /* height of image in pixels (from IHDR) */

-   png_uint_32 valid;  /* valid chunk data (see PNG_INFO_ below) */

-   png_size_t rowbytes; /* bytes needed to hold an untransformed row */

-   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */

-   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */

-   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */

-   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */

-   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */

-   /* The following three should have been named *_method not *_type */

-   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */

-   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */

-   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */

-

-   /* The following are set by png_set_IHDR, called from the application on

-    * write, but the are never actually used by the write code.

-    */

-   png_byte channels;       /* number of data channels per pixel (1, 2, 3, 4) */

-   png_byte pixel_depth;    /* number of bits per pixel */

-   png_byte spare_byte;     /* to align the data, and for future use */

-

-#ifdef PNG_READ_SUPPORTED

-   /* This is never set during write */

-   png_byte signature[8];   /* magic bytes read by libpng from start of file */

-#endif

-

-   /* The rest of the data is optional.  If you are reading, check the

-    * valid field to see if the information in these are valid.  If you

-    * are writing, set the valid field to those chunks you want written,

-    * and initialize the appropriate fields below.

-    */

-

-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)

-   /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are

-    * defined.  When COLORSPACE is switched on all the colorspace-defining

-    * chunks should be enabled, when GAMMA is switched on all the gamma-defining

-    * chunks should be enabled.  If this is not done it becomes possible to read

-    * inconsistent PNG files and assign a probably incorrect interpretation to

-    * the information.  (In other words, by carefully choosing which chunks to

-    * recognize the system configuration can select an interpretation for PNG

-    * files containing ambiguous data and this will result in inconsistent

-    * behavior between different libpng builds!)

-    */

-   png_colorspace colorspace;

-#endif

-

-#ifdef PNG_iCCP_SUPPORTED

-   /* iCCP chunk data. */

-   png_charp iccp_name;     /* profile name */

-   png_bytep iccp_profile;  /* International Color Consortium profile data */

-   png_uint_32 iccp_proflen;  /* ICC profile data length */

-#endif

-

-#ifdef PNG_TEXT_SUPPORTED

-   /* The tEXt, and zTXt chunks contain human-readable textual data in

-    * uncompressed, compressed, and optionally compressed forms, respectively.

-    * The data in "text" is an array of pointers to uncompressed,

-    * null-terminated C strings. Each chunk has a keyword that describes the

-    * textual data contained in that chunk.  Keywords are not required to be

-    * unique, and the text string may be empty.  Any number of text chunks may

-    * be in an image.

-    */

-   int num_text; /* number of comments read or comments to write */

-   int max_text; /* current size of text array */

-   png_textp text; /* array of comments read or comments to write */

-#endif /* PNG_TEXT_SUPPORTED */

-

-#ifdef PNG_tIME_SUPPORTED

-   /* The tIME chunk holds the last time the displayed image data was

-    * modified.  See the png_time struct for the contents of this struct.

-    */

-   png_time mod_time;

-#endif

-

-#ifdef PNG_sBIT_SUPPORTED

-   /* The sBIT chunk specifies the number of significant high-order bits

-    * in the pixel data.  Values are in the range [1, bit_depth], and are

-    * only specified for the channels in the pixel data.  The contents of

-    * the low-order bits is not specified.  Data is valid if

-    * (valid & PNG_INFO_sBIT) is non-zero.

-    */

-   png_color_8 sig_bit; /* significant bits in color channels */

-#endif

-

-#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \

-defined(PNG_READ_BACKGROUND_SUPPORTED)

-   /* The tRNS chunk supplies transparency data for paletted images and

-    * other image types that don't need a full alpha channel.  There are

-    * "num_trans" transparency values for a paletted image, stored in the

-    * same order as the palette colors, starting from index 0.  Values

-    * for the data are in the range [0, 255], ranging from fully transparent

-    * to fully opaque, respectively.  For non-paletted images, there is a

-    * single color specified that should be treated as fully transparent.

-    * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.

-    */

-   png_bytep trans_alpha;    /* alpha values for paletted image */

-   png_color_16 trans_color; /* transparent color for non-palette image */

-#endif

-

-#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)

-   /* The bKGD chunk gives the suggested image background color if the

-    * display program does not have its own background color and the image

-    * is needs to composited onto a background before display.  The colors

-    * in "background" are normally in the same color space/depth as the

-    * pixel data.  Data is valid if (valid & PNG_INFO_bKGD) is non-zero.

-    */

-   png_color_16 background;

-#endif

-

-#ifdef PNG_oFFs_SUPPORTED

-   /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards

-    * and downwards from the top-left corner of the display, page, or other

-    * application-specific co-ordinate space.  See the PNG_OFFSET_ defines

-    * below for the unit types.  Valid if (valid & PNG_INFO_oFFs) non-zero.

-    */

-   png_int_32 x_offset; /* x offset on page */

-   png_int_32 y_offset; /* y offset on page */

-   png_byte offset_unit_type; /* offset units type */

-#endif

-

-#ifdef PNG_pHYs_SUPPORTED

-   /* The pHYs chunk gives the physical pixel density of the image for

-    * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_

-    * defines below).  Data is valid if (valid & PNG_INFO_pHYs) is non-zero.

-    */

-   png_uint_32 x_pixels_per_unit; /* horizontal pixel density */

-   png_uint_32 y_pixels_per_unit; /* vertical pixel density */

-   png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */

-#endif

-

-#ifdef PNG_hIST_SUPPORTED

-   /* The hIST chunk contains the relative frequency or importance of the

-    * various palette entries, so that a viewer can intelligently select a

-    * reduced-color palette, if required.  Data is an array of "num_palette"

-    * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)

-    * is non-zero.

-    */

-   png_uint_16p hist;

-#endif

-

-#ifdef PNG_pCAL_SUPPORTED

-   /* The pCAL chunk describes a transformation between the stored pixel

-    * values and original physical data values used to create the image.

-    * The integer range [0, 2^bit_depth - 1] maps to the floating-point

-    * range given by [pcal_X0, pcal_X1], and are further transformed by a

-    * (possibly non-linear) transformation function given by "pcal_type"

-    * and "pcal_params" into "pcal_units".  Please see the PNG_EQUATION_

-    * defines below, and the PNG-Group's PNG extensions document for a

-    * complete description of the transformations and how they should be

-    * implemented, and for a description of the ASCII parameter strings.

-    * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.

-    */

-   png_charp pcal_purpose;  /* pCAL chunk description string */

-   png_int_32 pcal_X0;      /* minimum value */

-   png_int_32 pcal_X1;      /* maximum value */

-   png_charp pcal_units;    /* Latin-1 string giving physical units */

-   png_charpp pcal_params;  /* ASCII strings containing parameter values */

-   png_byte pcal_type;      /* equation type (see PNG_EQUATION_ below) */

-   png_byte pcal_nparams;   /* number of parameters given in pcal_params */

-#endif

-

-/* New members added in libpng-1.0.6 */

-   png_uint_32 free_me;     /* flags items libpng is responsible for freeing */

-

-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED

-   /* Storage for unknown chunks that the library doesn't recognize. */

-   png_unknown_chunkp unknown_chunks;

-

-   /* The type of this field is limited by the type of 

-    * png_struct::user_chunk_cache_max, else overflow can occur.

-    */

-   int                unknown_chunks_num;

-#endif

-

-#ifdef PNG_sPLT_SUPPORTED

-   /* Data on sPLT chunks (there may be more than one). */

-   png_sPLT_tp splt_palettes;

-   int         splt_palettes_num; /* Match type returned by png_get API */

-#endif

-

-#ifdef PNG_sCAL_SUPPORTED

-   /* The sCAL chunk describes the actual physical dimensions of the

-    * subject matter of the graphic.  The chunk contains a unit specification

-    * a byte value, and two ASCII strings representing floating-point

-    * values.  The values are width and height corresponsing to one pixel

-    * in the image.  Data values are valid if (valid & PNG_INFO_sCAL) is

-    * non-zero.

-    */

-   png_byte scal_unit;         /* unit of physical scale */

-   png_charp scal_s_width;     /* string containing height */

-   png_charp scal_s_height;    /* string containing width */

-#endif

-

-#ifdef PNG_INFO_IMAGE_SUPPORTED

-   /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)

-      non-zero */

-   /* Data valid if (valid & PNG_INFO_IDAT) non-zero */

-   png_bytepp row_pointers;        /* the image bits */

-#endif

-

-};

-#endif /* PNGINFO_H */

+
+/* pnginfo.h - header file for PNG reference library
+ *
+ * Last changed in libpng 1.6.1 [March 28, 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+ /* png_info is a structure that holds the information in a PNG file so
+ * that the application can find out the characteristics of the image.
+ * If you are reading the file, this structure will tell you what is
+ * in the PNG file.  If you are writing the file, fill in the information
+ * you want to put into the PNG file, using png_set_*() functions, then
+ * call png_write_info().
+ *
+ * The names chosen should be very close to the PNG specification, so
+ * consult that document for information about the meaning of each field.
+ *
+ * With libpng < 0.95, it was only possible to directly set and read the
+ * the values in the png_info_struct, which meant that the contents and
+ * order of the values had to remain fixed.  With libpng 0.95 and later,
+ * however, there are now functions that abstract the contents of
+ * png_info_struct from the application, so this makes it easier to use
+ * libpng with dynamic libraries, and even makes it possible to use
+ * libraries that don't have all of the libpng ancillary chunk-handing
+ * functionality.  In libpng-1.5.0 this was moved into a separate private
+ * file that is not visible to applications.
+ *
+ * The following members may have allocated storage attached that should be
+ * cleaned up before the structure is discarded: palette, trans, text,
+ * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
+ * splt_palettes, scal_unit, row_pointers, and unknowns.   By default, these
+ * are automatically freed when the info structure is deallocated, if they were
+ * allocated internally by libpng.  This behavior can be changed by means
+ * of the png_data_freer() function.
+ *
+ * More allocation details: all the chunk-reading functions that
+ * change these members go through the corresponding png_set_*
+ * functions.  A function to clear these members is available: see
+ * png_free_data().  The png_set_* functions do not depend on being
+ * able to point info structure members to any of the storage they are
+ * passed (they make their own copies), EXCEPT that the png_set_text
+ * functions use the same storage passed to them in the text_ptr or
+ * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
+ * functions do not make their own copies.
+ */
+#ifndef PNGINFO_H
+#define PNGINFO_H
+
+struct png_info_def
+{
+   /* The following are necessary for every PNG file */
+   png_uint_32 width;  /* width of image in pixels (from IHDR) */
+   png_uint_32 height; /* height of image in pixels (from IHDR) */
+   png_uint_32 valid;  /* valid chunk data (see PNG_INFO_ below) */
+   png_size_t rowbytes; /* bytes needed to hold an untransformed row */
+   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
+   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
+   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
+   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
+   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
+   /* The following three should have been named *_method not *_type */
+   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
+   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
+   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+
+   /* The following are set by png_set_IHDR, called from the application on
+    * write, but the are never actually used by the write code.
+    */
+   png_byte channels;       /* number of data channels per pixel (1, 2, 3, 4) */
+   png_byte pixel_depth;    /* number of bits per pixel */
+   png_byte spare_byte;     /* to align the data, and for future use */
+
+#ifdef PNG_READ_SUPPORTED
+   /* This is never set during write */
+   png_byte signature[8];   /* magic bytes read by libpng from start of file */
+#endif
+
+   /* The rest of the data is optional.  If you are reading, check the
+    * valid field to see if the information in these are valid.  If you
+    * are writing, set the valid field to those chunks you want written,
+    * and initialize the appropriate fields below.
+    */
+
+#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
+   /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are
+    * defined.  When COLORSPACE is switched on all the colorspace-defining
+    * chunks should be enabled, when GAMMA is switched on all the gamma-defining
+    * chunks should be enabled.  If this is not done it becomes possible to read
+    * inconsistent PNG files and assign a probably incorrect interpretation to
+    * the information.  (In other words, by carefully choosing which chunks to
+    * recognize the system configuration can select an interpretation for PNG
+    * files containing ambiguous data and this will result in inconsistent
+    * behavior between different libpng builds!)
+    */
+   png_colorspace colorspace;
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+   /* iCCP chunk data. */
+   png_charp iccp_name;     /* profile name */
+   png_bytep iccp_profile;  /* International Color Consortium profile data */
+   png_uint_32 iccp_proflen;  /* ICC profile data length */
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+   /* The tEXt, and zTXt chunks contain human-readable textual data in
+    * uncompressed, compressed, and optionally compressed forms, respectively.
+    * The data in "text" is an array of pointers to uncompressed,
+    * null-terminated C strings. Each chunk has a keyword that describes the
+    * textual data contained in that chunk.  Keywords are not required to be
+    * unique, and the text string may be empty.  Any number of text chunks may
+    * be in an image.
+    */
+   int num_text; /* number of comments read or comments to write */
+   int max_text; /* current size of text array */
+   png_textp text; /* array of comments read or comments to write */
+#endif /* TEXT */
+
+#ifdef PNG_tIME_SUPPORTED
+   /* The tIME chunk holds the last time the displayed image data was
+    * modified.  See the png_time struct for the contents of this struct.
+    */
+   png_time mod_time;
+#endif
+
+#ifdef PNG_sBIT_SUPPORTED
+   /* The sBIT chunk specifies the number of significant high-order bits
+    * in the pixel data.  Values are in the range [1, bit_depth], and are
+    * only specified for the channels in the pixel data.  The contents of
+    * the low-order bits is not specified.  Data is valid if
+    * (valid & PNG_INFO_sBIT) is non-zero.
+    */
+   png_color_8 sig_bit; /* significant bits in color channels */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
+defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The tRNS chunk supplies transparency data for paletted images and
+    * other image types that don't need a full alpha channel.  There are
+    * "num_trans" transparency values for a paletted image, stored in the
+    * same order as the palette colors, starting from index 0.  Values
+    * for the data are in the range [0, 255], ranging from fully transparent
+    * to fully opaque, respectively.  For non-paletted images, there is a
+    * single color specified that should be treated as fully transparent.
+    * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
+    */
+   png_bytep trans_alpha;    /* alpha values for paletted image */
+   png_color_16 trans_color; /* transparent color for non-palette image */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The bKGD chunk gives the suggested image background color if the
+    * display program does not have its own background color and the image
+    * is needs to composited onto a background before display.  The colors
+    * in "background" are normally in the same color space/depth as the
+    * pixel data.  Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
+    */
+   png_color_16 background;
+#endif
+
+#ifdef PNG_oFFs_SUPPORTED
+   /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
+    * and downwards from the top-left corner of the display, page, or other
+    * application-specific co-ordinate space.  See the PNG_OFFSET_ defines
+    * below for the unit types.  Valid if (valid & PNG_INFO_oFFs) non-zero.
+    */
+   png_int_32 x_offset; /* x offset on page */
+   png_int_32 y_offset; /* y offset on page */
+   png_byte offset_unit_type; /* offset units type */
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+   /* The pHYs chunk gives the physical pixel density of the image for
+    * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
+    * defines below).  Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
+    */
+   png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
+   png_uint_32 y_pixels_per_unit; /* vertical pixel density */
+   png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+   /* The hIST chunk contains the relative frequency or importance of the
+    * various palette entries, so that a viewer can intelligently select a
+    * reduced-color palette, if required.  Data is an array of "num_palette"
+    * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
+    * is non-zero.
+    */
+   png_uint_16p hist;
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+   /* The pCAL chunk describes a transformation between the stored pixel
+    * values and original physical data values used to create the image.
+    * The integer range [0, 2^bit_depth - 1] maps to the floating-point
+    * range given by [pcal_X0, pcal_X1], and are further transformed by a
+    * (possibly non-linear) transformation function given by "pcal_type"
+    * and "pcal_params" into "pcal_units".  Please see the PNG_EQUATION_
+    * defines below, and the PNG-Group's PNG extensions document for a
+    * complete description of the transformations and how they should be
+    * implemented, and for a description of the ASCII parameter strings.
+    * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
+    */
+   png_charp pcal_purpose;  /* pCAL chunk description string */
+   png_int_32 pcal_X0;      /* minimum value */
+   png_int_32 pcal_X1;      /* maximum value */
+   png_charp pcal_units;    /* Latin-1 string giving physical units */
+   png_charpp pcal_params;  /* ASCII strings containing parameter values */
+   png_byte pcal_type;      /* equation type (see PNG_EQUATION_ below) */
+   png_byte pcal_nparams;   /* number of parameters given in pcal_params */
+#endif
+
+/* New members added in libpng-1.0.6 */
+   png_uint_32 free_me;     /* flags items libpng is responsible for freeing */
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+   /* Storage for unknown chunks that the library doesn't recognize. */
+   png_unknown_chunkp unknown_chunks;
+
+   /* The type of this field is limited by the type of
+    * png_struct::user_chunk_cache_max, else overflow can occur.
+    */
+   int                unknown_chunks_num;
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+   /* Data on sPLT chunks (there may be more than one). */
+   png_sPLT_tp splt_palettes;
+   int         splt_palettes_num; /* Match type returned by png_get API */
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+   /* The sCAL chunk describes the actual physical dimensions of the
+    * subject matter of the graphic.  The chunk contains a unit specification
+    * a byte value, and two ASCII strings representing floating-point
+    * values.  The values are width and height corresponsing to one pixel
+    * in the image.  Data values are valid if (valid & PNG_INFO_sCAL) is
+    * non-zero.
+    */
+   png_byte scal_unit;         /* unit of physical scale */
+   png_charp scal_s_width;     /* string containing height */
+   png_charp scal_s_height;    /* string containing width */
+#endif
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+   /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)
+      non-zero */
+   /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
+   png_bytepp row_pointers;        /* the image bits */
+#endif
+
+};
+#endif /* PNGINFO_H */
diff --git a/third_party/lpng_v163/pnglibconf.h b/third_party/libpng/pnglibconf.h
similarity index 93%
rename from third_party/lpng_v163/pnglibconf.h
rename to third_party/libpng/pnglibconf.h
index ececc38..adf4238 100644
--- a/third_party/lpng_v163/pnglibconf.h
+++ b/third_party/libpng/pnglibconf.h
@@ -1,211 +1,214 @@
-/* libpng 1.6.3 STANDARD API DEFINITION */

-

-/* pnglibconf.h - library build configuration */

-

-/* Libpng version 1.6.3 - July 18, 2013 */

-

-/* Copyright (c) 1998-2013 Glenn Randers-Pehrson */

-

-/* This code is released under the libpng license. */

-/* For conditions of distribution and use, see the disclaimer */

-/* and license in png.h */

-

-/* pnglibconf.h */

-/* Machine generated file: DO NOT EDIT */

-/* Derived from: scripts/pnglibconf.dfa */

-#ifndef PNGLCONF_H

-#define PNGLCONF_H

-/* options */

-#define PNG_16BIT_SUPPORTED

-#define PNG_ALIGNED_MEMORY_SUPPORTED

-/*#undef PNG_ARM_NEON_API_SUPPORTED*/

-/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/

-#define PNG_BENIGN_ERRORS_SUPPORTED

-#define PNG_BENIGN_READ_ERRORS_SUPPORTED

-/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/

-#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED

-#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED

-#define PNG_COLORSPACE_SUPPORTED

-#define PNG_CONSOLE_IO_SUPPORTED

-#define PNG_CONVERT_tIME_SUPPORTED

-#define PNG_EASY_ACCESS_SUPPORTED

-/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/

-#define PNG_ERROR_TEXT_SUPPORTED

-#define PNG_FIXED_POINT_SUPPORTED

-#define PNG_FLOATING_ARITHMETIC_SUPPORTED

-#define PNG_FLOATING_POINT_SUPPORTED

-#define PNG_FORMAT_AFIRST_SUPPORTED

-#define PNG_FORMAT_BGR_SUPPORTED

-#define PNG_GAMMA_SUPPORTED

-#define PNG_GET_PALETTE_MAX_SUPPORTED

-#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-#define PNG_INCH_CONVERSIONS_SUPPORTED

-#define PNG_INFO_IMAGE_SUPPORTED

-#define PNG_IO_STATE_SUPPORTED

-#define PNG_MNG_FEATURES_SUPPORTED

-#define PNG_POINTER_INDEXING_SUPPORTED

-#define PNG_PROGRESSIVE_READ_SUPPORTED

-#define PNG_READ_16BIT_SUPPORTED

-#define PNG_READ_ALPHA_MODE_SUPPORTED

-#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED

-#define PNG_READ_BACKGROUND_SUPPORTED

-#define PNG_READ_BGR_SUPPORTED

-#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED

-#define PNG_READ_COMPOSITE_NODIV_SUPPORTED

-#define PNG_READ_COMPRESSED_TEXT_SUPPORTED

-#define PNG_READ_EXPAND_16_SUPPORTED

-#define PNG_READ_EXPAND_SUPPORTED

-#define PNG_READ_FILLER_SUPPORTED

-#define PNG_READ_GAMMA_SUPPORTED

-#define PNG_READ_GET_PALETTE_MAX_SUPPORTED

-#define PNG_READ_GRAY_TO_RGB_SUPPORTED

-#define PNG_READ_INTERLACING_SUPPORTED

-#define PNG_READ_INT_FUNCTIONS_SUPPORTED

-#define PNG_READ_INVERT_ALPHA_SUPPORTED

-#define PNG_READ_INVERT_SUPPORTED

-#define PNG_READ_OPT_PLTE_SUPPORTED

-#define PNG_READ_PACKSWAP_SUPPORTED

-#define PNG_READ_PACK_SUPPORTED

-#define PNG_READ_QUANTIZE_SUPPORTED

-#define PNG_READ_RGB_TO_GRAY_SUPPORTED

-#define PNG_READ_SCALE_16_TO_8_SUPPORTED

-#define PNG_READ_SHIFT_SUPPORTED

-#define PNG_READ_STRIP_16_TO_8_SUPPORTED

-#define PNG_READ_STRIP_ALPHA_SUPPORTED

-#define PNG_READ_SUPPORTED

-#define PNG_READ_SWAP_ALPHA_SUPPORTED

-#define PNG_READ_SWAP_SUPPORTED

-#define PNG_READ_TEXT_SUPPORTED

-#define PNG_READ_TRANSFORMS_SUPPORTED

-#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED

-#define PNG_READ_USER_CHUNKS_SUPPORTED

-#define PNG_READ_USER_TRANSFORM_SUPPORTED

-#define PNG_READ_bKGD_SUPPORTED

-#define PNG_READ_cHRM_SUPPORTED

-#define PNG_READ_gAMA_SUPPORTED

-#define PNG_READ_hIST_SUPPORTED

-#define PNG_READ_iCCP_SUPPORTED

-#define PNG_READ_iTXt_SUPPORTED

-#define PNG_READ_oFFs_SUPPORTED

-#define PNG_READ_pCAL_SUPPORTED

-#define PNG_READ_pHYs_SUPPORTED

-#define PNG_READ_sBIT_SUPPORTED

-#define PNG_READ_sCAL_SUPPORTED

-#define PNG_READ_sPLT_SUPPORTED

-#define PNG_READ_sRGB_SUPPORTED

-#define PNG_READ_tEXt_SUPPORTED

-#define PNG_READ_tIME_SUPPORTED

-#define PNG_READ_tRNS_SUPPORTED

-#define PNG_READ_zTXt_SUPPORTED

-/*#undef PNG_SAFE_LIMITS_SUPPORTED*/

-#define PNG_SAVE_INT_32_SUPPORTED

-#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED

-#define PNG_SEQUENTIAL_READ_SUPPORTED

-#define PNG_SETJMP_SUPPORTED

-#define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED

-#define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED

-#define PNG_SET_OPTION_SUPPORTED

-#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-#define PNG_SET_USER_LIMITS_SUPPORTED

-#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED

-#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED

-#define PNG_SIMPLIFIED_READ_SUPPORTED

-#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED

-#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED

-#define PNG_SIMPLIFIED_WRITE_SUPPORTED

-#define PNG_STDIO_SUPPORTED

-#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED

-#define PNG_TEXT_SUPPORTED

-#define PNG_TIME_RFC1123_SUPPORTED

-#define PNG_UNKNOWN_CHUNKS_SUPPORTED

-#define PNG_USER_CHUNKS_SUPPORTED

-#define PNG_USER_LIMITS_SUPPORTED

-#define PNG_USER_MEM_SUPPORTED

-#define PNG_USER_TRANSFORM_INFO_SUPPORTED

-#define PNG_USER_TRANSFORM_PTR_SUPPORTED

-#define PNG_WARNINGS_SUPPORTED

-#define PNG_WRITE_16BIT_SUPPORTED

-#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED

-#define PNG_WRITE_BGR_SUPPORTED

-#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED

-#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED

-#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED

-#define PNG_WRITE_FILLER_SUPPORTED

-#define PNG_WRITE_FILTER_SUPPORTED

-#define PNG_WRITE_FLUSH_SUPPORTED

-#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED

-#define PNG_WRITE_INTERLACING_SUPPORTED

-#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED

-#define PNG_WRITE_INVERT_ALPHA_SUPPORTED

-#define PNG_WRITE_INVERT_SUPPORTED

-#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED

-#define PNG_WRITE_PACKSWAP_SUPPORTED

-#define PNG_WRITE_PACK_SUPPORTED

-#define PNG_WRITE_SHIFT_SUPPORTED

-#define PNG_WRITE_SUPPORTED

-#define PNG_WRITE_SWAP_ALPHA_SUPPORTED

-#define PNG_WRITE_SWAP_SUPPORTED

-#define PNG_WRITE_TEXT_SUPPORTED

-#define PNG_WRITE_TRANSFORMS_SUPPORTED

-#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED

-#define PNG_WRITE_USER_TRANSFORM_SUPPORTED

-#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-#define PNG_WRITE_bKGD_SUPPORTED

-#define PNG_WRITE_cHRM_SUPPORTED

-#define PNG_WRITE_gAMA_SUPPORTED

-#define PNG_WRITE_hIST_SUPPORTED

-#define PNG_WRITE_iCCP_SUPPORTED

-#define PNG_WRITE_iTXt_SUPPORTED

-#define PNG_WRITE_oFFs_SUPPORTED

-#define PNG_WRITE_pCAL_SUPPORTED

-#define PNG_WRITE_pHYs_SUPPORTED

-#define PNG_WRITE_sBIT_SUPPORTED

-#define PNG_WRITE_sCAL_SUPPORTED

-#define PNG_WRITE_sPLT_SUPPORTED

-#define PNG_WRITE_sRGB_SUPPORTED

-#define PNG_WRITE_tEXt_SUPPORTED

-#define PNG_WRITE_tIME_SUPPORTED

-#define PNG_WRITE_tRNS_SUPPORTED

-#define PNG_WRITE_zTXt_SUPPORTED

-#define PNG_bKGD_SUPPORTED

-#define PNG_cHRM_SUPPORTED

-#define PNG_gAMA_SUPPORTED

-#define PNG_hIST_SUPPORTED

-#define PNG_iCCP_SUPPORTED

-#define PNG_iTXt_SUPPORTED

-#define PNG_oFFs_SUPPORTED

-#define PNG_pCAL_SUPPORTED

-#define PNG_pHYs_SUPPORTED

-#define PNG_sBIT_SUPPORTED

-#define PNG_sCAL_SUPPORTED

-#define PNG_sPLT_SUPPORTED

-#define PNG_sRGB_SUPPORTED

-#define PNG_tEXt_SUPPORTED

-#define PNG_tIME_SUPPORTED

-#define PNG_tRNS_SUPPORTED

-#define PNG_zTXt_SUPPORTED

-/* end of options */

-/* settings */

-#define PNG_API_RULE 0

-#define PNG_CALLOC_SUPPORTED

-#define PNG_COST_SHIFT 3

-#define PNG_DEFAULT_READ_MACROS 1

-#define PNG_GAMMA_THRESHOLD_FIXED 5000

-#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE

-#define PNG_INFLATE_BUF_SIZE 1024

-#define PNG_MAX_GAMMA_8 11

-#define PNG_QUANTIZE_BLUE_BITS 5

-#define PNG_QUANTIZE_GREEN_BITS 5

-#define PNG_QUANTIZE_RED_BITS 5

-#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)

-#define PNG_TEXT_Z_DEFAULT_STRATEGY 0

-#define PNG_WEIGHT_SHIFT 8

-#define PNG_ZBUF_SIZE 8192

-#define PNG_ZLIB_VERNUM 0 /* unknown */

-#define PNG_Z_DEFAULT_COMPRESSION (-1)

-#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0

-#define PNG_Z_DEFAULT_STRATEGY 1

-#define PNG_sCAL_PRECISION 5

-#define PNG_sRGB_PROFILE_CHECKS 2

-/* end of settings */

-#endif /* PNGLCONF_H */

+/* libpng 1.6.20 STANDARD API DEFINITION */
+
+/* pnglibconf.h - library build configuration */
+
+/* Libpng version 1.6.20 - December 3, 2015 */
+
+/* Copyright (c) 1998-2015 Glenn Randers-Pehrson */
+
+/* This code is released under the libpng license. */
+/* For conditions of distribution and use, see the disclaimer */
+/* and license in png.h */
+
+/* pnglibconf.h */
+/* Machine generated file: DO NOT EDIT */
+/* Derived from: scripts/pnglibconf.dfa */
+#ifndef PNGLCONF_H
+#define PNGLCONF_H
+/* options */
+#define PNG_16BIT_SUPPORTED
+#define PNG_ALIGNED_MEMORY_SUPPORTED
+/*#undef PNG_ARM_NEON_API_SUPPORTED*/
+/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
+#define PNG_BENIGN_ERRORS_SUPPORTED
+#define PNG_BENIGN_READ_ERRORS_SUPPORTED
+/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
+#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
+#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_COLORSPACE_SUPPORTED
+#define PNG_CONSOLE_IO_SUPPORTED
+#define PNG_CONVERT_tIME_SUPPORTED
+#define PNG_EASY_ACCESS_SUPPORTED
+/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
+#define PNG_ERROR_TEXT_SUPPORTED
+#define PNG_FIXED_POINT_SUPPORTED
+#define PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define PNG_FLOATING_POINT_SUPPORTED
+#define PNG_FORMAT_AFIRST_SUPPORTED
+#define PNG_FORMAT_BGR_SUPPORTED
+#define PNG_GAMMA_SUPPORTED
+#define PNG_GET_PALETTE_MAX_SUPPORTED
+#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#define PNG_INCH_CONVERSIONS_SUPPORTED
+#define PNG_INFO_IMAGE_SUPPORTED
+#define PNG_IO_STATE_SUPPORTED
+#define PNG_MNG_FEATURES_SUPPORTED
+#define PNG_POINTER_INDEXING_SUPPORTED
+#define PNG_PROGRESSIVE_READ_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+#define PNG_READ_ALPHA_MODE_SUPPORTED
+#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_READ_BACKGROUND_SUPPORTED
+#define PNG_READ_BGR_SUPPORTED
+#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
+#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
+#define PNG_READ_EXPAND_16_SUPPORTED
+#define PNG_READ_EXPAND_SUPPORTED
+#define PNG_READ_FILLER_SUPPORTED
+#define PNG_READ_GAMMA_SUPPORTED
+#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
+#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#define PNG_READ_INTERLACING_SUPPORTED
+#define PNG_READ_INT_FUNCTIONS_SUPPORTED
+#define PNG_READ_INVERT_ALPHA_SUPPORTED
+#define PNG_READ_INVERT_SUPPORTED
+#define PNG_READ_OPT_PLTE_SUPPORTED
+#define PNG_READ_PACKSWAP_SUPPORTED
+#define PNG_READ_PACK_SUPPORTED
+#define PNG_READ_QUANTIZE_SUPPORTED
+#define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#define PNG_READ_SCALE_16_TO_8_SUPPORTED
+#define PNG_READ_SHIFT_SUPPORTED
+#define PNG_READ_STRIP_16_TO_8_SUPPORTED
+#define PNG_READ_STRIP_ALPHA_SUPPORTED
+#define PNG_READ_SUPPORTED
+#define PNG_READ_SWAP_ALPHA_SUPPORTED
+#define PNG_READ_SWAP_SUPPORTED
+#define PNG_READ_TEXT_SUPPORTED
+#define PNG_READ_TRANSFORMS_SUPPORTED
+#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_READ_USER_CHUNKS_SUPPORTED
+#define PNG_READ_USER_TRANSFORM_SUPPORTED
+#define PNG_READ_bKGD_SUPPORTED
+#define PNG_READ_cHRM_SUPPORTED
+#define PNG_READ_gAMA_SUPPORTED
+#define PNG_READ_hIST_SUPPORTED
+#define PNG_READ_iCCP_SUPPORTED
+#define PNG_READ_iTXt_SUPPORTED
+#define PNG_READ_oFFs_SUPPORTED
+#define PNG_READ_pCAL_SUPPORTED
+#define PNG_READ_pHYs_SUPPORTED
+#define PNG_READ_sBIT_SUPPORTED
+#define PNG_READ_sCAL_SUPPORTED
+#define PNG_READ_sPLT_SUPPORTED
+#define PNG_READ_sRGB_SUPPORTED
+#define PNG_READ_tEXt_SUPPORTED
+#define PNG_READ_tIME_SUPPORTED
+#define PNG_READ_tRNS_SUPPORTED
+#define PNG_READ_zTXt_SUPPORTED
+#define PNG_SAVE_INT_32_SUPPORTED
+#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SEQUENTIAL_READ_SUPPORTED
+#define PNG_SETJMP_SUPPORTED
+#define PNG_SET_OPTION_SUPPORTED
+#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SET_USER_LIMITS_SUPPORTED
+#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
+#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_READ_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_SUPPORTED
+#define PNG_STDIO_SUPPORTED
+#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_TEXT_SUPPORTED
+#define PNG_TIME_RFC1123_SUPPORTED
+#define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_USER_CHUNKS_SUPPORTED
+#define PNG_USER_LIMITS_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED
+#define PNG_USER_TRANSFORM_INFO_SUPPORTED
+#define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#define PNG_WARNINGS_SUPPORTED
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_WRITE_BGR_SUPPORTED
+#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+#define PNG_WRITE_FILLER_SUPPORTED
+#define PNG_WRITE_FILTER_SUPPORTED
+#define PNG_WRITE_FLUSH_SUPPORTED
+#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
+#define PNG_WRITE_INTERLACING_SUPPORTED
+#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#define PNG_WRITE_INVERT_SUPPORTED
+#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+#define PNG_WRITE_PACKSWAP_SUPPORTED
+#define PNG_WRITE_PACK_SUPPORTED
+#define PNG_WRITE_SHIFT_SUPPORTED
+#define PNG_WRITE_SUPPORTED
+#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#define PNG_WRITE_SWAP_SUPPORTED
+#define PNG_WRITE_TEXT_SUPPORTED
+#define PNG_WRITE_TRANSFORMS_SUPPORTED
+#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#define PNG_WRITE_bKGD_SUPPORTED
+#define PNG_WRITE_cHRM_SUPPORTED
+#define PNG_WRITE_gAMA_SUPPORTED
+#define PNG_WRITE_hIST_SUPPORTED
+#define PNG_WRITE_iCCP_SUPPORTED
+#define PNG_WRITE_iTXt_SUPPORTED
+#define PNG_WRITE_oFFs_SUPPORTED
+#define PNG_WRITE_pCAL_SUPPORTED
+#define PNG_WRITE_pHYs_SUPPORTED
+#define PNG_WRITE_sBIT_SUPPORTED
+#define PNG_WRITE_sCAL_SUPPORTED
+#define PNG_WRITE_sPLT_SUPPORTED
+#define PNG_WRITE_sRGB_SUPPORTED
+#define PNG_WRITE_tEXt_SUPPORTED
+#define PNG_WRITE_tIME_SUPPORTED
+#define PNG_WRITE_tRNS_SUPPORTED
+#define PNG_WRITE_zTXt_SUPPORTED
+#define PNG_bKGD_SUPPORTED
+#define PNG_cHRM_SUPPORTED
+#define PNG_gAMA_SUPPORTED
+#define PNG_hIST_SUPPORTED
+#define PNG_iCCP_SUPPORTED
+#define PNG_iTXt_SUPPORTED
+#define PNG_oFFs_SUPPORTED
+#define PNG_pCAL_SUPPORTED
+#define PNG_pHYs_SUPPORTED
+#define PNG_sBIT_SUPPORTED
+#define PNG_sCAL_SUPPORTED
+#define PNG_sPLT_SUPPORTED
+#define PNG_sRGB_SUPPORTED
+#define PNG_tEXt_SUPPORTED
+#define PNG_tIME_SUPPORTED
+#define PNG_tRNS_SUPPORTED
+#define PNG_zTXt_SUPPORTED
+/* end of options */
+/* settings */
+#define PNG_API_RULE 0
+#define PNG_DEFAULT_READ_MACROS 1
+#define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
+#define PNG_INFLATE_BUF_SIZE 1024
+#define PNG_LINKAGE_API extern
+#define PNG_LINKAGE_CALLBACK extern
+#define PNG_LINKAGE_DATA extern
+#define PNG_LINKAGE_FUNCTION extern
+#define PNG_MAX_GAMMA_8 11
+#define PNG_QUANTIZE_BLUE_BITS 5
+#define PNG_QUANTIZE_GREEN_BITS 5
+#define PNG_QUANTIZE_RED_BITS 5
+#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
+#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
+#define PNG_USER_CHUNK_CACHE_MAX 1000
+#define PNG_USER_CHUNK_MALLOC_MAX 8000000
+#define PNG_USER_HEIGHT_MAX 1000000
+#define PNG_USER_WIDTH_MAX 1000000
+#define PNG_ZBUF_SIZE 8192
+#define PNG_ZLIB_VERNUM 0 /* unknown */
+#define PNG_Z_DEFAULT_COMPRESSION (-1)
+#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
+#define PNG_Z_DEFAULT_STRATEGY 1
+#define PNG_sCAL_PRECISION 5
+#define PNG_sRGB_PROFILE_CHECKS 2
+/* end of settings */
+#endif /* PNGLCONF_H */
diff --git a/third_party/lpng_v163/pngmem.c b/third_party/libpng/pngmem.c
similarity index 92%
rename from third_party/lpng_v163/pngmem.c
rename to third_party/libpng/pngmem.c
index cbbd4db..db71219 100644
--- a/third_party/lpng_v163/pngmem.c
+++ b/third_party/libpng/pngmem.c
@@ -1,281 +1,287 @@
-/* pngmem.c - stub functions for memory allocation

- *

- * Last changed in libpng 1.6.0 [February 14, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file provides a location for all memory allocation.  Users who

- * need special memory handling are expected to supply replacement

- * functions for png_malloc() and png_free(), and to use

- * png_create_read_struct_2() and png_create_write_struct_2() to

- * identify the replacement functions.

- */

-

-#include "pngpriv.h"

-

-void*	FXMEM_DefaultAlloc(int byte_size, int);

-void	FXMEM_DefaultFree(void* pointer, int);

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-/* Free a png_struct */

-void /* PRIVATE */

-png_destroy_png_struct(png_structrp png_ptr)

-{

-   if (png_ptr != NULL)

-   {

-      /* png_free might call png_error and may certainly call

-       * png_get_mem_ptr, so fake a temporary png_struct to support this.

-       */

-      png_struct dummy_struct = *png_ptr;

-      memset(png_ptr, 0, (sizeof *png_ptr));

-      png_free(&dummy_struct, png_ptr);

-

-#     ifdef PNG_SETJMP_SUPPORTED

-         /* We may have a jmp_buf left to deallocate. */

-         png_free_jmpbuf(&dummy_struct);

-#     endif

-   }

-}

-

-/* Allocate memory.  For reasonable files, size should never exceed

- * 64K.  However, zlib may allocate more then 64K if you don't tell

- * it not to.  See zconf.h and png.h for more information.  zlib does

- * need to allocate exactly 64K, so whatever you call here must

- * have the ability to do that.

- */

-PNG_FUNCTION(png_voidp,PNGAPI

-png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)

-{

-   png_voidp ret;

-

-   ret = png_malloc(png_ptr, size);

-

-   if (ret != NULL)

-      memset(ret, 0, size);

-

-   return ret;

-}

-

-/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of

- * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED.

- * Checking and error handling must happen outside this routine; it returns NULL

- * if the allocation cannot be done (for any reason.)

- */

-PNG_FUNCTION(png_voidp /* PRIVATE */,

-png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),

-   PNG_ALLOCATED)

-{

-   /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS

-    * allocators have also been removed in 1.6.0, so any 16-bit system now has

-    * to implement a user memory handler.  This checks to be sure it isn't

-    * called with big numbers.

-    */

-#ifdef PNG_USER_MEM_SUPPORTED

-   PNG_UNUSED(png_ptr)

-#endif

-   if (size > 0 && size <= PNG_SIZE_MAX

-#     ifdef PNG_MAX_MALLOC_64K

-         && size <= 65536U

-#     endif

-      )

-   {

-#ifdef PNG_USER_MEM_SUPPORTED

-      if (png_ptr != NULL && png_ptr->malloc_fn != NULL)

-         return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);

-

-      else

-#endif

-         return FXMEM_DefaultAlloc((int)size, 0);

-         //return malloc((size_t)size); /* checked for truncation above */

-   }

-

-   else

-      return NULL;

-}

-

-/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7

- * that arises because of the checks in png_realloc_array that are repeated in

- * png_malloc_array.

- */

-static png_voidp

-png_malloc_array_checked(png_const_structrp png_ptr, int nelements,

-   size_t element_size)

-{

-   png_alloc_size_t req = nelements; /* known to be > 0 */

-

-   if (req <= PNG_SIZE_MAX/element_size)

-      return png_malloc_base(png_ptr, req * element_size);

-

-   /* The failure case when the request is too large */

-   return NULL;

-}

-

-PNG_FUNCTION(png_voidp /* PRIVATE */,

-png_malloc_array,(png_const_structrp png_ptr, int nelements,

-   size_t element_size),PNG_ALLOCATED)

-{

-   if (nelements <= 0 || element_size == 0)

-      png_error(png_ptr, "internal error: array alloc");

-

-   return png_malloc_array_checked(png_ptr, nelements, element_size);

-}

-

-PNG_FUNCTION(png_voidp /* PRIVATE */,

-png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,

-   int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)

-{

-   /* These are internal errors: */

-   if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||

-      (old_array == NULL && old_elements > 0))

-      png_error(png_ptr, "internal error: array realloc");

-

-   /* Check for overflow on the elements count (so the caller does not have to

-    * check.)

-    */

-   if (add_elements <= INT_MAX - old_elements)

-   {

-      png_voidp new_array = png_malloc_array_checked(png_ptr,

-         old_elements+add_elements, element_size);

-

-      if (new_array != NULL)

-      {

-         /* Because png_malloc_array worked the size calculations below cannot

-          * overflow.

-          */

-         if (old_elements > 0)

-            memcpy(new_array, old_array, element_size*(unsigned)old_elements);

-

-         memset((char*)new_array + element_size*(unsigned)old_elements, 0,

-            element_size*(unsigned)add_elements);

-

-         return new_array;

-      }

-   }

-

-   return NULL; /* error */

-}

-

-/* Various functions that have different error handling are derived from this.

- * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate

- * function png_malloc_default is also provided.

- */

-PNG_FUNCTION(png_voidp,PNGAPI

-png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)

-{

-   png_voidp ret;

-

-   if (png_ptr == NULL)

-      return NULL;

-

-   ret = png_malloc_base(png_ptr, size);

-

-   if (ret == NULL)

-       png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */

-

-   return ret;

-}

-

-#ifdef PNG_USER_MEM_SUPPORTED

-PNG_FUNCTION(png_voidp,PNGAPI

-png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size),

-   PNG_ALLOCATED PNG_DEPRECATED)

-{

-   png_voidp ret;

-

-   if (png_ptr == NULL)

-      return NULL;

-

-   /* Passing 'NULL' here bypasses the application provided memory handler. */

-   ret = png_malloc_base(NULL/*use malloc*/, size);

-

-   if (ret == NULL)

-      png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */

-

-   return ret;

-}

-#endif /* PNG_USER_MEM_SUPPORTED */

-

-/* This function was added at libpng version 1.2.3.  The png_malloc_warn()

- * function will issue a png_warning and return NULL instead of issuing a

- * png_error, if it fails to allocate the requested memory.

- */

-PNG_FUNCTION(png_voidp,PNGAPI

-png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),

-   PNG_ALLOCATED)

-{

-   if (png_ptr != NULL)

-   {

-      png_voidp ret = png_malloc_base(png_ptr, size);

-

-      if (ret != NULL)

-         return ret;

-

-      png_warning(png_ptr, "Out of memory");

-   }

-

-   return NULL;

-}

-

-/* Free a pointer allocated by png_malloc().  If ptr is NULL, return

- * without taking any action.

- */

-void PNGAPI

-png_free(png_const_structrp png_ptr, png_voidp ptr)

-{

-   if (png_ptr == NULL || ptr == NULL)

-      return;

-

-#ifdef PNG_USER_MEM_SUPPORTED

-   if (png_ptr->free_fn != NULL)

-      png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr);

-

-   else

-      png_free_default(png_ptr, ptr);

-}

-

-PNG_FUNCTION(void,PNGAPI

-png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)

-{

-   if (png_ptr == NULL || ptr == NULL)

-      return;

-#endif /* PNG_USER_MEM_SUPPORTED */

-

-   FXMEM_DefaultFree(ptr, 0);

-   //free(ptr);

-}

-

-#ifdef PNG_USER_MEM_SUPPORTED

-/* This function is called when the application wants to use another method

- * of allocating and freeing memory.

- */

-void PNGAPI

-png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr

-  malloc_fn, png_free_ptr free_fn)

-{

-   if (png_ptr != NULL)

-   {

-      png_ptr->mem_ptr = mem_ptr;

-      png_ptr->malloc_fn = malloc_fn;

-      png_ptr->free_fn = free_fn;

-   }

-}

-

-/* This function returns a pointer to the mem_ptr associated with the user

- * functions.  The application should free any memory associated with this

- * pointer before png_write_destroy and png_read_destroy are called.

- */

-png_voidp PNGAPI

-png_get_mem_ptr(png_const_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return NULL;

-

-   return png_ptr->mem_ptr;

-}

-#endif /* PNG_USER_MEM_SUPPORTED */

-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */

+
+/* pngmem.c - stub functions for memory allocation
+ *
+ * Last changed in libpng 1.6.15 [November 20, 2014]
+ * Copyright (c) 1998-2014 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all memory allocation.  Users who
+ * need special memory handling are expected to supply replacement
+ * functions for png_malloc() and png_free(), and to use
+ * png_create_read_struct_2() and png_create_write_struct_2() to
+ * identify the replacement functions.
+ */
+
+#include "pngpriv.h"
+
+void*	FXMEM_DefaultAlloc(int byte_size, int);
+void	FXMEM_DefaultFree(void* pointer, int);
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Free a png_struct */
+void /* PRIVATE */
+png_destroy_png_struct(png_structrp png_ptr)
+{
+   if (png_ptr != NULL)
+   {
+      /* png_free might call png_error and may certainly call
+       * png_get_mem_ptr, so fake a temporary png_struct to support this.
+       */
+      png_struct dummy_struct = *png_ptr;
+      memset(png_ptr, 0, (sizeof *png_ptr));
+      png_free(&dummy_struct, png_ptr);
+
+#     ifdef PNG_SETJMP_SUPPORTED
+         /* We may have a jmp_buf left to deallocate. */
+         png_free_jmpbuf(&dummy_struct);
+#     endif
+   }
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+ * 64K.  However, zlib may allocate more than 64K if you don't tell
+ * it not to.  See zconf.h and png.h for more information.  zlib does
+ * need to allocate exactly 64K, so whatever you call here must
+ * have the ability to do that.
+ */
+PNG_FUNCTION(png_voidp,PNGAPI
+png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+   png_voidp ret;
+
+   ret = png_malloc(png_ptr, size);
+
+   if (ret != NULL)
+      memset(ret, 0, size);
+
+   return ret;
+}
+
+/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of
+ * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED.
+ * Checking and error handling must happen outside this routine; it returns NULL
+ * if the allocation cannot be done (for any reason.)
+ */
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED)
+{
+   /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS
+    * allocators have also been removed in 1.6.0, so any 16-bit system now has
+    * to implement a user memory handler.  This checks to be sure it isn't
+    * called with big numbers.
+    */
+#ifndef PNG_USER_MEM_SUPPORTED
+   PNG_UNUSED(png_ptr)
+#endif
+
+   /* Some compilers complain that this is always true.  However, it
+    * can be false when integer overflow happens.
+    */
+   if (size > 0 && size <= PNG_SIZE_MAX
+#     ifdef PNG_MAX_MALLOC_64K
+         && size <= 65536U
+#     endif
+      )
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if (png_ptr != NULL && png_ptr->malloc_fn != NULL)
+         return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
+
+      else
+#endif
+         return FXMEM_DefaultAlloc((int)size, 0);
+   }
+
+   else
+      return NULL;
+}
+
+#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
+   defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
+/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7
+ * that arises because of the checks in png_realloc_array that are repeated in
+ * png_malloc_array.
+ */
+static png_voidp
+png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
+   size_t element_size)
+{
+   png_alloc_size_t req = nelements; /* known to be > 0 */
+
+   if (req <= PNG_SIZE_MAX/element_size)
+      return png_malloc_base(png_ptr, req * element_size);
+
+   /* The failure case when the request is too large */
+   return NULL;
+}
+
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_malloc_array,(png_const_structrp png_ptr, int nelements,
+   size_t element_size),PNG_ALLOCATED)
+{
+   if (nelements <= 0 || element_size == 0)
+      png_error(png_ptr, "internal error: array alloc");
+
+   return png_malloc_array_checked(png_ptr, nelements, element_size);
+}
+
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
+   int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
+{
+   /* These are internal errors: */
+   if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
+      (old_array == NULL && old_elements > 0))
+      png_error(png_ptr, "internal error: array realloc");
+
+   /* Check for overflow on the elements count (so the caller does not have to
+    * check.)
+    */
+   if (add_elements <= INT_MAX - old_elements)
+   {
+      png_voidp new_array = png_malloc_array_checked(png_ptr,
+         old_elements+add_elements, element_size);
+
+      if (new_array != NULL)
+      {
+         /* Because png_malloc_array worked the size calculations below cannot
+          * overflow.
+          */
+         if (old_elements > 0)
+            memcpy(new_array, old_array, element_size*(unsigned)old_elements);
+
+         memset((char*)new_array + element_size*(unsigned)old_elements, 0,
+            element_size*(unsigned)add_elements);
+
+         return new_array;
+      }
+   }
+
+   return NULL; /* error */
+}
+#endif /* TEXT || sPLT || STORE_UNKNOWN_CHUNKS */
+
+/* Various functions that have different error handling are derived from this.
+ * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate
+ * function png_malloc_default is also provided.
+ */
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+   png_voidp ret;
+
+   if (png_ptr == NULL)
+      return NULL;
+
+   ret = png_malloc_base(png_ptr, size);
+
+   if (ret == NULL)
+       png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */
+
+   return ret;
+}
+
+#ifdef PNG_USER_MEM_SUPPORTED
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED PNG_DEPRECATED)
+{
+   png_voidp ret;
+
+   if (png_ptr == NULL)
+      return NULL;
+
+   /* Passing 'NULL' here bypasses the application provided memory handler. */
+   ret = png_malloc_base(NULL/*use malloc*/, size);
+
+   if (ret == NULL)
+      png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */
+
+   return ret;
+}
+#endif /* USER_MEM */
+
+/* This function was added at libpng version 1.2.3.  The png_malloc_warn()
+ * function will issue a png_warning and return NULL instead of issuing a
+ * png_error, if it fails to allocate the requested memory.
+ */
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED)
+{
+   if (png_ptr != NULL)
+   {
+      png_voidp ret = png_malloc_base(png_ptr, size);
+
+      if (ret != NULL)
+         return ret;
+
+      png_warning(png_ptr, "Out of memory");
+   }
+
+   return NULL;
+}
+
+/* Free a pointer allocated by png_malloc().  If ptr is NULL, return
+ * without taking any action.
+ */
+void PNGAPI
+png_free(png_const_structrp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+      png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr);
+
+   else
+      png_free_default(png_ptr, ptr);
+}
+
+PNG_FUNCTION(void,PNGAPI
+png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+#endif /* USER_MEM */
+
+   FXMEM_DefaultFree(ptr, 0);
+}
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* This function is called when the application wants to use another method
+ * of allocating and freeing memory.
+ */
+void PNGAPI
+png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr
+  malloc_fn, png_free_ptr free_fn)
+{
+   if (png_ptr != NULL)
+   {
+      png_ptr->mem_ptr = mem_ptr;
+      png_ptr->malloc_fn = malloc_fn;
+      png_ptr->free_fn = free_fn;
+   }
+}
+
+/* This function returns a pointer to the mem_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_mem_ptr(png_const_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return NULL;
+
+   return png_ptr->mem_ptr;
+}
+#endif /* USER_MEM */
+#endif /* READ || WRITE */
diff --git a/third_party/lpng_v163/pngpread.c b/third_party/libpng/pngpread.c
similarity index 77%
rename from third_party/lpng_v163/pngpread.c
rename to third_party/libpng/pngpread.c
index afb2abf..89ffc40 100644
--- a/third_party/lpng_v163/pngpread.c
+++ b/third_party/libpng/pngpread.c
@@ -1,1290 +1,1085 @@
-/* pngpread.c - read a png file in push mode

- *

- * Last changed in libpng 1.6.0 [February 14, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

-#include "pngpriv.h"

-

-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED

-

-/* Push model modes */

-#define PNG_READ_SIG_MODE   0

-#define PNG_READ_CHUNK_MODE 1

-#define PNG_READ_IDAT_MODE  2

-#define PNG_SKIP_MODE       3

-#define PNG_READ_tEXt_MODE  4

-#define PNG_READ_zTXt_MODE  5

-#define PNG_READ_DONE_MODE  6

-#define PNG_READ_iTXt_MODE  7

-#define PNG_ERROR_MODE      8

-

-void PNGAPI

-png_process_data(png_structrp png_ptr, png_inforp info_ptr,

-    png_bytep buffer, png_size_t buffer_size)

-{

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   png_push_restore_buffer(png_ptr, buffer, buffer_size);

-

-   while (png_ptr->buffer_size)

-   {

-      png_process_some_data(png_ptr, info_ptr);

-   }

-}

-

-png_size_t PNGAPI

-png_process_data_pause(png_structrp png_ptr, int save)

-{

-   if (png_ptr != NULL)

-   {

-      /* It's easiest for the caller if we do the save, then the caller doesn't

-       * have to supply the same data again:

-       */

-      if (save)

-         png_push_save_buffer(png_ptr);

-      else

-      {

-         /* This includes any pending saved bytes: */

-         png_size_t remaining = png_ptr->buffer_size;

-         png_ptr->buffer_size = 0;

-

-         /* So subtract the saved buffer size, unless all the data

-          * is actually 'saved', in which case we just return 0

-          */

-         if (png_ptr->save_buffer_size < remaining)

-            return remaining - png_ptr->save_buffer_size;

-      }

-   }

-

-   return 0;

-}

-

-png_uint_32 PNGAPI

-png_process_data_skip(png_structrp png_ptr)

-{

-   png_uint_32 remaining = 0;

-

-   if (png_ptr != NULL && png_ptr->process_mode == PNG_SKIP_MODE &&

-      png_ptr->skip_length > 0)

-   {

-      /* At the end of png_process_data the buffer size must be 0 (see the loop

-       * above) so we can detect a broken call here:

-       */

-      if (png_ptr->buffer_size != 0)

-         png_error(png_ptr,

-            "png_process_data_skip called inside png_process_data");

-

-      /* If is impossible for there to be a saved buffer at this point -

-       * otherwise we could not be in SKIP mode.  This will also happen if

-       * png_process_skip is called inside png_process_data (but only very

-       * rarely.)

-       */

-      if (png_ptr->save_buffer_size != 0)

-         png_error(png_ptr, "png_process_data_skip called with saved data");

-

-      remaining = png_ptr->skip_length;

-      png_ptr->skip_length = 0;

-      png_ptr->process_mode = PNG_READ_CHUNK_MODE;

-   }

-

-   return remaining;

-}

-

-/* What we do with the incoming data depends on what we were previously

- * doing before we ran out of data...

- */

-void /* PRIVATE */

-png_process_some_data(png_structrp png_ptr, png_inforp info_ptr)

-{

-   if (png_ptr == NULL)

-      return;

-

-   switch (png_ptr->process_mode)

-   {

-      case PNG_READ_SIG_MODE:

-      {

-         png_push_read_sig(png_ptr, info_ptr);

-         break;

-      }

-

-      case PNG_READ_CHUNK_MODE:

-      {

-         png_push_read_chunk(png_ptr, info_ptr);

-         break;

-      }

-

-      case PNG_READ_IDAT_MODE:

-      {

-         png_push_read_IDAT(png_ptr);

-         break;

-      }

-

-      case PNG_SKIP_MODE:

-      {

-         png_push_crc_finish(png_ptr);

-         break;

-      }

-

-      default:

-      {

-         png_ptr->buffer_size = 0;

-         break;

-      }

-   }

-}

-

-/* Read any remaining signature bytes from the stream and compare them with

- * the correct PNG signature.  It is possible that this routine is called

- * with bytes already read from the signature, either because they have been

- * checked by the calling application, or because of multiple calls to this

- * routine.

- */

-void /* PRIVATE */

-png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr)

-{

-   png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */ 

-             num_to_check = 8 - num_checked;

-

-   if (png_ptr->buffer_size < num_to_check)

-   {

-      num_to_check = png_ptr->buffer_size;

-   }

-

-   png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),

-       num_to_check);

-   png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check);

-

-   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))

-   {

-      if (num_checked < 4 &&

-          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))

-         png_error(png_ptr, "Not a PNG file");

-

-      else

-         png_error(png_ptr, "PNG file corrupted by ASCII conversion");

-   }

-   else

-   {

-      if (png_ptr->sig_bytes >= 8)

-      {

-         png_ptr->process_mode = PNG_READ_CHUNK_MODE;

-      }

-   }

-}

-

-void /* PRIVATE */

-png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)

-{

-   png_uint_32 chunk_name;

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-   int keep; /* unknown handling method */

-#endif

-

-   /* First we make sure we have enough data for the 4 byte chunk name

-    * and the 4 byte chunk length before proceeding with decoding the

-    * chunk data.  To fully decode each of these chunks, we also make

-    * sure we have enough data in the buffer for the 4 byte CRC at the

-    * end of every chunk (except IDAT, which is handled separately).

-    */

-   if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))

-   {

-      png_byte chunk_length[4];

-      png_byte chunk_tag[4];

-

-      if (png_ptr->buffer_size < 8)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_push_fill_buffer(png_ptr, chunk_length, 4);

-      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);

-      png_reset_crc(png_ptr);

-      png_crc_read(png_ptr, chunk_tag, 4);

-      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);

-      png_check_chunk_name(png_ptr, png_ptr->chunk_name);

-      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;

-   }

-

-   chunk_name = png_ptr->chunk_name;

-

-   if (chunk_name == png_IDAT)

-   {

-      if (png_ptr->mode & PNG_AFTER_IDAT)

-         png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;

-

-      /* If we reach an IDAT chunk, this means we have read all of the

-       * header chunks, and we can start reading the image (or if this

-       * is called after the image has been read - we have an error).

-       */

-      if (!(png_ptr->mode & PNG_HAVE_IHDR))

-         png_error(png_ptr, "Missing IHDR before IDAT");

-

-      else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&

-          !(png_ptr->mode & PNG_HAVE_PLTE))

-         png_error(png_ptr, "Missing PLTE before IDAT");

-

-      png_ptr->mode |= PNG_HAVE_IDAT;

-

-      if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))

-         if (png_ptr->push_length == 0)

-            return;

-

-      if (png_ptr->mode & PNG_AFTER_IDAT)

-         png_benign_error(png_ptr, "Too many IDATs found");

-   }

-

-   if (chunk_name == png_IHDR)

-   {

-      if (png_ptr->push_length != 13)

-         png_error(png_ptr, "Invalid IHDR length");

-

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-   else if (chunk_name == png_IEND)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);

-

-      png_ptr->process_mode = PNG_READ_DONE_MODE;

-      png_push_have_end(png_ptr, info_ptr);

-   }

-

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-   else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep);

-

-      if (chunk_name == png_PLTE)

-         png_ptr->mode |= PNG_HAVE_PLTE;

-   }

-

-#endif

-   else if (chunk_name == png_PLTE)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-   else if (chunk_name == png_IDAT)

-   {

-      png_ptr->idat_size = png_ptr->push_length;

-      png_ptr->process_mode = PNG_READ_IDAT_MODE;

-      png_push_have_info(png_ptr, info_ptr);

-      png_ptr->zstream.avail_out =

-          (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,

-          png_ptr->iwidth) + 1;

-      png_ptr->zstream.next_out = png_ptr->row_buf;

-      return;

-   }

-

-#ifdef PNG_READ_gAMA_SUPPORTED

-   else if (png_ptr->chunk_name == png_gAMA)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_sBIT_SUPPORTED

-   else if (png_ptr->chunk_name == png_sBIT)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_cHRM_SUPPORTED

-   else if (png_ptr->chunk_name == png_cHRM)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_sRGB_SUPPORTED

-   else if (chunk_name == png_sRGB)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_iCCP_SUPPORTED

-   else if (png_ptr->chunk_name == png_iCCP)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_sPLT_SUPPORTED

-   else if (chunk_name == png_sPLT)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_tRNS_SUPPORTED

-   else if (chunk_name == png_tRNS)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_bKGD_SUPPORTED

-   else if (chunk_name == png_bKGD)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_hIST_SUPPORTED

-   else if (chunk_name == png_hIST)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_pHYs_SUPPORTED

-   else if (chunk_name == png_pHYs)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_oFFs_SUPPORTED

-   else if (chunk_name == png_oFFs)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);

-   }

-#endif

-

-#ifdef PNG_READ_pCAL_SUPPORTED

-   else if (chunk_name == png_pCAL)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_sCAL_SUPPORTED

-   else if (chunk_name == png_sCAL)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_tIME_SUPPORTED

-   else if (chunk_name == png_tIME)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_tEXt_SUPPORTED

-   else if (chunk_name == png_tEXt)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_zTXt_SUPPORTED

-   else if (chunk_name == png_zTXt)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-#ifdef PNG_READ_iTXt_SUPPORTED

-   else if (chunk_name == png_iTXt)

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);

-   }

-

-#endif

-   else

-   {

-      if (png_ptr->push_length + 4 > png_ptr->buffer_size)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,

-         PNG_HANDLE_CHUNK_AS_DEFAULT);

-   }

-

-   png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;

-}

-

-void /* PRIVATE */

-png_push_crc_skip(png_structrp png_ptr, png_uint_32 skip)

-{

-   png_ptr->process_mode = PNG_SKIP_MODE;

-   png_ptr->skip_length = skip;

-}

-

-void /* PRIVATE */

-png_push_crc_finish(png_structrp png_ptr)

-{

-   if (png_ptr->skip_length && png_ptr->save_buffer_size)

-   {

-      png_size_t save_size = png_ptr->save_buffer_size;

-      png_uint_32 skip_length = png_ptr->skip_length;

-

-      /* We want the smaller of 'skip_length' and 'save_buffer_size', but

-       * they are of different types and we don't know which variable has the

-       * fewest bits.  Carefully select the smaller and cast it to the type of

-       * the larger - this cannot overflow.  Do not cast in the following test

-       * - it will break on either 16 or 64 bit platforms.

-       */

-      if (skip_length < save_size)

-         save_size = (png_size_t)skip_length;

-

-      else

-         skip_length = (png_uint_32)save_size;

-

-      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);

-

-      png_ptr->skip_length -= skip_length;

-      png_ptr->buffer_size -= save_size;

-      png_ptr->save_buffer_size -= save_size;

-      png_ptr->save_buffer_ptr += save_size;

-   }

-   if (png_ptr->skip_length && png_ptr->current_buffer_size)

-   {

-      png_size_t save_size = png_ptr->current_buffer_size;

-      png_uint_32 skip_length = png_ptr->skip_length;

-

-      /* We want the smaller of 'skip_length' and 'current_buffer_size', here,

-       * the same problem exists as above and the same solution.

-       */

-      if (skip_length < save_size)

-         save_size = (png_size_t)skip_length;

-

-      else

-         skip_length = (png_uint_32)save_size;

-

-      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);

-

-      png_ptr->skip_length -= skip_length;

-      png_ptr->buffer_size -= save_size;

-      png_ptr->current_buffer_size -= save_size;

-      png_ptr->current_buffer_ptr += save_size;

-   }

-   if (!png_ptr->skip_length)

-   {

-      if (png_ptr->buffer_size < 4)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_crc_finish(png_ptr, 0);

-      png_ptr->process_mode = PNG_READ_CHUNK_MODE;

-   }

-}

-

-void PNGCBAPI

-png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)

-{

-   png_bytep ptr;

-

-   if (png_ptr == NULL)

-      return;

-

-   ptr = buffer;

-   if (png_ptr->save_buffer_size)

-   {

-      png_size_t save_size;

-

-      if (length < png_ptr->save_buffer_size)

-         save_size = length;

-

-      else

-         save_size = png_ptr->save_buffer_size;

-

-      memcpy(ptr, png_ptr->save_buffer_ptr, save_size);

-      length -= save_size;

-      ptr += save_size;

-      png_ptr->buffer_size -= save_size;

-      png_ptr->save_buffer_size -= save_size;

-      png_ptr->save_buffer_ptr += save_size;

-   }

-   if (length && png_ptr->current_buffer_size)

-   {

-      png_size_t save_size;

-

-      if (length < png_ptr->current_buffer_size)

-         save_size = length;

-

-      else

-         save_size = png_ptr->current_buffer_size;

-

-      memcpy(ptr, png_ptr->current_buffer_ptr, save_size);

-      png_ptr->buffer_size -= save_size;

-      png_ptr->current_buffer_size -= save_size;

-      png_ptr->current_buffer_ptr += save_size;

-   }

-}

-

-void /* PRIVATE */

-png_push_save_buffer(png_structrp png_ptr)

-{

-   if (png_ptr->save_buffer_size)

-   {

-      if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)

-      {

-         png_size_t i, istop;

-         png_bytep sp;

-         png_bytep dp;

-

-         istop = png_ptr->save_buffer_size;

-         for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;

-             i < istop; i++, sp++, dp++)

-         {

-            *dp = *sp;

-         }

-      }

-   }

-   if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >

-       png_ptr->save_buffer_max)

-   {

-      png_size_t new_max;

-      png_bytep old_buffer;

-

-      if (png_ptr->save_buffer_size > PNG_SIZE_MAX -

-          (png_ptr->current_buffer_size + 256))

-      {

-         png_error(png_ptr, "Potential overflow of save_buffer");

-      }

-

-      new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;

-      old_buffer = png_ptr->save_buffer;

-      png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr,

-          (png_size_t)new_max);

-

-      if (png_ptr->save_buffer == NULL)

-      {

-         png_free(png_ptr, old_buffer);

-         png_error(png_ptr, "Insufficient memory for save_buffer");

-      }

-

-      memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);

-      png_free(png_ptr, old_buffer);

-      png_ptr->save_buffer_max = new_max;

-   }

-   if (png_ptr->current_buffer_size)

-   {

-      memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,

-         png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);

-      png_ptr->save_buffer_size += png_ptr->current_buffer_size;

-      png_ptr->current_buffer_size = 0;

-   }

-   png_ptr->save_buffer_ptr = png_ptr->save_buffer;

-   png_ptr->buffer_size = 0;

-}

-

-void /* PRIVATE */

-png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer,

-   png_size_t buffer_length)

-{

-   png_ptr->current_buffer = buffer;

-   png_ptr->current_buffer_size = buffer_length;

-   png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;

-   png_ptr->current_buffer_ptr = png_ptr->current_buffer;

-}

-

-void /* PRIVATE */

-png_push_read_IDAT(png_structrp png_ptr)

-{

-   if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))

-   {

-      png_byte chunk_length[4];

-      png_byte chunk_tag[4];

-

-      /* TODO: this code can be commoned up with the same code in push_read */

-      if (png_ptr->buffer_size < 8)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_push_fill_buffer(png_ptr, chunk_length, 4);

-      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);

-      png_reset_crc(png_ptr);

-      png_crc_read(png_ptr, chunk_tag, 4);

-      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);

-      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;

-

-      if (png_ptr->chunk_name != png_IDAT)

-      {

-         png_ptr->process_mode = PNG_READ_CHUNK_MODE;

-

-         if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))

-            png_error(png_ptr, "Not enough compressed data");

-

-         return;

-      }

-

-      png_ptr->idat_size = png_ptr->push_length;

-   }

-

-   if (png_ptr->idat_size && png_ptr->save_buffer_size)

-   {

-      png_size_t save_size = png_ptr->save_buffer_size;

-      png_uint_32 idat_size = png_ptr->idat_size;

-

-      /* We want the smaller of 'idat_size' and 'current_buffer_size', but they

-       * are of different types and we don't know which variable has the fewest

-       * bits.  Carefully select the smaller and cast it to the type of the

-       * larger - this cannot overflow.  Do not cast in the following test - it

-       * will break on either 16 or 64 bit platforms.

-       */

-      if (idat_size < save_size)

-         save_size = (png_size_t)idat_size;

-

-      else

-         idat_size = (png_uint_32)save_size;

-

-      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);

-

-      png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);

-

-      png_ptr->idat_size -= idat_size;

-      png_ptr->buffer_size -= save_size;

-      png_ptr->save_buffer_size -= save_size;

-      png_ptr->save_buffer_ptr += save_size;

-   }

-

-   if (png_ptr->idat_size && png_ptr->current_buffer_size)

-   {

-      png_size_t save_size = png_ptr->current_buffer_size;

-      png_uint_32 idat_size = png_ptr->idat_size;

-

-      /* We want the smaller of 'idat_size' and 'current_buffer_size', but they

-       * are of different types and we don't know which variable has the fewest

-       * bits.  Carefully select the smaller and cast it to the type of the

-       * larger - this cannot overflow.

-       */

-      if (idat_size < save_size)

-         save_size = (png_size_t)idat_size;

-

-      else

-         idat_size = (png_uint_32)save_size;

-

-      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);

-

-      png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);

-

-      png_ptr->idat_size -= idat_size;

-      png_ptr->buffer_size -= save_size;

-      png_ptr->current_buffer_size -= save_size;

-      png_ptr->current_buffer_ptr += save_size;

-   }

-   if (!png_ptr->idat_size)

-   {

-      if (png_ptr->buffer_size < 4)

-      {

-         png_push_save_buffer(png_ptr);

-         return;

-      }

-

-      png_crc_finish(png_ptr, 0);

-      png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;

-      png_ptr->mode |= PNG_AFTER_IDAT;

-      png_ptr->zowner = 0;

-   }

-}

-

-void /* PRIVATE */

-png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer,

-   png_size_t buffer_length)

-{

-   /* The caller checks for a non-zero buffer length. */

-   if (!(buffer_length > 0) || buffer == NULL)

-      png_error(png_ptr, "No IDAT data (internal error)");

-

-   /* This routine must process all the data it has been given

-    * before returning, calling the row callback as required to

-    * handle the uncompressed results.

-    */

-   png_ptr->zstream.next_in = buffer;

-   /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */

-   png_ptr->zstream.avail_in = (uInt)buffer_length;

-

-   /* Keep going until the decompressed data is all processed

-    * or the stream marked as finished.

-    */

-   while (png_ptr->zstream.avail_in > 0 &&

-      !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED))

-   {

-      int ret;

-

-      /* We have data for zlib, but we must check that zlib

-       * has someplace to put the results.  It doesn't matter

-       * if we don't expect any results -- it may be the input

-       * data is just the LZ end code.

-       */

-      if (!(png_ptr->zstream.avail_out > 0))

-      {

-         /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */

-         png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth,

-             png_ptr->iwidth) + 1);

-

-         png_ptr->zstream.next_out = png_ptr->row_buf;

-      }

-

-      /* Using Z_SYNC_FLUSH here means that an unterminated

-       * LZ stream (a stream with a missing end code) can still

-       * be handled, otherwise (Z_NO_FLUSH) a future zlib

-       * implementation might defer output and therefore

-       * change the current behavior (see comments in inflate.c

-       * for why this doesn't happen at present with zlib 1.2.5).

-       */

-      ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH);

-

-      /* Check for any failure before proceeding. */

-      if (ret != Z_OK && ret != Z_STREAM_END)

-      {

-         /* Terminate the decompression. */

-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;

-         png_ptr->zowner = 0;

-

-         /* This may be a truncated stream (missing or

-          * damaged end code).  Treat that as a warning.

-          */

-         if (png_ptr->row_number >= png_ptr->num_rows ||

-             png_ptr->pass > 6)

-            png_warning(png_ptr, "Truncated compressed data in IDAT");

-

-         else

-            png_error(png_ptr, "Decompression error in IDAT");

-

-         /* Skip the check on unprocessed input */

-         return;

-      }

-

-      /* Did inflate output any data? */

-      if (png_ptr->zstream.next_out != png_ptr->row_buf)

-      {

-         /* Is this unexpected data after the last row?

-          * If it is, artificially terminate the LZ output

-          * here.

-          */

-         if (png_ptr->row_number >= png_ptr->num_rows ||

-             png_ptr->pass > 6)

-         {

-            /* Extra data. */

-            png_warning(png_ptr, "Extra compressed data in IDAT");

-            png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;

-            png_ptr->zowner = 0;

-

-            /* Do no more processing; skip the unprocessed

-             * input check below.

-             */

-            return;

-         }

-

-         /* Do we have a complete row? */

-         if (png_ptr->zstream.avail_out == 0)

-            png_push_process_row(png_ptr);

-      }

-

-      /* And check for the end of the stream. */

-      if (ret == Z_STREAM_END)

-         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;

-   }

-

-   /* All the data should have been processed, if anything

-    * is left at this point we have bytes of IDAT data

-    * after the zlib end code.

-    */

-   if (png_ptr->zstream.avail_in > 0)

-      png_warning(png_ptr, "Extra compression data in IDAT");

-}

-

-void /* PRIVATE */

-png_push_process_row(png_structrp png_ptr)

-{

-   /* 1.5.6: row_info moved out of png_struct to a local here. */

-   png_row_info row_info;

-

-   row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */

-   row_info.color_type = png_ptr->color_type;

-   row_info.bit_depth = png_ptr->bit_depth;

-   row_info.channels = png_ptr->channels;

-   row_info.pixel_depth = png_ptr->pixel_depth;

-   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);

-

-   if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)

-   {

-      if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)

-         png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,

-            png_ptr->prev_row + 1, png_ptr->row_buf[0]);

-      else

-         png_error(png_ptr, "bad adaptive filter value");

-   }

-

-   /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before

-    * 1.5.6, while the buffer really is this big in current versions of libpng

-    * it may not be in the future, so this was changed just to copy the

-    * interlaced row count:

-    */

-   memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-   if (png_ptr->transformations)

-      png_do_read_transformations(png_ptr, &row_info);

-#endif

-

-   /* The transformed pixel depth should match the depth now in row_info. */

-   if (png_ptr->transformed_pixel_depth == 0)

-   {

-      png_ptr->transformed_pixel_depth = row_info.pixel_depth;

-      if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)

-         png_error(png_ptr, "progressive row overflow");

-   }

-

-   else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)

-      png_error(png_ptr, "internal progressive row size calculation error");

-

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   /* Blow up interlaced rows to full size */

-   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))

-   {

-      if (png_ptr->pass < 6)

-         png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,

-            png_ptr->transformations);

-

-    switch (png_ptr->pass)

-    {

-         case 0:

-         {

-            int i;

-            for (i = 0; i < 8 && png_ptr->pass == 0; i++)

-            {

-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-               png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */

-            }

-

-            if (png_ptr->pass == 2) /* Pass 1 might be empty */

-            {

-               for (i = 0; i < 4 && png_ptr->pass == 2; i++)

-               {

-                  png_push_have_row(png_ptr, NULL);

-                  png_read_push_finish_row(png_ptr);

-               }

-            }

-

-            if (png_ptr->pass == 4 && png_ptr->height <= 4)

-            {

-               for (i = 0; i < 2 && png_ptr->pass == 4; i++)

-               {

-                  png_push_have_row(png_ptr, NULL);

-                  png_read_push_finish_row(png_ptr);

-               }

-            }

-

-            if (png_ptr->pass == 6 && png_ptr->height <= 4)

-            {

-                png_push_have_row(png_ptr, NULL);

-                png_read_push_finish_row(png_ptr);

-            }

-

-            break;

-         }

-

-         case 1:

-         {

-            int i;

-            for (i = 0; i < 8 && png_ptr->pass == 1; i++)

-            {

-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            if (png_ptr->pass == 2) /* Skip top 4 generated rows */

-            {

-               for (i = 0; i < 4 && png_ptr->pass == 2; i++)

-               {

-                  png_push_have_row(png_ptr, NULL);

-                  png_read_push_finish_row(png_ptr);

-               }

-            }

-

-            break;

-         }

-

-         case 2:

-         {

-            int i;

-

-            for (i = 0; i < 4 && png_ptr->pass == 2; i++)

-            {

-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            for (i = 0; i < 4 && png_ptr->pass == 2; i++)

-            {

-               png_push_have_row(png_ptr, NULL);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            if (png_ptr->pass == 4) /* Pass 3 might be empty */

-            {

-               for (i = 0; i < 2 && png_ptr->pass == 4; i++)

-               {

-                  png_push_have_row(png_ptr, NULL);

-                  png_read_push_finish_row(png_ptr);

-               }

-            }

-

-            break;

-         }

-

-         case 3:

-         {

-            int i;

-

-            for (i = 0; i < 4 && png_ptr->pass == 3; i++)

-            {

-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            if (png_ptr->pass == 4) /* Skip top two generated rows */

-            {

-               for (i = 0; i < 2 && png_ptr->pass == 4; i++)

-               {

-                  png_push_have_row(png_ptr, NULL);

-                  png_read_push_finish_row(png_ptr);

-               }

-            }

-

-            break;

-         }

-

-         case 4:

-         {

-            int i;

-

-            for (i = 0; i < 2 && png_ptr->pass == 4; i++)

-            {

-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            for (i = 0; i < 2 && png_ptr->pass == 4; i++)

-            {

-               png_push_have_row(png_ptr, NULL);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            if (png_ptr->pass == 6) /* Pass 5 might be empty */

-            {

-               png_push_have_row(png_ptr, NULL);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            break;

-         }

-

-         case 5:

-         {

-            int i;

-

-            for (i = 0; i < 2 && png_ptr->pass == 5; i++)

-            {

-               png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            if (png_ptr->pass == 6) /* Skip top generated row */

-            {

-               png_push_have_row(png_ptr, NULL);

-               png_read_push_finish_row(png_ptr);

-            }

-

-            break;

-         }

-

-         default:

-         case 6:

-         {

-            png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-            png_read_push_finish_row(png_ptr);

-

-            if (png_ptr->pass != 6)

-               break;

-

-            png_push_have_row(png_ptr, NULL);

-            png_read_push_finish_row(png_ptr);

-         }

-      }

-   }

-   else

-#endif

-   {

-      png_push_have_row(png_ptr, png_ptr->row_buf + 1);

-      png_read_push_finish_row(png_ptr);

-   }

-}

-

-void /* PRIVATE */

-png_read_push_finish_row(png_structrp png_ptr)

-{

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */

-

-   /* Start of interlace block */

-   static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};

-

-   /* Offset to next interlace block */

-   static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};

-

-   /* Start of interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};

-

-   /* Offset to next interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};

-

-   /* Height of interlace block.  This is not currently used - if you need

-    * it, uncomment it here and in png.h

-   static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};

-   */

-#endif

-

-   png_ptr->row_number++;

-   if (png_ptr->row_number < png_ptr->num_rows)

-      return;

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   if (png_ptr->interlaced)

-   {

-      png_ptr->row_number = 0;

-      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);

-

-      do

-      {

-         png_ptr->pass++;

-         if ((png_ptr->pass == 1 && png_ptr->width < 5) ||

-             (png_ptr->pass == 3 && png_ptr->width < 3) ||

-             (png_ptr->pass == 5 && png_ptr->width < 2))

-            png_ptr->pass++;

-

-         if (png_ptr->pass > 7)

-            png_ptr->pass--;

-

-         if (png_ptr->pass >= 7)

-            break;

-

-         png_ptr->iwidth = (png_ptr->width +

-             png_pass_inc[png_ptr->pass] - 1 -

-             png_pass_start[png_ptr->pass]) /

-             png_pass_inc[png_ptr->pass];

-

-         if (png_ptr->transformations & PNG_INTERLACE)

-            break;

-

-         png_ptr->num_rows = (png_ptr->height +

-             png_pass_yinc[png_ptr->pass] - 1 -

-             png_pass_ystart[png_ptr->pass]) /

-             png_pass_yinc[png_ptr->pass];

-

-      } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);

-   }

-#endif /* PNG_READ_INTERLACING_SUPPORTED */

-}

-

-void /* PRIVATE */

-png_push_have_info(png_structrp png_ptr, png_inforp info_ptr)

-{

-   if (png_ptr->info_fn != NULL)

-      (*(png_ptr->info_fn))(png_ptr, info_ptr);

-}

-

-void /* PRIVATE */

-png_push_have_end(png_structrp png_ptr, png_inforp info_ptr)

-{

-   if (png_ptr->end_fn != NULL)

-      (*(png_ptr->end_fn))(png_ptr, info_ptr);

-}

-

-void /* PRIVATE */

-png_push_have_row(png_structrp png_ptr, png_bytep row)

-{

-   if (png_ptr->row_fn != NULL)

-      (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,

-         (int)png_ptr->pass);

-}

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-void PNGAPI

-png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row,

-    png_const_bytep new_row)

-{

-   if (png_ptr == NULL)

-      return;

-

-   /* new_row is a flag here - if it is NULL then the app callback was called

-    * from an empty row (see the calls to png_struct::row_fn below), otherwise

-    * it must be png_ptr->row_buf+1

-    */

-   if (new_row != NULL)

-      png_combine_row(png_ptr, old_row, 1/*display*/);

-}

-#endif /* PNG_READ_INTERLACING_SUPPORTED */

-

-void PNGAPI

-png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr,

-    png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,

-    png_progressive_end_ptr end_fn)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->info_fn = info_fn;

-   png_ptr->row_fn = row_fn;

-   png_ptr->end_fn = end_fn;

-

-   png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);

-}

-

-png_voidp PNGAPI

-png_get_progressive_ptr(png_const_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return (NULL);

-

-   return png_ptr->io_ptr;

-}

-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */

+
+/* pngpread.c - read a png file in push mode
+ *
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+
+/* Push model modes */
+#define PNG_READ_SIG_MODE   0
+#define PNG_READ_CHUNK_MODE 1
+#define PNG_READ_IDAT_MODE  2
+#define PNG_READ_tEXt_MODE  4
+#define PNG_READ_zTXt_MODE  5
+#define PNG_READ_DONE_MODE  6
+#define PNG_READ_iTXt_MODE  7
+#define PNG_ERROR_MODE      8
+
+#define PNG_PUSH_SAVE_BUFFER_IF_FULL \
+if (png_ptr->push_length + 4 > png_ptr->buffer_size) \
+   { png_push_save_buffer(png_ptr); return; }
+#define PNG_PUSH_SAVE_BUFFER_IF_LT(N) \
+if (png_ptr->buffer_size < N) \
+   { png_push_save_buffer(png_ptr); return; }
+
+void PNGAPI
+png_process_data(png_structrp png_ptr, png_inforp info_ptr,
+    png_bytep buffer, png_size_t buffer_size)
+{
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_push_restore_buffer(png_ptr, buffer, buffer_size);
+
+   while (png_ptr->buffer_size)
+   {
+      png_process_some_data(png_ptr, info_ptr);
+   }
+}
+
+png_size_t PNGAPI
+png_process_data_pause(png_structrp png_ptr, int save)
+{
+   if (png_ptr != NULL)
+   {
+      /* It's easiest for the caller if we do the save; then the caller doesn't
+       * have to supply the same data again:
+       */
+      if (save != 0)
+         png_push_save_buffer(png_ptr);
+      else
+      {
+         /* This includes any pending saved bytes: */
+         png_size_t remaining = png_ptr->buffer_size;
+         png_ptr->buffer_size = 0;
+
+         /* So subtract the saved buffer size, unless all the data
+          * is actually 'saved', in which case we just return 0
+          */
+         if (png_ptr->save_buffer_size < remaining)
+            return remaining - png_ptr->save_buffer_size;
+      }
+   }
+
+   return 0;
+}
+
+png_uint_32 PNGAPI
+png_process_data_skip(png_structrp png_ptr)
+{
+  /* TODO: Deprecate and remove this API.
+   * Somewhere the implementation of this seems to have been lost,
+   * or abandoned.  It was only to support some internal back-door access
+   * to png_struct) in libpng-1.4.x.
+   */
+   png_app_warning(png_ptr,
+"png_process_data_skip is not implemented in any current version of libpng");
+   return 0;
+}
+
+/* What we do with the incoming data depends on what we were previously
+ * doing before we ran out of data...
+ */
+void /* PRIVATE */
+png_process_some_data(png_structrp png_ptr, png_inforp info_ptr)
+{
+   if (png_ptr == NULL)
+      return;
+
+   switch (png_ptr->process_mode)
+   {
+      case PNG_READ_SIG_MODE:
+      {
+         png_push_read_sig(png_ptr, info_ptr);
+         break;
+      }
+
+      case PNG_READ_CHUNK_MODE:
+      {
+         png_push_read_chunk(png_ptr, info_ptr);
+         break;
+      }
+
+      case PNG_READ_IDAT_MODE:
+      {
+         png_push_read_IDAT(png_ptr);
+         break;
+      }
+
+      default:
+      {
+         png_ptr->buffer_size = 0;
+         break;
+      }
+   }
+}
+
+/* Read any remaining signature bytes from the stream and compare them with
+ * the correct PNG signature.  It is possible that this routine is called
+ * with bytes already read from the signature, either because they have been
+ * checked by the calling application, or because of multiple calls to this
+ * routine.
+ */
+void /* PRIVATE */
+png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr)
+{
+   png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */
+       num_to_check = 8 - num_checked;
+
+   if (png_ptr->buffer_size < num_to_check)
+   {
+      num_to_check = png_ptr->buffer_size;
+   }
+
+   png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
+       num_to_check);
+   png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check);
+
+   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+   {
+      if (num_checked < 4 &&
+          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+         png_error(png_ptr, "Not a PNG file");
+
+      else
+         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+   }
+   else
+   {
+      if (png_ptr->sig_bytes >= 8)
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+      }
+   }
+}
+
+void /* PRIVATE */
+png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
+{
+   png_uint_32 chunk_name;
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   int keep; /* unknown handling method */
+#endif
+
+   /* First we make sure we have enough data for the 4-byte chunk name
+    * and the 4-byte chunk length before proceeding with decoding the
+    * chunk data.  To fully decode each of these chunks, we also make
+    * sure we have enough data in the buffer for the 4-byte CRC at the
+    * end of every chunk (except IDAT, which is handled separately).
+    */
+   if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0)
+   {
+      png_byte chunk_length[4];
+      png_byte chunk_tag[4];
+
+      PNG_PUSH_SAVE_BUFFER_IF_LT(8)
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, chunk_tag, 4);
+      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
+      png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+   }
+
+   chunk_name = png_ptr->chunk_name;
+
+   if (chunk_name == png_IDAT)
+   {
+      if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
+         png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
+
+      /* If we reach an IDAT chunk, this means we have read all of the
+       * header chunks, and we can start reading the image (or if this
+       * is called after the image has been read - we have an error).
+       */
+      if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+         png_error(png_ptr, "Missing IHDR before IDAT");
+
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+          (png_ptr->mode & PNG_HAVE_PLTE) == 0)
+         png_error(png_ptr, "Missing PLTE before IDAT");
+
+      png_ptr->mode |= PNG_HAVE_IDAT;
+      png_ptr->process_mode = PNG_READ_IDAT_MODE;
+
+      if ((png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) == 0)
+         if (png_ptr->push_length == 0)
+            return;
+
+      if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
+         png_benign_error(png_ptr, "Too many IDATs found");
+   }
+
+   if (chunk_name == png_IHDR)
+   {
+      if (png_ptr->push_length != 13)
+         png_error(png_ptr, "Invalid IHDR length");
+
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+   else if (chunk_name == png_IEND)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
+
+      png_ptr->process_mode = PNG_READ_DONE_MODE;
+      png_push_have_end(png_ptr, info_ptr);
+   }
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep);
+
+      if (chunk_name == png_PLTE)
+         png_ptr->mode |= PNG_HAVE_PLTE;
+   }
+#endif
+
+   else if (chunk_name == png_PLTE)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+   else if (chunk_name == png_IDAT)
+   {
+      png_ptr->idat_size = png_ptr->push_length;
+      png_ptr->process_mode = PNG_READ_IDAT_MODE;
+      png_push_have_info(png_ptr, info_ptr);
+      png_ptr->zstream.avail_out =
+          (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,
+          png_ptr->iwidth) + 1;
+      png_ptr->zstream.next_out = png_ptr->row_buf;
+      return;
+   }
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+   else if (png_ptr->chunk_name == png_gAMA)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_sBIT_SUPPORTED
+   else if (png_ptr->chunk_name == png_sBIT)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_cHRM_SUPPORTED
+   else if (png_ptr->chunk_name == png_cHRM)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_sRGB_SUPPORTED
+   else if (chunk_name == png_sRGB)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_iCCP_SUPPORTED
+   else if (png_ptr->chunk_name == png_iCCP)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_sPLT_SUPPORTED
+   else if (chunk_name == png_sPLT)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_tRNS_SUPPORTED
+   else if (chunk_name == png_tRNS)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_bKGD_SUPPORTED
+   else if (chunk_name == png_bKGD)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_hIST_SUPPORTED
+   else if (chunk_name == png_hIST)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_pHYs_SUPPORTED
+   else if (chunk_name == png_pHYs)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_oFFs_SUPPORTED
+   else if (chunk_name == png_oFFs)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+   else if (chunk_name == png_pCAL)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_sCAL_SUPPORTED
+   else if (chunk_name == png_sCAL)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_tIME_SUPPORTED
+   else if (chunk_name == png_tIME)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_tEXt_SUPPORTED
+   else if (chunk_name == png_tEXt)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_zTXt_SUPPORTED
+   else if (chunk_name == png_zTXt)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+#endif
+#ifdef PNG_READ_iTXt_SUPPORTED
+   else if (chunk_name == png_iTXt)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+
+   else
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_FULL
+      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length,
+         PNG_HANDLE_CHUNK_AS_DEFAULT);
+   }
+
+   png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+}
+
+void PNGCBAPI
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
+{
+   png_bytep ptr;
+
+   if (png_ptr == NULL)
+      return;
+
+   ptr = buffer;
+   if (png_ptr->save_buffer_size != 0)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->save_buffer_size)
+         save_size = length;
+
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      memcpy(ptr, png_ptr->save_buffer_ptr, save_size);
+      length -= save_size;
+      ptr += save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (length != 0 && png_ptr->current_buffer_size != 0)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->current_buffer_size)
+         save_size = length;
+
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      memcpy(ptr, png_ptr->current_buffer_ptr, save_size);
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+}
+
+void /* PRIVATE */
+png_push_save_buffer(png_structrp png_ptr)
+{
+   if (png_ptr->save_buffer_size != 0)
+   {
+      if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
+      {
+         png_size_t i, istop;
+         png_bytep sp;
+         png_bytep dp;
+
+         istop = png_ptr->save_buffer_size;
+         for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
+             i < istop; i++, sp++, dp++)
+         {
+            *dp = *sp;
+         }
+      }
+   }
+   if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
+       png_ptr->save_buffer_max)
+   {
+      png_size_t new_max;
+      png_bytep old_buffer;
+
+      if (png_ptr->save_buffer_size > PNG_SIZE_MAX -
+          (png_ptr->current_buffer_size + 256))
+      {
+         png_error(png_ptr, "Potential overflow of save_buffer");
+      }
+
+      new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
+      old_buffer = png_ptr->save_buffer;
+      png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr,
+          (png_size_t)new_max);
+
+      if (png_ptr->save_buffer == NULL)
+      {
+         png_free(png_ptr, old_buffer);
+         png_error(png_ptr, "Insufficient memory for save_buffer");
+      }
+
+      memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+      png_free(png_ptr, old_buffer);
+      png_ptr->save_buffer_max = new_max;
+   }
+   if (png_ptr->current_buffer_size)
+   {
+      memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
+         png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
+      png_ptr->save_buffer_size += png_ptr->current_buffer_size;
+      png_ptr->current_buffer_size = 0;
+   }
+   png_ptr->save_buffer_ptr = png_ptr->save_buffer;
+   png_ptr->buffer_size = 0;
+}
+
+void /* PRIVATE */
+png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   png_ptr->current_buffer = buffer;
+   png_ptr->current_buffer_size = buffer_length;
+   png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
+   png_ptr->current_buffer_ptr = png_ptr->current_buffer;
+}
+
+void /* PRIVATE */
+png_push_read_IDAT(png_structrp png_ptr)
+{
+   if ((png_ptr->mode & PNG_HAVE_CHUNK_HEADER) == 0)
+   {
+      png_byte chunk_length[4];
+      png_byte chunk_tag[4];
+
+      /* TODO: this code can be commoned up with the same code in push_read */
+      PNG_PUSH_SAVE_BUFFER_IF_LT(8)
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, chunk_tag, 4);
+      png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag);
+      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+
+      if (png_ptr->chunk_name != png_IDAT)
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+
+         if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
+            png_error(png_ptr, "Not enough compressed data");
+
+         return;
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+   }
+
+   if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0)
+   {
+      png_size_t save_size = png_ptr->save_buffer_size;
+      png_uint_32 idat_size = png_ptr->idat_size;
+
+      /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
+       * are of different types and we don't know which variable has the fewest
+       * bits.  Carefully select the smaller and cast it to the type of the
+       * larger - this cannot overflow.  Do not cast in the following test - it
+       * will break on either 16-bit or 64-bit platforms.
+       */
+      if (idat_size < save_size)
+         save_size = (png_size_t)idat_size;
+
+      else
+         idat_size = (png_uint_32)save_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+      png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+      png_ptr->idat_size -= idat_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+
+   if (png_ptr->idat_size != 0 && png_ptr->current_buffer_size != 0)
+   {
+      png_size_t save_size = png_ptr->current_buffer_size;
+      png_uint_32 idat_size = png_ptr->idat_size;
+
+      /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
+       * are of different types and we don't know which variable has the fewest
+       * bits.  Carefully select the smaller and cast it to the type of the
+       * larger - this cannot overflow.
+       */
+      if (idat_size < save_size)
+         save_size = (png_size_t)idat_size;
+
+      else
+         idat_size = (png_uint_32)save_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->idat_size -= idat_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+
+   if (png_ptr->idat_size == 0)
+   {
+      PNG_PUSH_SAVE_BUFFER_IF_LT(4)
+      png_crc_finish(png_ptr, 0);
+      png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+      png_ptr->mode |= PNG_AFTER_IDAT;
+      png_ptr->zowner = 0;
+   }
+}
+
+void /* PRIVATE */
+png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   /* The caller checks for a non-zero buffer length. */
+   if (!(buffer_length > 0) || buffer == NULL)
+      png_error(png_ptr, "No IDAT data (internal error)");
+
+   /* This routine must process all the data it has been given
+    * before returning, calling the row callback as required to
+    * handle the uncompressed results.
+    */
+   png_ptr->zstream.next_in = buffer;
+   /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */
+   png_ptr->zstream.avail_in = (uInt)buffer_length;
+
+   /* Keep going until the decompressed data is all processed
+    * or the stream marked as finished.
+    */
+   while (png_ptr->zstream.avail_in > 0 &&
+      (png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0)
+   {
+      int ret;
+
+      /* We have data for zlib, but we must check that zlib
+       * has someplace to put the results.  It doesn't matter
+       * if we don't expect any results -- it may be the input
+       * data is just the LZ end code.
+       */
+      if (!(png_ptr->zstream.avail_out > 0))
+      {
+         /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */
+         png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth,
+             png_ptr->iwidth) + 1);
+
+         png_ptr->zstream.next_out = png_ptr->row_buf;
+      }
+
+      /* Using Z_SYNC_FLUSH here means that an unterminated
+       * LZ stream (a stream with a missing end code) can still
+       * be handled, otherwise (Z_NO_FLUSH) a future zlib
+       * implementation might defer output and therefore
+       * change the current behavior (see comments in inflate.c
+       * for why this doesn't happen at present with zlib 1.2.5).
+       */
+      ret = PNG_INFLATE(png_ptr, Z_SYNC_FLUSH);
+
+      /* Check for any failure before proceeding. */
+      if (ret != Z_OK && ret != Z_STREAM_END)
+      {
+         /* Terminate the decompression. */
+         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+         png_ptr->zowner = 0;
+
+         /* This may be a truncated stream (missing or
+          * damaged end code).  Treat that as a warning.
+          */
+         if (png_ptr->row_number >= png_ptr->num_rows ||
+             png_ptr->pass > 6)
+            png_warning(png_ptr, "Truncated compressed data in IDAT");
+
+         else
+            png_error(png_ptr, "Decompression error in IDAT");
+
+         /* Skip the check on unprocessed input */
+         return;
+      }
+
+      /* Did inflate output any data? */
+      if (png_ptr->zstream.next_out != png_ptr->row_buf)
+      {
+         /* Is this unexpected data after the last row?
+          * If it is, artificially terminate the LZ output
+          * here.
+          */
+         if (png_ptr->row_number >= png_ptr->num_rows ||
+             png_ptr->pass > 6)
+         {
+            /* Extra data. */
+            png_warning(png_ptr, "Extra compressed data in IDAT");
+            png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+            png_ptr->zowner = 0;
+
+            /* Do no more processing; skip the unprocessed
+             * input check below.
+             */
+            return;
+         }
+
+         /* Do we have a complete row? */
+         if (png_ptr->zstream.avail_out == 0)
+            png_push_process_row(png_ptr);
+      }
+
+      /* And check for the end of the stream. */
+      if (ret == Z_STREAM_END)
+         png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED;
+   }
+
+   /* All the data should have been processed, if anything
+    * is left at this point we have bytes of IDAT data
+    * after the zlib end code.
+    */
+   if (png_ptr->zstream.avail_in > 0)
+      png_warning(png_ptr, "Extra compression data in IDAT");
+}
+
+void /* PRIVATE */
+png_push_process_row(png_structrp png_ptr)
+{
+   /* 1.5.6: row_info moved out of png_struct to a local here. */
+   png_row_info row_info;
+
+   row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
+   row_info.color_type = png_ptr->color_type;
+   row_info.bit_depth = png_ptr->bit_depth;
+   row_info.channels = png_ptr->channels;
+   row_info.pixel_depth = png_ptr->pixel_depth;
+   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
+
+   if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
+   {
+      if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
+         png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
+            png_ptr->prev_row + 1, png_ptr->row_buf[0]);
+      else
+         png_error(png_ptr, "bad adaptive filter value");
+   }
+
+   /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
+    * 1.5.6, while the buffer really is this big in current versions of libpng
+    * it may not be in the future, so this was changed just to copy the
+    * interlaced row count:
+    */
+   memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+   if (png_ptr->transformations != 0)
+      png_do_read_transformations(png_ptr, &row_info);
+#endif
+
+   /* The transformed pixel depth should match the depth now in row_info. */
+   if (png_ptr->transformed_pixel_depth == 0)
+   {
+      png_ptr->transformed_pixel_depth = row_info.pixel_depth;
+      if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)
+         png_error(png_ptr, "progressive row overflow");
+   }
+
+   else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)
+      png_error(png_ptr, "internal progressive row size calculation error");
+
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   /* Expand interlaced rows to full size */
+   if (png_ptr->interlaced != 0 &&
+       (png_ptr->transformations & PNG_INTERLACE) != 0)
+   {
+      if (png_ptr->pass < 6)
+         png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
+            png_ptr->transformations);
+
+      switch (png_ptr->pass)
+      {
+         case 0:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 0; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */
+            }
+
+            if (png_ptr->pass == 2) /* Pass 1 might be empty */
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+
+            if (png_ptr->pass == 4 && png_ptr->height <= 4)
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+
+            if (png_ptr->pass == 6 && png_ptr->height <= 4)
+            {
+                png_push_have_row(png_ptr, NULL);
+                png_read_push_finish_row(png_ptr);
+            }
+
+            break;
+         }
+
+         case 1:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 1; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            if (png_ptr->pass == 2) /* Skip top 4 generated rows */
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+
+            break;
+         }
+
+         case 2:
+         {
+            int i;
+
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            if (png_ptr->pass == 4) /* Pass 3 might be empty */
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+
+            break;
+         }
+
+         case 3:
+         {
+            int i;
+
+            for (i = 0; i < 4 && png_ptr->pass == 3; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            if (png_ptr->pass == 4) /* Skip top two generated rows */
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+
+            break;
+         }
+
+         case 4:
+         {
+            int i;
+
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            if (png_ptr->pass == 6) /* Pass 5 might be empty */
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            break;
+         }
+
+         case 5:
+         {
+            int i;
+
+            for (i = 0; i < 2 && png_ptr->pass == 5; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            if (png_ptr->pass == 6) /* Skip top generated row */
+            {
+               png_push_have_row(png_ptr, NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+
+            break;
+         }
+
+         default:
+         case 6:
+         {
+            png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+            png_read_push_finish_row(png_ptr);
+
+            if (png_ptr->pass != 6)
+               break;
+
+            png_push_have_row(png_ptr, NULL);
+            png_read_push_finish_row(png_ptr);
+         }
+      }
+   }
+   else
+#endif
+   {
+      png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+      png_read_push_finish_row(png_ptr);
+   }
+}
+
+void /* PRIVATE */
+png_read_push_finish_row(png_structrp png_ptr)
+{
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* Start of interlace block */
+   static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* Offset to next interlace block */
+   static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* Start of interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* Offset to next interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+   /* Height of interlace block.  This is not currently used - if you need
+    * it, uncomment it here and in png.h
+   static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+   */
+#endif
+
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   if (png_ptr->interlaced != 0)
+   {
+      png_ptr->row_number = 0;
+      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+      do
+      {
+         png_ptr->pass++;
+         if ((png_ptr->pass == 1 && png_ptr->width < 5) ||
+             (png_ptr->pass == 3 && png_ptr->width < 3) ||
+             (png_ptr->pass == 5 && png_ptr->width < 2))
+            png_ptr->pass++;
+
+         if (png_ptr->pass > 7)
+            png_ptr->pass--;
+
+         if (png_ptr->pass >= 7)
+            break;
+
+         png_ptr->iwidth = (png_ptr->width +
+             png_pass_inc[png_ptr->pass] - 1 -
+             png_pass_start[png_ptr->pass]) /
+             png_pass_inc[png_ptr->pass];
+
+         if ((png_ptr->transformations & PNG_INTERLACE) != 0)
+            break;
+
+         png_ptr->num_rows = (png_ptr->height +
+             png_pass_yinc[png_ptr->pass] - 1 -
+             png_pass_ystart[png_ptr->pass]) /
+             png_pass_yinc[png_ptr->pass];
+
+      } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
+   }
+#endif /* READ_INTERLACING */
+}
+
+void /* PRIVATE */
+png_push_have_info(png_structrp png_ptr, png_inforp info_ptr)
+{
+   if (png_ptr->info_fn != NULL)
+      (*(png_ptr->info_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_end(png_structrp png_ptr, png_inforp info_ptr)
+{
+   if (png_ptr->end_fn != NULL)
+      (*(png_ptr->end_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_row(png_structrp png_ptr, png_bytep row)
+{
+   if (png_ptr->row_fn != NULL)
+      (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
+         (int)png_ptr->pass);
+}
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+void PNGAPI
+png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row,
+    png_const_bytep new_row)
+{
+   if (png_ptr == NULL)
+      return;
+
+   /* new_row is a flag here - if it is NULL then the app callback was called
+    * from an empty row (see the calls to png_struct::row_fn below), otherwise
+    * it must be png_ptr->row_buf+1
+    */
+   if (new_row != NULL)
+      png_combine_row(png_ptr, old_row, 1/*blocky display*/);
+}
+#endif /* READ_INTERLACING */
+
+void PNGAPI
+png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr,
+    png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+    png_progressive_end_ptr end_fn)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->info_fn = info_fn;
+   png_ptr->row_fn = row_fn;
+   png_ptr->end_fn = end_fn;
+
+   png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
+}
+
+png_voidp PNGAPI
+png_get_progressive_ptr(png_const_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return (NULL);
+
+   return png_ptr->io_ptr;
+}
+#endif /* PROGRESSIVE_READ */
diff --git a/third_party/lpng_v163/pngpriv.h b/third_party/libpng/pngpriv.h
similarity index 90%
rename from third_party/lpng_v163/pngpriv.h
rename to third_party/libpng/pngpriv.h
index 4ef3b18..58d3aa8 100644
--- a/third_party/lpng_v163/pngpriv.h
+++ b/third_party/libpng/pngpriv.h
@@ -1,2010 +1,1929 @@
-

-/* pngpriv.h - private declarations for use inside libpng

- *

- * For conditions of distribution and use, see copyright notice in png.h

- * 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.)

- *

- * Last changed in libpng 1.6.3 [July 18, 2013]

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

-/* The symbols declared in this file (including the functions declared

- * as extern) are PRIVATE.  They are not part of the libpng public

- * interface, and are not recommended for use by regular applications.

- * Some of them may become public in the future; others may stay private,

- * change in an incompatible way, or even disappear.

- * Although the libpng users are not forbidden to include this header,

- * they should be well aware of the issues that may arise from doing so.

- */

-

-#ifndef PNGPRIV_H

-#define PNGPRIV_H

-

-/* Feature Test Macros.  The following are defined here to ensure that correctly

- * implemented libraries reveal the APIs libpng needs to build and hide those

- * that are not needed and potentially damaging to the compilation.

- *

- * Feature Test Macros must be defined before any system header is included (see

- * POSIX 1003.1 2.8.2 "POSIX Symbols."

- *

- * These macros only have an effect if the operating system supports either

- * POSIX 1003.1 or C99, or both.  On other operating systems (particularly

- * Windows/Visual Studio) there is no effect; the OS specific tests below are

- * still required (as of 2011-05-02.)

- */

-#define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */

-

-#ifndef PNG_VERSION_INFO_ONLY

-/* Standard library headers not required by png.h: */

-#  include <stdlib.h>

-#  include <string.h>

-#endif

-

-#define PNGLIB_BUILD /*libpng is being built, not used*/

-

-/* If HAVE_CONFIG_H is defined during the build then the build system must

- * provide an appropriate "config.h" file on the include path.  The header file

- * must provide definitions as required below (search for "HAVE_CONFIG_H");

- * see configure.ac for more details of the requirements.  The macro

- * "PNG_NO_CONFIG_H" is provided for maintainers to test for dependencies on

- * 'configure'; define this macro to prevent the configure build including the

- * configure generated config.h.  Libpng is expected to compile without *any*

- * special build system support on a reasonably ANSI-C compliant system.

- */

-#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)

-#  include <config.h>

-

-   /* Pick up the definition of 'restrict' from config.h if it was read: */

-#  define PNG_RESTRICT restrict

-#endif

-

-/* To support symbol prefixing it is necessary to know *before* including png.h

- * whether the fixed point (and maybe other) APIs are exported, because if they

- * are not internal definitions may be required.  This is handled below just

- * before png.h is included, but load the configuration now if it is available.

- */

-#ifndef PNGLCONF_H

-#  include "pnglibconf.h"

-#endif

-

-/* Local renames may change non-exported API functions from png.h */

-#if defined(PNG_PREFIX) && !defined(PNGPREFIX_H)

-#  include "pngprefix.h"

-#endif

-

-#ifdef PNG_USER_CONFIG

-#  include "pngusr.h"

-   /* These should have been defined in pngusr.h */

-#  ifndef PNG_USER_PRIVATEBUILD

-#    define PNG_USER_PRIVATEBUILD "Custom libpng build"

-#  endif

-#  ifndef PNG_USER_DLLFNAME_POSTFIX

-#    define PNG_USER_DLLFNAME_POSTFIX "Cb"

-#  endif

-#endif

-

-/* Compile time options.

- * =====================

- * In a multi-arch build the compiler may compile the code several times for the

- * same object module, producing different binaries for different architectures.

- * When this happens configure-time setting of the target host options cannot be

- * done and this interferes with the handling of the ARM NEON optimizations, and

- * possibly other similar optimizations.  Put additional tests here; in general

- * this is needed when the same option can be changed at both compile time and

- * run time depending on the target OS (i.e. iOS vs Android.)

- *

- * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because

- * this is not possible with certain compilers (Oracle SUN OS CC), as a result

- * it is necessary to ensure that all extern functions that *might* be used

- * regardless of $(CFLAGS) get declared in this file.  The test on __ARM_NEON__

- * below is one example of this behavior because it is controlled by the

- * presence or not of -mfpu=neon on the GCC command line, it is possible to do

- * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely

- * do this.

- */

-#ifndef PNG_ARM_NEON_OPT

-   /* ARM NEON optimizations are being controlled by the compiler settings,

-    * typically the target FPU.  If the FPU has been set to NEON (-mfpu=neon

-    * with GCC) then the compiler will define __ARM_NEON__ and we can rely

-    * unconditionally on NEON instructions not crashing, otherwise we must

-    * disable use of NEON instructions:

-    */

-#  ifdef __ARM_NEON__

-#     define PNG_ARM_NEON_OPT 2

-#  else

-#     define PNG_ARM_NEON_OPT 0

-#  endif

-#endif

-

-#if PNG_ARM_NEON_OPT > 0

-   /* NEON optimizations are to be at least considered by libpng, so enable the

-    * callbacks to do this.

-    */

-/*

- * Disabled by sbc since this copy of libpng doesn't contains the arm

- * specific code (e.g. arm/arm_init.c)

- */

-/*#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon*/

-#endif

-

-/* Is this a build of a DLL where compilation of the object modules requires

- * different preprocessor settings to those required for a simple library?  If

- * so PNG_BUILD_DLL must be set.

- *

- * If libpng is used inside a DLL but that DLL does not export the libpng APIs

- * PNG_BUILD_DLL must not be set.  To avoid the code below kicking in build a

- * static library of libpng then link the DLL against that.

- */

-#ifndef PNG_BUILD_DLL

-#  ifdef DLL_EXPORT

-      /* This is set by libtool when files are compiled for a DLL; libtool

-       * always compiles twice, even on systems where it isn't necessary.  Set

-       * PNG_BUILD_DLL in case it is necessary:

-       */

-#     define PNG_BUILD_DLL

-#  else

-#     ifdef _WINDLL

-         /* This is set by the Microsoft Visual Studio IDE in projects that

-          * build a DLL.  It can't easily be removed from those projects (it

-          * isn't visible in the Visual Studio UI) so it is a fairly reliable

-          * indication that PNG_IMPEXP needs to be set to the DLL export

-          * attributes.

-          */

-#        define PNG_BUILD_DLL

-#     else

-#        ifdef __DLL__

-            /* This is set by the Borland C system when compiling for a DLL

-             * (as above.)

-             */

-#           define PNG_BUILD_DLL

-#        else

-            /* Add additional compiler cases here. */

-#        endif

-#     endif

-#  endif

-#endif /* Setting PNG_BUILD_DLL if required */

-

-/* See pngconf.h for more details: the builder of the library may set this on

- * the command line to the right thing for the specific compilation system or it

- * may be automagically set above (at present we know of no system where it does

- * need to be set on the command line.)

- *

- * PNG_IMPEXP must be set here when building the library to prevent pngconf.h

- * setting it to the "import" setting for a DLL build.

- */

-#ifndef PNG_IMPEXP

-#  ifdef PNG_BUILD_DLL

-#     define PNG_IMPEXP PNG_DLL_EXPORT

-#  else

-      /* Not building a DLL, or the DLL doesn't require specific export

-       * definitions.

-       */

-#     define PNG_IMPEXP

-#  endif

-#endif

-

-/* No warnings for private or deprecated functions in the build: */

-#ifndef PNG_DEPRECATED

-#  define PNG_DEPRECATED

-#endif

-#ifndef PNG_PRIVATE

-#  define PNG_PRIVATE

-#endif

-

-/* Symbol preprocessing support.

- *

- * To enable listing global, but internal, symbols the following macros should

- * always be used to declare an extern data or function object in this file.

- */

-#ifndef PNG_INTERNAL_DATA

-#  define PNG_INTERNAL_DATA(type, name, array) extern type name array

-#endif

-

-#ifndef PNG_INTERNAL_FUNCTION

-#  define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\

-      extern PNG_FUNCTION(type, name, args, PNG_EMPTY attributes)

-#endif

-

-/* If floating or fixed point APIs are disabled they may still be compiled

- * internally.  To handle this make sure they are declared as the appropriate

- * internal extern function (otherwise the symbol prefixing stuff won't work and

- * the functions will be used without definitions.)

- *

- * NOTE: although all the API functions are declared here they are not all

- * actually built!  Because the declarations are still made it is necessary to

- * fake out types that they depend on.

- */

-#ifndef PNG_FP_EXPORT

-#  ifndef PNG_FLOATING_POINT_SUPPORTED

-#     define PNG_FP_EXPORT(ordinal, type, name, args)\

-         PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY);

-#     ifndef PNG_VERSION_INFO_ONLY

-         typedef struct png_incomplete png_double;

-         typedef png_double*           png_doublep;

-         typedef const png_double*     png_const_doublep;

-         typedef png_double**          png_doublepp;

-#     endif

-#  endif

-#endif

-#ifndef PNG_FIXED_EXPORT

-#  ifndef PNG_FIXED_POINT_SUPPORTED

-#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\

-         PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY);

-#  endif

-#endif

-

-#include "png.h"

-

-/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */

-#ifndef PNG_DLL_EXPORT

-#  define PNG_DLL_EXPORT

-#endif

-

-/* SECURITY and SAFETY:

- *

- * By default libpng is built without any internal limits on image size,

- * individual heap (png_malloc) allocations or the total amount of memory used.

- * If PNG_SAFE_LIMITS_SUPPORTED is defined, however, the limits below are used

- * (unless individually overridden).  These limits are believed to be fairly

- * safe, but builders of secure systems should verify the values against the

- * real system capabilities.

- */

-#ifdef PNG_SAFE_LIMITS_SUPPORTED

-   /* 'safe' limits */

-#  ifndef PNG_USER_WIDTH_MAX

-#     define PNG_USER_WIDTH_MAX 1000000

-#  endif

-#  ifndef PNG_USER_HEIGHT_MAX

-#     define PNG_USER_HEIGHT_MAX 1000000

-#  endif

-#  ifndef PNG_USER_CHUNK_CACHE_MAX

-#     define PNG_USER_CHUNK_CACHE_MAX 128

-#  endif

-#  ifndef PNG_USER_CHUNK_MALLOC_MAX

-#     define PNG_USER_CHUNK_MALLOC_MAX 8000000

-#  endif

-#else

-   /* values for no limits */

-#  ifndef PNG_USER_WIDTH_MAX

-#     define PNG_USER_WIDTH_MAX 0x7fffffff

-#  endif

-#  ifndef PNG_USER_HEIGHT_MAX

-#     define PNG_USER_HEIGHT_MAX 0x7fffffff

-#  endif

-#  ifndef PNG_USER_CHUNK_CACHE_MAX

-#     define PNG_USER_CHUNK_CACHE_MAX 0

-#  endif

-#  ifndef PNG_USER_CHUNK_MALLOC_MAX

-#     define PNG_USER_CHUNK_MALLOC_MAX 0

-#  endif

-#endif

-

-/* Moved to pngpriv.h at libpng-1.5.0 */

-/* NOTE: some of these may have been used in external applications as

- * these definitions were exposed in pngconf.h prior to 1.5.

- */

-

-/* If you are running on a machine where you cannot allocate more

- * than 64K of memory at once, uncomment this.  While libpng will not

- * normally need that much memory in a chunk (unless you load up a very

- * large file), zlib needs to know how big of a chunk it can use, and

- * libpng thus makes sure to check any memory allocation to verify it

- * will fit into memory.

- *

- * zlib provides 'MAXSEG_64K' which, if defined, indicates the

- * same limit and pngconf.h (already included) sets the limit

- * if certain operating systems are detected.

- */

-#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)

-#  define PNG_MAX_MALLOC_64K

-#endif

-

-#ifndef PNG_UNUSED

-/* Unused formal parameter warnings are silenced using the following macro

- * which is expected to have no bad effects on performance (optimizing

- * compilers will probably remove it entirely).  Note that if you replace

- * it with something other than whitespace, you must include the terminating

- * semicolon.

- */

-#  define PNG_UNUSED(param) (void)param;

-#endif

-

-/* Just a little check that someone hasn't tried to define something

- * contradictory.

- */

-#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)

-#  undef PNG_ZBUF_SIZE

-#  define PNG_ZBUF_SIZE 65536L

-#endif

-

-/* If warnings or errors are turned off the code is disabled or redirected here.

- * From 1.5.4 functions have been added to allow very limited formatting of

- * error and warning messages - this code will also be disabled here.

- */

-#ifdef PNG_WARNINGS_SUPPORTED

-#  define PNG_WARNING_PARAMETERS(p) png_warning_parameters p;

-#else

-#  define png_warning(s1,s2) ((void)(s1))

-#  define png_chunk_warning(s1,s2) ((void)(s1))

-#  define png_warning_parameter(p,number,string) ((void)0)

-#  define png_warning_parameter_unsigned(p,number,format,value) ((void)0)

-#  define png_warning_parameter_signed(p,number,format,value) ((void)0)

-#  define png_formatted_warning(pp,p,message) ((void)(pp))

-#  define PNG_WARNING_PARAMETERS(p)

-#endif

-#ifndef PNG_ERROR_TEXT_SUPPORTED

-#  define png_error(s1,s2) png_err(s1)

-#  define png_chunk_error(s1,s2) png_err(s1)

-#  define png_fixed_error(s1,s2) png_err(s1)

-#endif

-

-/* C allows up-casts from (void*) to any pointer and (const void*) to any

- * pointer to a const object.  C++ regards this as a type error and requires an

- * explicit, static, cast and provides the static_cast<> rune to ensure that

- * const is not cast away.

- */

-#ifdef __cplusplus

-#  define png_voidcast(type, value) static_cast<type>(value)

-#  define png_constcast(type, value) const_cast<type>(value)

-#  define png_aligncast(type, value) \

-   static_cast<type>(static_cast<void*>(value))

-#  define png_aligncastconst(type, value) \

-   static_cast<type>(static_cast<const void*>(value))

-#else

-#  define png_voidcast(type, value) (value)

-#  define png_constcast(type, value) ((type)(value))

-#  define png_aligncast(type, value) ((void*)(value))

-#  define png_aligncastconst(type, value) ((const void*)(value))

-#endif /* __cplusplus */

-

-/* Some fixed point APIs are still required even if not exported because

- * they get used by the corresponding floating point APIs.  This magic

- * deals with this:

- */

-#ifdef PNG_FIXED_POINT_SUPPORTED

-#  define PNGFAPI PNGAPI

-#else

-#  define PNGFAPI /* PRIVATE */

-#endif

-

-#ifndef PNG_VERSION_INFO_ONLY

-/* Other defines specific to compilers can go here.  Try to keep

- * them inside an appropriate ifdef/endif pair for portability.

- */

-#if defined(PNG_FLOATING_POINT_SUPPORTED) ||\

-    defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)

-   /* png.c requires the following ANSI-C constants if the conversion of

-    * floating point to ASCII is implemented therein:

-    *

-    *  DBL_DIG  Maximum number of decimal digits (can be set to any constant)

-    *  DBL_MIN  Smallest normalized fp number (can be set to an arbitrary value)

-    *  DBL_MAX  Maximum floating point number (can be set to an arbitrary value)

-    */

-#  include <float.h>

-

-#  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \

-    defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)

-     /* We need to check that <math.h> hasn't already been included earlier

-      * as it seems it doesn't agree with <fp.h>, yet we should really use

-      * <fp.h> if possible.

-      */

-#    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)

-#      include <fp.h>

-#    endif

-#  else

-#    include <math.h>

-#  endif

-#  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)

-     /* Amiga SAS/C: We must include builtin FPU functions when compiling using

-      * MATH=68881

-      */

-#    include <m68881.h>

-#  endif

-#endif

-

-/* This provides the non-ANSI (far) memory allocation routines. */

-#if defined(__TURBOC__) && defined(__MSDOS__)

-#  include <mem.h>

-#  include <alloc.h>

-#endif

-

-#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \

-    defined(_WIN32) || defined(__WIN32__)

-#  include <windows.h>  /* defines _WINDOWS_ macro */

-#endif

-#endif /* PNG_VERSION_INFO_ONLY */

-

-/* Moved here around 1.5.0beta36 from pngconf.h */

-/* Users may want to use these so they are not private.  Any library

- * functions that are passed far data must be model-independent.

- */

-

-/* Memory model/platform independent fns */

-#ifndef PNG_ABORT

-#  ifdef _WINDOWS_

-#    define PNG_ABORT() ExitProcess(0)

-#  else

-#    define PNG_ABORT() abort()

-#  endif

-#endif

-

-/* These macros may need to be architecture dependent. */

-#define PNG_ALIGN_NONE   0 /* do not use data alignment */

-#define PNG_ALIGN_ALWAYS 1 /* assume unaligned accesses are OK */

-#ifdef offsetof

-#  define PNG_ALIGN_OFFSET 2 /* use offsetof to determine alignment */

-#else

-#  define PNG_ALIGN_OFFSET -1 /* prevent the use of this */

-#endif

-#define PNG_ALIGN_SIZE   3 /* use sizeof to determine alignment */

-

-#ifndef PNG_ALIGN_TYPE

-   /* Default to using aligned access optimizations and requiring alignment to a

-    * multiple of the data type size.  Override in a compiler specific fashion

-    * if necessary by inserting tests here:

-    */

-#  define PNG_ALIGN_TYPE PNG_ALIGN_SIZE

-#endif

-

-#if PNG_ALIGN_TYPE == PNG_ALIGN_SIZE

-   /* This is used because in some compiler implementations non-aligned

-    * structure members are supported, so the offsetof approach below fails.

-    * Set PNG_ALIGN_SIZE=0 for compiler combinations where unaligned access

-    * is good for performance.  Do not do this unless you have tested the result

-    * and understand it.

-    */

-#  define png_alignof(type) (sizeof (type))

-#else

-#  if PNG_ALIGN_TYPE == PNG_ALIGN_OFFSET

-#     define png_alignof(type) offsetof(struct{char c; type t;}, t)

-#  else

-#     if PNG_ALIGN_TYPE == PNG_ALIGN_ALWAYS

-#        define png_alignof(type) (1)

-#     endif

-      /* Else leave png_alignof undefined to prevent use thereof */

-#  endif

-#endif

-

-/* This implicitly assumes alignment is always to a power of 2. */

-#ifdef png_alignof

-#  define png_isaligned(ptr, type)\

-   ((((const char*)ptr-(const char*)0) & (png_alignof(type)-1)) == 0)

-#else

-#  define png_isaligned(ptr, type) 0

-#endif

-

-/* End of memory model/platform independent support */

-/* End of 1.5.0beta36 move from pngconf.h */

-

-/* CONSTANTS and UTILITY MACROS

- * These are used internally by libpng and not exposed in the API

- */

-

-/* Various modes of operation.  Note that after an init, mode is set to

- * zero automatically when the structure is created.  Three of these

- * are defined in png.h because they need to be visible to applications

- * that call png_set_unknown_chunk().

- */

-/* #define PNG_HAVE_IHDR            0x01 (defined in png.h) */

-/* #define PNG_HAVE_PLTE            0x02 (defined in png.h) */

-#define PNG_HAVE_IDAT               0x04

-/* #define PNG_AFTER_IDAT           0x08 (defined in png.h) */

-#define PNG_HAVE_IEND               0x10

-                   /*               0x20 (unused) */

-                   /*               0x40 (unused) */

-                   /*               0x80 (unused) */

-#define PNG_HAVE_CHUNK_HEADER      0x100

-#define PNG_WROTE_tIME             0x200

-#define PNG_WROTE_INFO_BEFORE_PLTE 0x400

-#define PNG_BACKGROUND_IS_GRAY     0x800

-#define PNG_HAVE_PNG_SIGNATURE    0x1000

-#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */

-                   /*             0x4000 (unused) */

-#define PNG_IS_READ_STRUCT        0x8000 /* Else is a write struct */

-

-/* Flags for the transformations the PNG library does on the image data */

-#define PNG_BGR                 0x0001

-#define PNG_INTERLACE           0x0002

-#define PNG_PACK                0x0004

-#define PNG_SHIFT               0x0008

-#define PNG_SWAP_BYTES          0x0010

-#define PNG_INVERT_MONO         0x0020

-#define PNG_QUANTIZE            0x0040

-#define PNG_COMPOSE             0x0080     /* Was PNG_BACKGROUND */

-#define PNG_BACKGROUND_EXPAND   0x0100

-#define PNG_EXPAND_16           0x0200     /* Added to libpng 1.5.2 */

-#define PNG_16_TO_8             0x0400     /* Becomes 'chop' in 1.5.4 */

-#define PNG_RGBA                0x0800

-#define PNG_EXPAND              0x1000

-#define PNG_GAMMA               0x2000

-#define PNG_GRAY_TO_RGB         0x4000

-#define PNG_FILLER              0x8000

-#define PNG_PACKSWAP           0x10000

-#define PNG_SWAP_ALPHA         0x20000

-#define PNG_STRIP_ALPHA        0x40000

-#define PNG_INVERT_ALPHA       0x80000

-#define PNG_USER_TRANSFORM    0x100000

-#define PNG_RGB_TO_GRAY_ERR   0x200000

-#define PNG_RGB_TO_GRAY_WARN  0x400000

-#define PNG_RGB_TO_GRAY       0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */

-#define PNG_ENCODE_ALPHA      0x800000 /* Added to libpng-1.5.4 */

-#define PNG_ADD_ALPHA         0x1000000 /* Added to libpng-1.2.7 */

-#define PNG_EXPAND_tRNS       0x2000000 /* Added to libpng-1.2.9 */

-#define PNG_SCALE_16_TO_8     0x4000000 /* Added to libpng-1.5.4 */

-                       /*   0x8000000 unused */

-                       /*  0x10000000 unused */

-                       /*  0x20000000 unused */

-                       /*  0x40000000 unused */

-/* Flags for png_create_struct */

-#define PNG_STRUCT_PNG   0x0001

-#define PNG_STRUCT_INFO  0x0002

-

-/* Scaling factor for filter heuristic weighting calculations */

-#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT))

-#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT))

-

-/* Flags for the png_ptr->flags rather than declaring a byte for each one */

-#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001

-#define PNG_FLAG_ZSTREAM_INITIALIZED      0x0002 /* Added to libpng-1.6.0 */

-                                  /*      0x0004    unused */

-#define PNG_FLAG_ZSTREAM_ENDED            0x0008 /* Added to libpng-1.6.0 */

-                                  /*      0x0010    unused */

-                                  /*      0x0020    unused */

-#define PNG_FLAG_ROW_INIT                 0x0040

-#define PNG_FLAG_FILLER_AFTER             0x0080

-#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100

-#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200

-#define PNG_FLAG_CRC_CRITICAL_USE         0x0400

-#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800

-#define PNG_FLAG_ASSUME_sRGB              0x1000 /* Added to libpng-1.5.4 */

-#define PNG_FLAG_OPTIMIZE_ALPHA           0x2000 /* Added to libpng-1.5.4 */

-#define PNG_FLAG_DETECT_UNINITIALIZED     0x4000 /* Added to libpng-1.5.4 */

-/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000 */

-/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS      0x10000 */

-#define PNG_FLAG_LIBRARY_MISMATCH        0x20000

-#define PNG_FLAG_STRIP_ERROR_NUMBERS     0x40000

-#define PNG_FLAG_STRIP_ERROR_TEXT        0x80000

-#define PNG_FLAG_BENIGN_ERRORS_WARN     0x100000 /* Added to libpng-1.4.0 */

-#define PNG_FLAG_APP_WARNINGS_WARN      0x200000 /* Added to libpng-1.6.0 */

-#define PNG_FLAG_APP_ERRORS_WARN        0x400000 /* Added to libpng-1.6.0 */

-                                  /*    0x800000    unused */

-                                  /*   0x1000000    unused */

-                                  /*   0x2000000    unused */

-                                  /*   0x4000000    unused */

-                                  /*   0x8000000    unused */

-                                  /*  0x10000000    unused */

-                                  /*  0x20000000    unused */

-                                  /*  0x40000000    unused */

-

-#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \

-                                     PNG_FLAG_CRC_ANCILLARY_NOWARN)

-

-#define PNG_FLAG_CRC_CRITICAL_MASK  (PNG_FLAG_CRC_CRITICAL_USE | \

-                                     PNG_FLAG_CRC_CRITICAL_IGNORE)

-

-#define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \

-                                     PNG_FLAG_CRC_CRITICAL_MASK)

-

-/* Save typing and make code easier to understand */

-

-#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \

-   abs((int)((c1).green) - (int)((c2).green)) + \

-   abs((int)((c1).blue) - (int)((c2).blue)))

-

-/* Added to libpng-1.6.0: scale a 16-bit value in the range 0..65535 to 0..255

- * by dividing by 257 *with rounding*.  This macro is exact for the given range.

- * See the discourse in pngrtran.c png_do_scale_16_to_8.  The values in the

- * macro were established by experiment (modifying the added value).  The macro

- * has a second variant that takes a value already scaled by 255 and divides by

- * 65535 - this has a maximum error of .502.  Over the range 0..65535*65535 it

- * only gives off-by-one errors and only for 0.5% (1 in 200) of the values.

- */

-#define PNG_DIV65535(v24) (((v24) + 32895) >> 16)

-#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255)

-

-/* Added to libpng-1.2.6 JB */

-#define PNG_ROWBYTES(pixel_bits, width) \

-    ((pixel_bits) >= 8 ? \

-    ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \

-    (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) )

-

-/* PNG_OUT_OF_RANGE returns true if value is outside the range

- * ideal-delta..ideal+delta.  Each argument is evaluated twice.

- * "ideal" and "delta" should be constants, normally simple

- * integers, "value" a variable. Added to libpng-1.2.6 JB

- */

-#define PNG_OUT_OF_RANGE(value, ideal, delta) \

-   ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )

-

-/* Conversions between fixed and floating point, only defined if

- * required (to make sure the code doesn't accidentally use float

- * when it is supposedly disabled.)

- */

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-/* The floating point conversion can't overflow, though it can and

- * does lose accuracy relative to the original fixed point value.

- * In practice this doesn't matter because png_fixed_point only

- * stores numbers with very low precision.  The png_ptr and s

- * arguments are unused by default but are there in case error

- * checking becomes a requirement.

- */

-#define png_float(png_ptr, fixed, s) (.00001 * (fixed))

-

-/* The fixed point conversion performs range checking and evaluates

- * its argument multiple times, so must be used with care.  The

- * range checking uses the PNG specification values for a signed

- * 32 bit fixed point value except that the values are deliberately

- * rounded-to-zero to an integral value - 21474 (21474.83 is roughly

- * (2^31-1) * 100000). 's' is a string that describes the value being

- * converted.

- *

- * NOTE: this macro will raise a png_error if the range check fails,

- * therefore it is normally only appropriate to use this on values

- * that come from API calls or other sources where an out of range

- * error indicates a programming error, not a data error!

- *

- * NOTE: by default this is off - the macro is not used - because the

- * function call saves a lot of code.

- */

-#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED

-#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\

-    ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0))

-#endif

-/* else the corresponding function is defined below, inside the scope of the

- * cplusplus test.

- */

-#endif

-

-/* Constants for known chunk types.  If you need to add a chunk, define the name

- * here.  For historical reasons these constants have the form png_<name>; i.e.

- * the prefix is lower case.  Please use decimal values as the parameters to

- * match the ISO PNG specification and to avoid relying on the C locale

- * interpretation of character values.

- *

- * Prior to 1.5.6 these constants were strings, as of 1.5.6 png_uint_32 values

- * are computed and a new macro (PNG_STRING_FROM_CHUNK) added to allow a string

- * to be generated if required.

- *

- * PNG_32b correctly produces a value shifted by up to 24 bits, even on

- * architectures where (int) is only 16 bits.

- */

-#define PNG_32b(b,s) ((png_uint_32)(b) << (s))

-#define PNG_U32(b1,b2,b3,b4) \

-   (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))

-

-/* Constants for known chunk types.

- *

- * MAINTAINERS: If you need to add a chunk, define the name here.

- * For historical reasons these constants have the form png_<name>; i.e.

- * the prefix is lower case.  Please use decimal values as the parameters to

- * match the ISO PNG specification and to avoid relying on the C locale

- * interpretation of character values.  Please keep the list sorted.

- *

- * Notice that PNG_U32 is used to define a 32-bit value for the 4 byte chunk

- * type.  In fact the specification does not express chunk types this way,

- * however using a 32-bit value means that the chunk type can be read from the

- * stream using exactly the same code as used for a 32-bit unsigned value and

- * can be examined far more efficiently (using one arithmetic compare).

- *

- * Prior to 1.5.6 the chunk type constants were expressed as C strings.  The

- * libpng API still uses strings for 'unknown' chunks and a macro,

- * PNG_STRING_FROM_CHUNK, allows a string to be generated if required.  Notice

- * that for portable code numeric values must still be used; the string "IHDR"

- * is not portable and neither is PNG_U32('I', 'H', 'D', 'R').

- *

- * In 1.7.0 the definitions will be made public in png.h to avoid having to

- * duplicate the same definitions in application code.

- */

-#define png_IDAT PNG_U32( 73,  68,  65,  84)

-#define png_IEND PNG_U32( 73,  69,  78,  68)

-#define png_IHDR PNG_U32( 73,  72,  68,  82)

-#define png_PLTE PNG_U32( 80,  76,  84,  69)

-#define png_bKGD PNG_U32( 98,  75,  71,  68)

-#define png_cHRM PNG_U32( 99,  72,  82,  77)

-#define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */

-#define png_gAMA PNG_U32(103,  65,  77,  65)

-#define png_gIFg PNG_U32(103,  73,  70, 103)

-#define png_gIFt PNG_U32(103,  73,  70, 116) /* deprecated */

-#define png_gIFx PNG_U32(103,  73,  70, 120)

-#define png_hIST PNG_U32(104,  73,  83,  84)

-#define png_iCCP PNG_U32(105,  67,  67,  80)

-#define png_iTXt PNG_U32(105,  84,  88, 116)

-#define png_oFFs PNG_U32(111,  70,  70, 115)

-#define png_pCAL PNG_U32(112,  67,  65,  76)

-#define png_pHYs PNG_U32(112,  72,  89, 115)

-#define png_sBIT PNG_U32(115,  66,  73,  84)

-#define png_sCAL PNG_U32(115,  67,  65,  76)

-#define png_sPLT PNG_U32(115,  80,  76,  84)

-#define png_sRGB PNG_U32(115,  82,  71,  66)

-#define png_sTER PNG_U32(115,  84,  69,  82)

-#define png_tEXt PNG_U32(116,  69,  88, 116)

-#define png_tIME PNG_U32(116,  73,  77,  69)

-#define png_tRNS PNG_U32(116,  82,  78,  83)

-#define png_zTXt PNG_U32(122,  84,  88, 116)

-

-/* The following will work on (signed char*) strings, whereas the get_uint_32

- * macro will fail on top-bit-set values because of the sign extension.

- */

-#define PNG_CHUNK_FROM_STRING(s)\

-   PNG_U32(0xff&(s)[0], 0xff&(s)[1], 0xff&(s)[2], 0xff&(s)[3])

-

-/* This uses (char), not (png_byte) to avoid warnings on systems where (char) is

- * signed and the argument is a (char[])  This macro will fail miserably on

- * systems where (char) is more than 8 bits.

- */

-#define PNG_STRING_FROM_CHUNK(s,c)\

-   (void)(((char*)(s))[0]=(char)((c)>>24), ((char*)(s))[1]=(char)((c)>>16),\

-   ((char*)(s))[2]=(char)((c)>>8), ((char*)(s))[3]=(char)((c)))

-

-/* Do the same but terminate with a null character. */

-#define PNG_CSTRING_FROM_CHUNK(s,c)\

-   (void)(PNG_STRING_FROM_CHUNK(s,c), ((char*)(s))[4] = 0)

-

-/* Test on flag values as defined in the spec (section 5.4): */

-#define PNG_CHUNK_ANCILLARY(c)   (1 & ((c) >> 29))

-#define PNG_CHUNK_CRITICAL(c)     (!PNG_CHUNK_ANCILLARY(c))

-#define PNG_CHUNK_PRIVATE(c)      (1 & ((c) >> 21))

-#define PNG_CHUNK_RESERVED(c)     (1 & ((c) >> 13))

-#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >>  5))

-

-/* Gamma values (new at libpng-1.5.4): */

-#define PNG_GAMMA_MAC_OLD 151724  /* Assume '1.8' is really 2.2/1.45! */

-#define PNG_GAMMA_MAC_INVERSE 65909

-#define PNG_GAMMA_sRGB_INVERSE 45455

-

-/* Almost everything below is C specific; the #defines above can be used in

- * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot.

- */

-#ifndef PNG_VERSION_INFO_ONLY

-

-#include "pngstruct.h"

-#include "pnginfo.h"

-

-/* Validate the include paths - the include path used to generate pnglibconf.h

- * must match that used in the build, or we must be using pnglibconf.h.prebuilt:

- */

-#if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM

-#  error ZLIB_VERNUM != PNG_ZLIB_VERNUM \

-      "-I (include path) error: see the notes in pngpriv.h"

-   /* This means that when pnglibconf.h was built the copy of zlib.h that it

-    * used is not the same as the one being used here.  Because the build of

-    * libpng makes decisions to use inflateInit2 and inflateReset2 based on the

-    * zlib version number and because this affects handling of certain broken

-    * PNG files the -I directives must match.

-    *

-    * The most likely explanation is that you passed a -I in CFLAGS, this will

-    * not work; all the preprocessor directories and in particular all the -I

-    * directives must be in CPPFLAGS.

-    */

-#endif

-

-/* This is used for 16 bit gamma tables -- only the top level pointers are

- * const; this could be changed:

- */

-typedef const png_uint_16p * png_const_uint_16pp;

-

-/* Added to libpng-1.5.7: sRGB conversion tables */

-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\

-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)

-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED

-PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_table, [256]);

-   /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value,

-    * 0..65535.  This table gives the closest 16-bit answers (no errors).

-    */

-#endif

-

-PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_base, [512]);

-PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]);

-

-#define PNG_sRGB_FROM_LINEAR(linear) ((png_byte)((png_sRGB_base[(linear)>>15] +\

-   ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8))

-   /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB

-    * encoded value with maximum error 0.646365.  Note that the input is not a

-    * 16-bit value; it has been multiplied by 255! */

-#endif /* PNG_SIMPLIFIED_READ/WRITE */

-

-

-/* Inhibit C++ name-mangling for libpng functions but not for system calls. */

-#ifdef __cplusplus

-extern "C" {

-#endif /* __cplusplus */

-

-/* Internal functions; these are not exported from a DLL however because they

- * are used within several of the C source files they have to be C extern.

- *

- * All of these functions must be declared with PNG_INTERNAL_FUNCTION.

- */

-

-/* Zlib support */

-#define PNG_UNEXPECTED_ZLIB_RETURN (-7)

-PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),

-   PNG_EMPTY);

-   /* Used by the zlib handling functions to ensure that z_stream::msg is always

-    * set before they return.

-    */

-

-#ifdef PNG_WRITE_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,

-   png_compression_bufferp *list),PNG_EMPTY);

-   /* Free the buffer list used by the compressed write code. */

-#endif

-

-#if defined(PNG_FLOATING_POINT_SUPPORTED) && \

-   !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \

-   (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \

-   defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \

-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \

-   (defined(PNG_sCAL_SUPPORTED) && \

-   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))

-PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr,

-   double fp, png_const_charp text),PNG_EMPTY);

-#endif

-

-/* Check the user version string for compatibility, returns false if the version

- * numbers aren't compatible.

- */

-PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,

-   png_const_charp user_png_ver),PNG_EMPTY);

-

-/* Internal base allocator - no messages, NULL on failure to allocate.  This

- * does, however, call the application provided allocator and that could call

- * png_error (although that would be a bug in the application implementation.)

- */

-PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr,

-   png_alloc_size_t size),PNG_ALLOCATED);

-

-#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\

-   defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)

-/* Internal array allocator, outputs no error or warning messages on failure,

- * just returns NULL.  

- */

-PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr,

-   int nelements, size_t element_size),PNG_ALLOCATED);

-

-/* The same but an existing array is extended by add_elements.  This function

- * also memsets the new elements to 0 and copies the old elements.  The old

- * array is not freed or altered.

- */

-PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,

-   png_const_voidp array, int old_elements, int add_elements,

-   size_t element_size),PNG_ALLOCATED);

-#endif /* text, sPLT or unknown chunks */

-

-/* Magic to create a struct when there is no struct to call the user supplied

- * memory allocators.  Because error handling has not been set up the memory

- * handlers can't safely call png_error, but this is an obscure and undocumented

- * restriction so libpng has to assume that the 'free' handler, at least, might

- * call png_error.

- */

-PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct,

-   (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,

-    png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn,

-    png_free_ptr free_fn),PNG_ALLOCATED);

-

-/* Free memory from internal libpng struct */

-PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr),

-   PNG_EMPTY);

-

-/* Free an allocated jmp_buf (always succeeds) */

-PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY);

-

-/* Function to allocate memory for zlib.  PNGAPI is disallowed. */

-PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size),

-   PNG_ALLOCATED);

-

-/* Function to free memory for zlib.  PNGAPI is disallowed. */

-PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY);

-

-/* Next four functions are used internally as callbacks.  PNGCBAPI is required

- * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3, changed to

- * PNGCBAPI at 1.5.0

- */

-

-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,

-    png_bytep data, png_size_t length),PNG_EMPTY);

-

-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED

-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,

-    png_bytep buffer, png_size_t length),PNG_EMPTY);

-#endif

-

-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,

-    png_bytep data, png_size_t length),PNG_EMPTY);

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-#  ifdef PNG_STDIO_SUPPORTED

-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr),

-   PNG_EMPTY);

-#  endif

-#endif

-

-/* Reset the CRC variable */

-PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY);

-

-/* Write the "data" buffer to whatever output you are using */

-PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,

-    png_const_bytep data, png_size_t length),PNG_EMPTY);

-

-/* Read and check the PNG file signature */

-PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,

-   png_inforp info_ptr),PNG_EMPTY);

-

-/* Read the chunk header (length + type name) */

-PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr),

-   PNG_EMPTY);

-

-/* Read data from whatever input you are using into the "data" buffer */

-PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,

-    png_size_t length),PNG_EMPTY);

-

-/* Read bytes into buf, and update png_ptr->crc */

-PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,

-    png_uint_32 length),PNG_EMPTY);

-

-/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */

-PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,

-   png_uint_32 skip),PNG_EMPTY);

-

-/* Read the CRC from the file and compare it to the libpng calculated CRC */

-PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY);

-

-/* Calculate the CRC over a section of data.  Note that we are only

- * passing a maximum of 64K on systems that have this as a memory limit,

- * since this is the maximum buffer size we can specify.

- */

-PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,

-   png_const_bytep ptr, png_size_t length),PNG_EMPTY);

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);

-#endif

-

-/* Write various chunks */

-

-/* Write the IHDR chunk, and update the png_struct with the necessary

- * information.

- */

-PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,

-   png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,

-   int compression_method, int filter_method, int interlace_method),PNG_EMPTY);

-

-PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,

-   png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);

-

-PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,

-   png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),

-   PNG_EMPTY);

-

-PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);

-

-#ifdef PNG_WRITE_gAMA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr,

-    png_fixed_point file_gamma),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_sBIT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr,

-    png_const_color_8p sbit, int color_type),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_cHRM_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,

-    const png_xy *xy), PNG_EMPTY);

-    /* The xy value must have been previously validated */

-#endif

-

-#ifdef PNG_WRITE_sRGB_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr,

-    int intent),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_iCCP_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,

-   png_const_charp name, png_const_bytep profile), PNG_EMPTY);

-   /* The profile must have been previously validated for correctness, the

-    * length comes from the first four bytes.  Only the base, deflate,

-    * compression is supported.

-    */

-#endif

-

-#ifdef PNG_WRITE_sPLT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr,

-    png_const_sPLT_tp palette),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_tRNS_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr,

-    png_const_bytep trans, png_const_color_16p values, int number,

-    int color_type),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_bKGD_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr,

-    png_const_color_16p values, int color_type),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_hIST_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,

-    png_const_uint_16p hist, int num_hist),PNG_EMPTY);

-#endif

-

-/* Chunks that have keywords */

-#ifdef PNG_WRITE_tEXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,

-   png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_zTXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp

-    key, png_const_charp text, png_size_t text_len, int compression),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_iTXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr,

-    int compression, png_const_charp key, png_const_charp lang,

-    png_const_charp lang_key, png_const_charp text),PNG_EMPTY);

-#endif

-

-#ifdef PNG_TEXT_SUPPORTED  /* Added at version 1.0.14 and 1.2.4 */

-PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr,

-    png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_oFFs_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr,

-    png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_pCAL_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr,

-    png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,

-    png_const_charp units, png_charpp params),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_pHYs_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr,

-    png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,

-    int unit_type),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_tIME_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr,

-    png_const_timep mod_time),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_sCAL_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,

-    int unit, png_const_charp width, png_const_charp height),PNG_EMPTY);

-#endif

-

-/* Called when finished processing a row of data */

-PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr),

-    PNG_EMPTY);

-

-/* Internal use only.   Called before first row of data */

-PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),

-    PNG_EMPTY);

-

-/* Combine a row of data, dealing with alpha, etc. if requested.  'row' is an

- * array of png_ptr->width pixels.  If the image is not interlaced or this

- * is the final pass this just does a memcpy, otherwise the "display" flag

- * is used to determine whether to copy pixels that are not in the current pass.

- *

- * Because 'png_do_read_interlace' (below) replicates pixels this allows this

- * function to achieve the documented 'blocky' appearance during interlaced read

- * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row'

- * are not changed if they are not in the current pass, when display is 0.

- *

- * 'display' must be 0 or 1, otherwise the memcpy will be done regardless.

- *

- * The API always reads from the png_struct row buffer and always assumes that

- * it is full width (png_do_read_interlace has already been called.)

- *

- * This function is only ever used to write to row buffers provided by the

- * caller of the relevant libpng API and the row must have already been

- * transformed by the read transformations.

- *

- * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed

- * bitmasks for use within the code, otherwise runtime generated masks are used.

- * The default is compile time masks.

- */

-#ifndef PNG_USE_COMPILE_TIME_MASKS

-#  define PNG_USE_COMPILE_TIME_MASKS 1

-#endif

-PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,

-    png_bytep row, int display),PNG_EMPTY);

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-/* Expand an interlaced row: the 'row_info' describes the pass data that has

- * been read in and must correspond to the pixels in 'row', the pixels are

- * expanded (moved apart) in 'row' to match the final layout, when doing this

- * the pixels are *replicated* to the intervening space.  This is essential for

- * the correct operation of png_combine_row, above.

- */

-PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info,

-    png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY);

-#endif

-

-/* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-/* Grab pixels out of a row for an interlaced pass */

-PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info,

-    png_bytep row, int pass),PNG_EMPTY);

-#endif

-

-/* Unfilter a row: check the filter value before calling this, there is no point

- * calling it for PNG_FILTER_VALUE_NONE.

- */

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);

-

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,

-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop

-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);

-

-/* Choose the best filter to use and filter the row data */

-PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,

-    png_row_infop row_info),PNG_EMPTY);

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,

-   png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);

-   /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer

-    * is NULL the function checks, instead, for the end of the stream.  In this

-    * case a benign error will be issued if the stream end is not found or if

-    * extra data has to be consumed.

-    */

-PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),

-   PNG_EMPTY);

-   /* This cleans up when the IDAT LZ stream does not end when the last image

-    * byte is read; there is still some pending input.

-    */

-

-PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),

-   PNG_EMPTY);

-   /* Finish a row while reading, dealing with interlacing passes, etc. */

-#endif

-

-/* Initialize the row buffers, etc. */

-PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-/* Optional call to update the users info structure */

-PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-#endif

-

-/* These are the functions that do the transformations */

-#ifdef PNG_READ_FILLER_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_read_filler,(png_row_infop row_info,

-    png_bytep row, png_uint_32 filler, png_uint_32 flags),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_read_swap_alpha,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_write_swap_alpha,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_read_invert_alpha,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_write_invert_alpha,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#if defined(PNG_WRITE_FILLER_SUPPORTED) || \

-    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info,

-    png_bytep row, int at_start),PNG_EMPTY);

-#endif

-

-#ifdef PNG_16BIT_SUPPORTED

-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-#endif

-

-#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \

-    defined(PNG_WRITE_PACKSWAP_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-PNG_INTERNAL_FUNCTION(int,png_do_rgb_to_gray,(png_structrp png_ptr,

-    png_row_infop row_info, png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_gray_to_rgb,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_PACK_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_unpack,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_SHIFT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_unshift,(png_row_infop row_info,

-    png_bytep row, png_const_color_8p sig_bits),PNG_EMPTY);

-#endif

-

-#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_scale_16_to_8,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_chop,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_quantize,(png_row_infop row_info,

-    png_bytep row, png_const_bytep palette_lookup,

-    png_const_bytep quantize_lookup),PNG_EMPTY);

-

-#  ifdef PNG_CORRECT_PALETTE_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_correct_palette,(png_structrp png_ptr,

-    png_colorp palette, int num_palette),PNG_EMPTY);

-#  endif

-#endif

-

-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_PACK_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_pack,(png_row_infop row_info,

-   png_bytep row, png_uint_32 bit_depth),PNG_EMPTY);

-#endif

-

-#ifdef PNG_WRITE_SHIFT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_shift,(png_row_infop row_info,

-    png_bytep row, png_const_color_8p bit_depth),PNG_EMPTY);

-#endif

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\

-    defined(PNG_READ_ALPHA_MODE_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_compose,(png_row_infop row_info,

-    png_bytep row, png_structrp png_ptr),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_gamma,(png_row_infop row_info,

-    png_bytep row, png_structrp png_ptr),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_encode_alpha,(png_row_infop row_info,

-   png_bytep row, png_structrp png_ptr),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_expand_palette,(png_row_infop row_info,

-    png_bytep row, png_const_colorp palette, png_const_bytep trans,

-    int num_trans),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_do_expand,(png_row_infop row_info,

-    png_bytep row, png_const_color_16p trans_color),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_expand_16,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-/* The following decodes the appropriate chunks, and does error correction,

- * then calls the appropriate callback for the chunk if it is valid.

- */

-

-/* Decode the IHDR chunk */

-PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-

-#ifdef PNG_READ_bKGD_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_cHRM_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_gAMA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_hIST_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_iCCP_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif /* PNG_READ_iCCP_SUPPORTED */

-

-#ifdef PNG_READ_iTXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_oFFs_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_pCAL_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_pHYs_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_sBIT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_sCAL_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_sPLT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif /* PNG_READ_sPLT_SUPPORTED */

-

-#ifdef PNG_READ_sRGB_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_tEXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_tIME_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_tRNS_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_zTXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-#endif

-

-PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr,

-    png_uint_32 chunk_name),PNG_EMPTY);

-

-#ifdef PNG_READ_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY);

-   /* This is the function that gets called for unknown chunks.  The 'keep'

-    * argument is either non-zero for a known chunk that has been set to be

-    * handled as unknown or zero for an unknown chunk.  By default the function

-    * just skips the chunk or errors out if it is critical.

-    */

-

-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED

-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,

-    (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY);

-   /* Exactly as the API png_handle_as_unknown() except that the argument is a

-    * 32-bit chunk name, not a string.

-    */

-#endif

-#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */

-#endif /* PNG_READ_SUPPORTED */

-

-/* Handle the transformations for reading and writing */

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr,

-   png_row_infop row_info),PNG_EMPTY);

-#endif

-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr,

-   png_row_infop row_info),PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr),

-    PNG_EMPTY);

-#endif

-

-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_crc_skip,(png_structrp png_ptr,

-    png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_crc_finish,(png_structrp png_ptr),

-    PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),

-    PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,

-    png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,

-    png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),

-    PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr,

-   png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,

-   png_inforp info_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,

-   png_inforp info_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,

-     png_bytep row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),

-    PNG_EMPTY);

-#  ifdef PNG_READ_tEXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-#  endif

-#  ifdef PNG_READ_zTXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-#  endif

-#  ifdef PNG_READ_iTXt_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr,

-    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr,

-    png_inforp info_ptr),PNG_EMPTY);

-#  endif

-

-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_do_read_intrapixel,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_do_write_intrapixel,(png_row_infop row_info,

-    png_bytep row),PNG_EMPTY);

-#endif

-

-/* Added at libpng version 1.6.0 */

-#ifdef PNG_GAMMA_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr,

-    png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY);

-   /* Set the colorspace gamma with a value provided by the application or by

-    * the gAMA chunk on read.  The value will override anything set by an ICC

-    * profile.

-    */

-

-PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr,

-    png_inforp info_ptr), PNG_EMPTY);

-    /* Synchronize the info 'valid' flags with the colorspace */

-

-PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr,

-    png_inforp info_ptr), PNG_EMPTY);

-    /* Copy the png_struct colorspace to the info_struct and call the above to

-     * synchronize the flags.  Checks for NULL info_ptr and does nothing.

-     */

-#endif

-

-/* Added at libpng version 1.4.0 */

-#ifdef PNG_COLORSPACE_SUPPORTED

-/* These internal functions are for maintaining the colorspace structure within

- * a png_info or png_struct (or, indeed, both).

- */

-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities,

-   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy,

-    int preferred), PNG_EMPTY);

-

-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints,

-   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ,

-    int preferred), PNG_EMPTY);

-

-#ifdef PNG_sRGB_SUPPORTED

-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, int intent), PNG_EMPTY);

-   /* This does set the colorspace gAMA and cHRM values too, but doesn't set the

-    * flags to write them, if it returns false there was a problem and an error

-    * message has already been output (but the colorspace may still need to be

-    * synced to record the invalid flag).

-    */

-#endif /* sRGB */

-

-#ifdef PNG_iCCP_SUPPORTED

-PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_const_charp name,

-   png_uint_32 profile_length, png_const_bytep profile, int color_type),

-   PNG_EMPTY);

-   /* The 'name' is used for information only */

-

-/* Routines for checking parts of an ICC profile. */

-PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_const_charp name,

-   png_uint_32 profile_length), PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_const_charp name,

-   png_uint_32 profile_length,

-   png_const_bytep profile /* first 132 bytes only */, int color_type),

-   PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,

-   png_colorspacerp colorspace, png_const_charp name,

-   png_uint_32 profile_length,

-   png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY);

-#ifdef PNG_sRGB_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,(

-   png_const_structrp png_ptr, png_colorspacerp colorspace,

-   png_const_bytep profile, uLong adler), PNG_EMPTY);

-   /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may

-    * be zero to indicate that it is not available.  It is used, if provided,

-    * as a fast check on the profile when checking to see if it is sRGB.

-    */

-#endif

-#endif /* iCCP */

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients,

-   (png_structrp png_ptr), PNG_EMPTY);

-   /* Set the rgb_to_gray coefficients from the colorspace Y values */

-#endif /* READ_RGB_TO_GRAY */

-#endif /* COLORSPACE */

-

-/* Added at libpng version 1.4.0 */

-PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,

-    png_uint_32 width, png_uint_32 height, int bit_depth,

-    int color_type, int interlace_type, int compression_type,

-    int filter_type),PNG_EMPTY);

-

-/* Added at libpng version 1.5.10 */

-#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \

-    defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,

-   (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);

-#endif

-

-#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)

-PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr,

-   png_const_charp name),PNG_NORETURN);

-#endif

-

-/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite

- * the end.  Always leaves the buffer nul terminated.  Never errors out (and

- * there is no error code.)

- */

-PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,

-   size_t pos, png_const_charp string),PNG_EMPTY);

-

-/* Various internal functions to handle formatted warning messages, currently

- * only implemented for warnings.

- */

-#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)

-/* Utility to dump an unsigned value into a buffer, given a start pointer and

- * and end pointer (which should point just *beyond* the end of the buffer!)

- * Returns the pointer to the start of the formatted string.  This utility only

- * does unsigned values.

- */

-PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,

-   png_charp end, int format, png_alloc_size_t number),PNG_EMPTY);

-

-/* Convenience macro that takes an array: */

-#define PNG_FORMAT_NUMBER(buffer,format,number) \

-   png_format_number(buffer, buffer + (sizeof buffer), format, number)

-

-/* Suggested size for a number buffer (enough for 64 bits and a sign!) */

-#define PNG_NUMBER_BUFFER_SIZE 24

-

-/* These are the integer formats currently supported, the name is formed from

- * the standard printf(3) format string.

- */

-#define PNG_NUMBER_FORMAT_u     1 /* chose unsigned API! */

-#define PNG_NUMBER_FORMAT_02u   2

-#define PNG_NUMBER_FORMAT_d     1 /* chose signed API! */

-#define PNG_NUMBER_FORMAT_02d   2

-#define PNG_NUMBER_FORMAT_x     3

-#define PNG_NUMBER_FORMAT_02x   4

-#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */

-#endif

-

-#ifdef PNG_WARNINGS_SUPPORTED

-/* New defines and members adding in libpng-1.5.4 */

-#  define PNG_WARNING_PARAMETER_SIZE 32

-#  define PNG_WARNING_PARAMETER_COUNT 8 /* Maximum 9; see pngerror.c */

-

-/* An l-value of this type has to be passed to the APIs below to cache the

- * values of the parameters to a formatted warning message.

- */

-typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][

-   PNG_WARNING_PARAMETER_SIZE];

-

-PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p,

-   int number, png_const_charp string),PNG_EMPTY);

-   /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,

-    * including the trailing '\0'.

-    */

-PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned,

-   (png_warning_parameters p, int number, int format, png_alloc_size_t value),

-   PNG_EMPTY);

-   /* Use png_alloc_size_t because it is an unsigned type as big as any we

-    * need to output.  Use the following for a signed value.

-    */

-PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed,

-   (png_warning_parameters p, int number, int format, png_int_32 value),

-   PNG_EMPTY);

-

-PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,

-   png_warning_parameters p, png_const_charp message),PNG_EMPTY);

-   /* 'message' follows the X/Open approach of using @1, @2 to insert

-    * parameters previously supplied using the above functions.  Errors in

-    * specifying the parameters will simply result in garbage substitutions.

-    */

-#endif

-

-#ifdef PNG_BENIGN_ERRORS_SUPPORTED

-/* Application errors (new in 1.6); use these functions (declared below) for

- * errors in the parameters or order of API function calls on read.  The

- * 'warning' should be used for an error that can be handled completely; the

- * 'error' for one which can be handled safely but which may lose application

- * information or settings.

- *

- * By default these both result in a png_error call prior to release, while in a

- * released version the 'warning' is just a warning.  However if the application

- * explicitly disables benign errors (explicitly permitting the code to lose

- * information) they both turn into warnings.

- *

- * If benign errors aren't supported they end up as the corresponding base call

- * (png_warning or png_error.)

- */

-PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr,

-   png_const_charp message),PNG_EMPTY);

-   /* The application provided invalid parameters to an API function or called

-    * an API function at the wrong time, libpng can completely recover.

-    */

-

-PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,

-   png_const_charp message),PNG_EMPTY);

-   /* As above but libpng will ignore the call, or attempt some other partial

-    * recovery from the error.

-    */

-#else

-#  define png_app_warning(pp,s) png_warning(pp,s)

-#  define png_app_error(pp,s) png_error(pp,s)

-#endif

-

-PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,

-   png_const_charp message, int error),PNG_EMPTY);

-   /* Report a recoverable issue in chunk data.  On read this is used to report

-    * a problem found while reading a particular chunk and the

-    * png_chunk_benign_error or png_chunk_warning function is used as

-    * appropriate.  On write this is used to report an error that comes from

-    * data set via an application call to a png_set_ API and png_app_error or

-    * png_app_warning is used as appropriate.

-    *

-    * The 'error' parameter must have one of the following values:

-    */

-#define PNG_CHUNK_WARNING     0 /* never an error */

-#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */

-#define PNG_CHUNK_ERROR       2 /* always an error */

-

-/* ASCII to FP interfaces, currently only implemented if sCAL

- * support is required.

- */

-#if defined(PNG_sCAL_SUPPORTED)

-/* MAX_DIGITS is actually the maximum number of characters in an sCAL

- * width or height, derived from the precision (number of significant

- * digits - a build time settable option) and assumptions about the

- * maximum ridiculous exponent.

- */

-#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/)

-

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr,

-   png_charp ascii, png_size_t size, double fp, unsigned int precision),

-   PNG_EMPTY);

-#endif /* FLOATING_POINT */

-

-#ifdef PNG_FIXED_POINT_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,

-   png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY);

-#endif /* FIXED_POINT */

-#endif /* sCAL */

-

-#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)

-/* An internal API to validate the format of a floating point number.

- * The result is the index of the next character.  If the number is

- * not valid it will be the index of a character in the supposed number.

- *

- * The format of a number is defined in the PNG extensions specification

- * and this API is strictly conformant to that spec, not anyone elses!

- *

- * The format as a regular expression is:

- *

- * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)?

- *

- * or:

- *

- * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)?

- *

- * The complexity is that either integer or fraction must be present and the

- * fraction is permitted to have no digits only if the integer is present.

- *

- * NOTE: The dangling E problem.

- *   There is a PNG valid floating point number in the following:

- *

- *       PNG floating point numbers are not greedy.

- *

- *   Working this out requires *TWO* character lookahead (because of the

- *   sign), the parser does not do this - it will fail at the 'r' - this

- *   doesn't matter for PNG sCAL chunk values, but it requires more care

- *   if the value were ever to be embedded in something more complex.  Use

- *   ANSI-C strtod if you need the lookahead.

- */

-/* State table for the parser. */

-#define PNG_FP_INTEGER    0  /* before or in integer */

-#define PNG_FP_FRACTION   1  /* before or in fraction */

-#define PNG_FP_EXPONENT   2  /* before or in exponent */

-#define PNG_FP_STATE      3  /* mask for the above */

-#define PNG_FP_SAW_SIGN   4  /* Saw +/- in current state */

-#define PNG_FP_SAW_DIGIT  8  /* Saw a digit in current state */

-#define PNG_FP_SAW_DOT   16  /* Saw a dot in current state */

-#define PNG_FP_SAW_E     32  /* Saw an E (or e) in current state */

-#define PNG_FP_SAW_ANY   60  /* Saw any of the above 4 */

-

-/* These three values don't affect the parser.  They are set but not used.

- */

-#define PNG_FP_WAS_VALID 64  /* Preceding substring is a valid fp number */

-#define PNG_FP_NEGATIVE 128  /* A negative number, including "-0" */

-#define PNG_FP_NONZERO  256  /* A non-zero value */

-#define PNG_FP_STICKY   448  /* The above three flags */

-

-/* This is available for the caller to store in 'state' if required.  Do not

- * call the parser after setting it (the parser sometimes clears it.)

- */

-#define PNG_FP_INVALID  512  /* Available for callers as a distinct value */

-

-/* Result codes for the parser (boolean - true meants ok, false means

- * not ok yet.)

- */

-#define PNG_FP_MAYBE      0  /* The number may be valid in the future */

-#define PNG_FP_OK         1  /* The number is valid */

-

-/* Tests on the sticky non-zero and negative flags.  To pass these checks

- * the state must also indicate that the whole number is valid - this is

- * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this

- * is equivalent to PNG_FP_OK above.)

- */

-#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO)

-   /* NZ_MASK: the string is valid and a non-zero negative value */

-#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO)

-   /* Z MASK: the string is valid and a non-zero value. */

-   /* PNG_FP_SAW_DIGIT: the string is valid. */

-#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT)

-#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK)

-#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK)

-

-/* The actual parser.  This can be called repeatedly. It updates

- * the index into the string and the state variable (which must

- * be initialized to 0).  It returns a result code, as above.  There

- * is no point calling the parser any more if it fails to advance to

- * the end of the string - it is stuck on an invalid character (or

- * terminated by '\0').

- *

- * Note that the pointer will consume an E or even an E+ and then leave

- * a 'maybe' state even though a preceding integer.fraction is valid.

- * The PNG_FP_WAS_VALID flag indicates that a preceding substring was

- * a valid number.  It's possible to recover from this by calling

- * the parser again (from the start, with state 0) but with a string

- * that omits the last character (i.e. set the size to the index of

- * the problem character.)  This has not been tested within libpng.

- */

-PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,

-   png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY);

-

-/* This is the same but it checks a complete string and returns true

- * only if it just contains a floating point number.  As of 1.5.4 this

- * function also returns the state at the end of parsing the number if

- * it was valid (otherwise it returns 0.)  This can be used for testing

- * for negative or zero values using the sticky flag.

- */

-PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,

-   png_size_t size),PNG_EMPTY);

-#endif /* pCAL || sCAL */

-

-#if defined(PNG_READ_GAMMA_SUPPORTED) ||\

-    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)

-/* Added at libpng version 1.5.0 */

-/* This is a utility to provide a*times/div (rounded) and indicate

- * if there is an overflow.  The result is a boolean - false (0)

- * for overflow, true (1) if no overflow, in which case *res

- * holds the result.

- */

-PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,

-   png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);

-#endif

-

-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)

-/* Same deal, but issue a warning on overflow and return 0. */

-PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn,

-   (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by,

-   png_int_32 divided_by),PNG_EMPTY);

-#endif

-

-#ifdef PNG_GAMMA_SUPPORTED

-/* Calculate a reciprocal - used for gamma values.  This returns

- * 0 if the argument is 0 in order to maintain an undefined value;

- * there are no warnings.

- */

-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),

-   PNG_EMPTY);

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-/* The same but gives a reciprocal of the product of two fixed point

- * values.  Accuracy is suitable for gamma calculations but this is

- * not exact - use png_muldiv for that.  Only required at present on read.

- */

-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,

-   png_fixed_point b),PNG_EMPTY);

-#endif

-

-/* Return true if the gamma value is significantly different from 1.0 */

-PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),

-   PNG_EMPTY);

-#endif

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-/* Internal fixed point gamma correction.  These APIs are called as

- * required to convert single values - they don't need to be fast,

- * they are not used when processing image pixel values.

- *

- * While the input is an 'unsigned' value it must actually be the

- * correct bit value - 0..255 or 0..65535 as required.

- */

-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr,

-   unsigned int value, png_fixed_point gamma_value),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value,

-   png_fixed_point gamma_value),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value,

-   png_fixed_point gamma_value),PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),

-   PNG_EMPTY);

-PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr,

-   int bit_depth),PNG_EMPTY);

-#endif

-

-/* SIMPLIFIED READ/WRITE SUPPORT */

-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\

-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)

-/* The internal structure that png_image::opaque points to. */

-typedef struct png_control

-{

-   png_structp png_ptr;

-   png_infop   info_ptr;

-   png_voidp   error_buf;           /* Always a jmp_buf at present. */

-

-   png_const_bytep memory;          /* Memory buffer. */

-   png_size_t      size;            /* Size of the memory buffer. */

-

-   unsigned int for_write       :1; /* Otherwise it is a read structure */

-   unsigned int owned_file      :1; /* We own the file in io_ptr */

-} png_control;

-

-/* Return the pointer to the jmp_buf from a png_control: necessary because C

- * does not reveal the type of the elements of jmp_buf.

- */

-#ifdef __cplusplus

-#  define png_control_jmp_buf(pc) (((jmp_buf*)((pc)->error_buf))[0])

-#else

-#  define png_control_jmp_buf(pc) ((pc)->error_buf)

-#endif

-

-/* Utility to safely execute a piece of libpng code catching and logging any

- * errors that might occur.  Returns true on success, false on failure (either

- * of the function or as a result of a png_error.)

- */

-PNG_INTERNAL_FUNCTION(void,png_safe_error,(png_structp png_ptr,

-   png_const_charp error_message),PNG_NORETURN);

-

-#ifdef PNG_WARNINGS_SUPPORTED

-PNG_INTERNAL_FUNCTION(void,png_safe_warning,(png_structp png_ptr,

-   png_const_charp warning_message),PNG_EMPTY);

-#else

-#  define png_safe_warning 0/*dummy argument*/

-#endif

-

-PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image,

-   int (*function)(png_voidp), png_voidp arg),PNG_EMPTY);

-

-/* Utility to log an error; this also cleans up the png_image; the function

- * always returns 0 (false).

- */

-PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image,

-   png_const_charp error_message),PNG_EMPTY);

-

-#ifndef PNG_SIMPLIFIED_READ_SUPPORTED

-/* png_image_free is used by the write code but not exported */

-PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);

-#endif /* !SIMPLIFIED_READ */

-

-#endif /* SIMPLIFIED READ/WRITE */

-

-/* These are initialization functions for hardware specific PNG filter

- * optimizations; list these here then select the appropriate one at compile

- * time using the macro PNG_FILTER_OPTIMIZATIONS.  If the macro is not defined

- * the generic code is used.

- */

-#ifdef PNG_FILTER_OPTIMIZATIONS

-PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,

-   unsigned int bpp), PNG_EMPTY);

-   /* Just declare the optimization that will be used */

-#else

-   /* List *all* the possible optimizations here - this branch is required if

-    * the builder of libpng passes the definition of PNG_FILTER_OPTIMIZATIONS in

-    * CFLAGS in place of CPPFLAGS *and* uses symbol prefixing.

-    */

-PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,

-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);

-#endif

-

-/* Maintainer: Put new private prototypes here ^ */

-

-#include "pngdebug.h"

-

-#ifdef __cplusplus

-}

-#endif

-

-#endif /* PNG_VERSION_INFO_ONLY */

-#endif /* PNGPRIV_H */

+
+/* pngpriv.h - private declarations for use inside libpng
+ *
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* The symbols declared in this file (including the functions declared
+ * as extern) are PRIVATE.  They are not part of the libpng public
+ * interface, and are not recommended for use by regular applications.
+ * Some of them may become public in the future; others may stay private,
+ * change in an incompatible way, or even disappear.
+ * Although the libpng users are not forbidden to include this header,
+ * they should be well aware of the issues that may arise from doing so.
+ */
+
+#ifndef PNGPRIV_H
+#define PNGPRIV_H
+
+/* Feature Test Macros.  The following are defined here to ensure that correctly
+ * implemented libraries reveal the APIs libpng needs to build and hide those
+ * that are not needed and potentially damaging to the compilation.
+ *
+ * Feature Test Macros must be defined before any system header is included (see
+ * POSIX 1003.1 2.8.2 "POSIX Symbols."
+ *
+ * These macros only have an effect if the operating system supports either
+ * POSIX 1003.1 or C99, or both.  On other operating systems (particularly
+ * Windows/Visual Studio) there is no effect; the OS specific tests below are
+ * still required (as of 2011-05-02.)
+ */
+#define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
+
+#ifndef PNG_VERSION_INFO_ONLY
+/* Standard library headers not required by png.h: */
+#  include <stdlib.h>
+#  include <string.h>
+#endif
+
+#define PNGLIB_BUILD /*libpng is being built, not used*/
+
+/* If HAVE_CONFIG_H is defined during the build then the build system must
+ * provide an appropriate "config.h" file on the include path.  The header file
+ * must provide definitions as required below (search for "HAVE_CONFIG_H");
+ * see configure.ac for more details of the requirements.  The macro
+ * "PNG_NO_CONFIG_H" is provided for maintainers to test for dependencies on
+ * 'configure'; define this macro to prevent the configure build including the
+ * configure generated config.h.  Libpng is expected to compile without *any*
+ * special build system support on a reasonably ANSI-C compliant system.
+ */
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+
+   /* Pick up the definition of 'restrict' from config.h if it was read: */
+#  define PNG_RESTRICT restrict
+#endif
+
+/* To support symbol prefixing it is necessary to know *before* including png.h
+ * whether the fixed point (and maybe other) APIs are exported, because if they
+ * are not internal definitions may be required.  This is handled below just
+ * before png.h is included, but load the configuration now if it is available.
+ */
+#ifndef PNGLCONF_H
+#  include "pnglibconf.h"
+#endif
+
+/* Local renames may change non-exported API functions from png.h */
+#if defined(PNG_PREFIX) && !defined(PNGPREFIX_H)
+#  include "pngprefix.h"
+#endif
+
+#ifdef PNG_USER_CONFIG
+#  include "pngusr.h"
+   /* These should have been defined in pngusr.h */
+#  ifndef PNG_USER_PRIVATEBUILD
+#    define PNG_USER_PRIVATEBUILD "Custom libpng build"
+#  endif
+#  ifndef PNG_USER_DLLFNAME_POSTFIX
+#    define PNG_USER_DLLFNAME_POSTFIX "Cb"
+#  endif
+#endif
+
+/* Compile time options.
+ * =====================
+ * In a multi-arch build the compiler may compile the code several times for the
+ * same object module, producing different binaries for different architectures.
+ * When this happens configure-time setting of the target host options cannot be
+ * done and this interferes with the handling of the ARM NEON optimizations, and
+ * possibly other similar optimizations.  Put additional tests here; in general
+ * this is needed when the same option can be changed at both compile time and
+ * run time depending on the target OS (i.e. iOS vs Android.)
+ *
+ * NOTE: symbol prefixing does not pass $(CFLAGS) to the preprocessor, because
+ * this is not possible with certain compilers (Oracle SUN OS CC), as a result
+ * it is necessary to ensure that all extern functions that *might* be used
+ * regardless of $(CFLAGS) get declared in this file.  The test on __ARM_NEON__
+ * below is one example of this behavior because it is controlled by the
+ * presence or not of -mfpu=neon on the GCC command line, it is possible to do
+ * this in $(CC), e.g. "CC=gcc -mfpu=neon", but people who build libpng rarely
+ * do this.
+ */
+#ifndef PNG_ARM_NEON_OPT
+   /* ARM NEON optimizations are being controlled by the compiler settings,
+    * typically the target FPU.  If the FPU has been set to NEON (-mfpu=neon
+    * with GCC) then the compiler will define __ARM_NEON__ and we can rely
+    * unconditionally on NEON instructions not crashing, otherwise we must
+    * disable use of NEON instructions.
+    *
+    * NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they
+    * can only be turned on automatically if that is supported too.  If
+    * PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail
+    * to compile with an appropriate #error if ALIGNED_MEMORY has been turned
+    * off.
+    *
+    * Note that gcc-4.9 defines __ARM_NEON instead of the deprecated
+    * __ARM_NEON__, so we check both variants.
+    *
+    * To disable ARM_NEON optimizations entirely, and skip compiling the
+    * associated assembler code, pass --enable-arm-neon=no to configure
+    * or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS.
+    */
+#  if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \
+   defined(PNG_ALIGNED_MEMORY_SUPPORTED)
+#     define PNG_ARM_NEON_OPT 0
+#  else
+#     define PNG_ARM_NEON_OPT 0
+#  endif
+#endif
+
+#if PNG_ARM_NEON_OPT > 0
+   /* NEON optimizations are to be at least considered by libpng, so enable the
+    * callbacks to do this.
+    */
+#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_neon
+
+   /* By default the 'intrinsics' code in arm/filter_neon_intrinsics.c is used
+    * if possible - if __ARM_NEON__ is set and the compiler version is not known
+    * to be broken.  This is controlled by PNG_ARM_NEON_IMPLEMENTATION which can
+    * be:
+    *
+    *    1  The intrinsics code (the default with __ARM_NEON__)
+    *    2  The hand coded assembler (the default without __ARM_NEON__)
+    *
+    * It is possible to set PNG_ARM_NEON_IMPLEMENTATION in CPPFLAGS, however
+    * this is *NOT* supported and may cease to work even after a minor revision
+    * to libpng.  It *is* valid to do this for testing purposes, e.g. speed
+    * testing or a new compiler, but the results should be communicated to the
+    * libpng implementation list for incorporation in the next minor release.
+    */
+#  ifndef PNG_ARM_NEON_IMPLEMENTATION
+#     if defined(__ARM_NEON__) || defined(__ARM_NEON)
+#        if defined(__clang__)
+            /* At present it is unknown by the libpng developers which versions
+             * of clang support the intrinsics, however some or perhaps all
+             * versions do not work with the assembler so this may be
+             * irrelevant, so just use the default (do nothing here.)
+             */
+#        elif defined(__GNUC__)
+            /* GCC 4.5.4 NEON support is known to be broken.  4.6.3 is known to
+             * work, so if this *is* GCC, or G++, look for a version >4.5
+             */
+#           if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
+#              define PNG_ARM_NEON_IMPLEMENTATION 2
+#           endif /* no GNUC support */
+#        endif /* __GNUC__ */
+#     else /* !defined __ARM_NEON__ */
+         /* The 'intrinsics' code simply won't compile without this -mfpu=neon:
+          */
+#        define PNG_ARM_NEON_IMPLEMENTATION 2
+#     endif /* __ARM_NEON__ */
+#  endif /* !PNG_ARM_NEON_IMPLEMENTATION */
+
+#  ifndef PNG_ARM_NEON_IMPLEMENTATION
+      /* Use the intrinsics code by default. */
+#     define PNG_ARM_NEON_IMPLEMENTATION 1
+#  endif
+#endif /* PNG_ARM_NEON_OPT > 0 */
+
+/* Is this a build of a DLL where compilation of the object modules requires
+ * different preprocessor settings to those required for a simple library?  If
+ * so PNG_BUILD_DLL must be set.
+ *
+ * If libpng is used inside a DLL but that DLL does not export the libpng APIs
+ * PNG_BUILD_DLL must not be set.  To avoid the code below kicking in build a
+ * static library of libpng then link the DLL against that.
+ */
+#ifndef PNG_BUILD_DLL
+#  ifdef DLL_EXPORT
+      /* This is set by libtool when files are compiled for a DLL; libtool
+       * always compiles twice, even on systems where it isn't necessary.  Set
+       * PNG_BUILD_DLL in case it is necessary:
+       */
+#     define PNG_BUILD_DLL
+#  else
+#     ifdef _WINDLL
+         /* This is set by the Microsoft Visual Studio IDE in projects that
+          * build a DLL.  It can't easily be removed from those projects (it
+          * isn't visible in the Visual Studio UI) so it is a fairly reliable
+          * indication that PNG_IMPEXP needs to be set to the DLL export
+          * attributes.
+          */
+#        define PNG_BUILD_DLL
+#     else
+#        ifdef __DLL__
+            /* This is set by the Borland C system when compiling for a DLL
+             * (as above.)
+             */
+#           define PNG_BUILD_DLL
+#        else
+            /* Add additional compiler cases here. */
+#        endif
+#     endif
+#  endif
+#endif /* Setting PNG_BUILD_DLL if required */
+
+/* See pngconf.h for more details: the builder of the library may set this on
+ * the command line to the right thing for the specific compilation system or it
+ * may be automagically set above (at present we know of no system where it does
+ * need to be set on the command line.)
+ *
+ * PNG_IMPEXP must be set here when building the library to prevent pngconf.h
+ * setting it to the "import" setting for a DLL build.
+ */
+#ifndef PNG_IMPEXP
+#  ifdef PNG_BUILD_DLL
+#     define PNG_IMPEXP PNG_DLL_EXPORT
+#  else
+      /* Not building a DLL, or the DLL doesn't require specific export
+       * definitions.
+       */
+#     define PNG_IMPEXP
+#  endif
+#endif
+
+/* No warnings for private or deprecated functions in the build: */
+#ifndef PNG_DEPRECATED
+#  define PNG_DEPRECATED
+#endif
+#ifndef PNG_PRIVATE
+#  define PNG_PRIVATE
+#endif
+
+/* Symbol preprocessing support.
+ *
+ * To enable listing global, but internal, symbols the following macros should
+ * always be used to declare an extern data or function object in this file.
+ */
+#ifndef PNG_INTERNAL_DATA
+#  define PNG_INTERNAL_DATA(type, name, array) PNG_LINKAGE_DATA type name array
+#endif
+
+#ifndef PNG_INTERNAL_FUNCTION
+#  define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\
+      PNG_LINKAGE_FUNCTION PNG_FUNCTION(type, name, args, PNG_EMPTY attributes)
+#endif
+
+#ifndef PNG_INTERNAL_CALLBACK
+#  define PNG_INTERNAL_CALLBACK(type, name, args, attributes)\
+      PNG_LINKAGE_CALLBACK PNG_FUNCTION(type, (PNGCBAPI name), args,\
+         PNG_EMPTY attributes)
+#endif
+
+/* If floating or fixed point APIs are disabled they may still be compiled
+ * internally.  To handle this make sure they are declared as the appropriate
+ * internal extern function (otherwise the symbol prefixing stuff won't work and
+ * the functions will be used without definitions.)
+ *
+ * NOTE: although all the API functions are declared here they are not all
+ * actually built!  Because the declarations are still made it is necessary to
+ * fake out types that they depend on.
+ */
+#ifndef PNG_FP_EXPORT
+#  ifndef PNG_FLOATING_POINT_SUPPORTED
+#     define PNG_FP_EXPORT(ordinal, type, name, args)\
+         PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY);
+#     ifndef PNG_VERSION_INFO_ONLY
+         typedef struct png_incomplete png_double;
+         typedef png_double*           png_doublep;
+         typedef const png_double*     png_const_doublep;
+         typedef png_double**          png_doublepp;
+#     endif
+#  endif
+#endif
+#ifndef PNG_FIXED_EXPORT
+#  ifndef PNG_FIXED_POINT_SUPPORTED
+#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\
+         PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY);
+#  endif
+#endif
+
+#include "png.h"
+
+/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */
+#ifndef PNG_DLL_EXPORT
+#  define PNG_DLL_EXPORT
+#endif
+
+/* This is a global switch to set the compilation for an installed system
+ * (a release build).  It can be set for testing debug builds to ensure that
+ * they will compile when the build type is switched to RC or STABLE, the
+ * default is just to use PNG_LIBPNG_BUILD_BASE_TYPE.  Set this in CPPFLAGS
+ * with either:
+ *
+ *   -DPNG_RELEASE_BUILD Turns on the release compile path
+ *   -DPNG_RELEASE_BUILD=0 Turns it off
+ * or in your pngusr.h with
+ *   #define PNG_RELEASE_BUILD=1 Turns on the release compile path
+ *   #define PNG_RELEASE_BUILD=0 Turns it off
+ */
+#ifndef PNG_RELEASE_BUILD
+#  define PNG_RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC)
+#endif
+
+/* SECURITY and SAFETY:
+ *
+ * libpng is built with support for internal limits on image dimensions and
+ * memory usage.  These are documented in scripts/pnglibconf.dfa of the
+ * source and recorded in the machine generated header file pnglibconf.h.
+ */
+
+/* If you are running on a machine where you cannot allocate more
+ * than 64K of memory at once, uncomment this.  While libpng will not
+ * normally need that much memory in a chunk (unless you load up a very
+ * large file), zlib needs to know how big of a chunk it can use, and
+ * libpng thus makes sure to check any memory allocation to verify it
+ * will fit into memory.
+ *
+ * zlib provides 'MAXSEG_64K' which, if defined, indicates the
+ * same limit and pngconf.h (already included) sets the limit
+ * if certain operating systems are detected.
+ */
+#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
+#  define PNG_MAX_MALLOC_64K
+#endif
+
+#ifndef PNG_UNUSED
+/* Unused formal parameter warnings are silenced using the following macro
+ * which is expected to have no bad effects on performance (optimizing
+ * compilers will probably remove it entirely).  Note that if you replace
+ * it with something other than whitespace, you must include the terminating
+ * semicolon.
+ */
+#  define PNG_UNUSED(param) (void)param;
+#endif
+
+/* Just a little check that someone hasn't tried to define something
+ * contradictory.
+ */
+#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)
+#  undef PNG_ZBUF_SIZE
+#  define PNG_ZBUF_SIZE 65536L
+#endif
+
+/* If warnings or errors are turned off the code is disabled or redirected here.
+ * From 1.5.4 functions have been added to allow very limited formatting of
+ * error and warning messages - this code will also be disabled here.
+ */
+#ifdef PNG_WARNINGS_SUPPORTED
+#  define PNG_WARNING_PARAMETERS(p) png_warning_parameters p;
+#else
+#  define png_warning_parameter(p,number,string) ((void)0)
+#  define png_warning_parameter_unsigned(p,number,format,value) ((void)0)
+#  define png_warning_parameter_signed(p,number,format,value) ((void)0)
+#  define png_formatted_warning(pp,p,message) ((void)(pp))
+#  define PNG_WARNING_PARAMETERS(p)
+#endif
+#ifndef PNG_ERROR_TEXT_SUPPORTED
+#  define png_fixed_error(s1,s2) png_err(s1)
+#endif
+
+/* C allows up-casts from (void*) to any pointer and (const void*) to any
+ * pointer to a const object.  C++ regards this as a type error and requires an
+ * explicit, static, cast and provides the static_cast<> rune to ensure that
+ * const is not cast away.
+ */
+#ifdef __cplusplus
+#  define png_voidcast(type, value) static_cast<type>(value)
+#  define png_constcast(type, value) const_cast<type>(value)
+#  define png_aligncast(type, value) \
+   static_cast<type>(static_cast<void*>(value))
+#  define png_aligncastconst(type, value) \
+   static_cast<type>(static_cast<const void*>(value))
+#else
+#  define png_voidcast(type, value) (value)
+#  define png_constcast(type, value) ((type)(value))
+#  define png_aligncast(type, value) ((void*)(value))
+#  define png_aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+/* Some fixed point APIs are still required even if not exported because
+ * they get used by the corresponding floating point APIs.  This magic
+ * deals with this:
+ */
+#ifdef PNG_FIXED_POINT_SUPPORTED
+#  define PNGFAPI PNGAPI
+#else
+#  define PNGFAPI /* PRIVATE */
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+/* Other defines specific to compilers can go here.  Try to keep
+ * them inside an appropriate ifdef/endif pair for portability.
+ */
+#if defined(PNG_FLOATING_POINT_SUPPORTED) ||\
+    defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)
+   /* png.c requires the following ANSI-C constants if the conversion of
+    * floating point to ASCII is implemented therein:
+    *
+    *  DBL_DIG  Maximum number of decimal digits (can be set to any constant)
+    *  DBL_MIN  Smallest normalized fp number (can be set to an arbitrary value)
+    *  DBL_MAX  Maximum floating point number (can be set to an arbitrary value)
+    */
+#  include <float.h>
+
+#  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
+    defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
+     /* We need to check that <math.h> hasn't already been included earlier
+      * as it seems it doesn't agree with <fp.h>, yet we should really use
+      * <fp.h> if possible.
+      */
+#    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
+#      include <fp.h>
+#    endif
+#  else
+#    include <math.h>
+#  endif
+#  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
+     /* Amiga SAS/C: We must include builtin FPU functions when compiling using
+      * MATH=68881
+      */
+#    include <m68881.h>
+#  endif
+#endif
+
+/* This provides the non-ANSI (far) memory allocation routines. */
+#if defined(__TURBOC__) && defined(__MSDOS__)
+#  include <mem.h>
+#  include <alloc.h>
+#endif
+
+#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \
+    defined(_WIN32) || defined(__WIN32__)
+#  include <windows.h>  /* defines _WINDOWS_ macro */
+#endif
+#endif /* PNG_VERSION_INFO_ONLY */
+
+/* Moved here around 1.5.0beta36 from pngconf.h */
+/* Users may want to use these so they are not private.  Any library
+ * functions that are passed far data must be model-independent.
+ */
+
+/* Memory model/platform independent fns */
+#ifndef PNG_ABORT
+#  ifdef _WINDOWS_
+#    define PNG_ABORT() ExitProcess(0)
+#  else
+#    define PNG_ABORT() abort()
+#  endif
+#endif
+
+/* These macros may need to be architecture dependent. */
+#define PNG_ALIGN_NONE   0 /* do not use data alignment */
+#define PNG_ALIGN_ALWAYS 1 /* assume unaligned accesses are OK */
+#ifdef offsetof
+#  define PNG_ALIGN_OFFSET 2 /* use offsetof to determine alignment */
+#else
+#  define PNG_ALIGN_OFFSET -1 /* prevent the use of this */
+#endif
+#define PNG_ALIGN_SIZE   3 /* use sizeof to determine alignment */
+
+#ifndef PNG_ALIGN_TYPE
+   /* Default to using aligned access optimizations and requiring alignment to a
+    * multiple of the data type size.  Override in a compiler specific fashion
+    * if necessary by inserting tests here:
+    */
+#  define PNG_ALIGN_TYPE PNG_ALIGN_SIZE
+#endif
+
+#if PNG_ALIGN_TYPE == PNG_ALIGN_SIZE
+   /* This is used because in some compiler implementations non-aligned
+    * structure members are supported, so the offsetof approach below fails.
+    * Set PNG_ALIGN_SIZE=0 for compiler combinations where unaligned access
+    * is good for performance.  Do not do this unless you have tested the result
+    * and understand it.
+    */
+#  define png_alignof(type) (sizeof (type))
+#else
+#  if PNG_ALIGN_TYPE == PNG_ALIGN_OFFSET
+#     define png_alignof(type) offsetof(struct{char c; type t;}, t)
+#  else
+#     if PNG_ALIGN_TYPE == PNG_ALIGN_ALWAYS
+#        define png_alignof(type) (1)
+#     endif
+      /* Else leave png_alignof undefined to prevent use thereof */
+#  endif
+#endif
+
+/* This implicitly assumes alignment is always to a power of 2. */
+#ifdef png_alignof
+#  define png_isaligned(ptr, type)\
+   ((((const char*)ptr-(const char*)0) & (png_alignof(type)-1)) == 0)
+#else
+#  define png_isaligned(ptr, type) 0
+#endif
+
+/* End of memory model/platform independent support */
+/* End of 1.5.0beta36 move from pngconf.h */
+
+/* CONSTANTS and UTILITY MACROS
+ * These are used internally by libpng and not exposed in the API
+ */
+
+/* Various modes of operation.  Note that after an init, mode is set to
+ * zero automatically when the structure is created.  Three of these
+ * are defined in png.h because they need to be visible to applications
+ * that call png_set_unknown_chunk().
+ */
+/* #define PNG_HAVE_IHDR            0x01 (defined in png.h) */
+/* #define PNG_HAVE_PLTE            0x02 (defined in png.h) */
+#define PNG_HAVE_IDAT               0x04
+/* #define PNG_AFTER_IDAT           0x08 (defined in png.h) */
+#define PNG_HAVE_IEND               0x10
+                   /*               0x20 (unused) */
+                   /*               0x40 (unused) */
+                   /*               0x80 (unused) */
+#define PNG_HAVE_CHUNK_HEADER      0x100
+#define PNG_WROTE_tIME             0x200
+#define PNG_WROTE_INFO_BEFORE_PLTE 0x400
+#define PNG_BACKGROUND_IS_GRAY     0x800
+#define PNG_HAVE_PNG_SIGNATURE    0x1000
+#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
+                   /*             0x4000 (unused) */
+#define PNG_IS_READ_STRUCT        0x8000 /* Else is a write struct */
+
+/* Flags for the transformations the PNG library does on the image data */
+#define PNG_BGR                 0x0001
+#define PNG_INTERLACE           0x0002
+#define PNG_PACK                0x0004
+#define PNG_SHIFT               0x0008
+#define PNG_SWAP_BYTES          0x0010
+#define PNG_INVERT_MONO         0x0020
+#define PNG_QUANTIZE            0x0040
+#define PNG_COMPOSE             0x0080     /* Was PNG_BACKGROUND */
+#define PNG_BACKGROUND_EXPAND   0x0100
+#define PNG_EXPAND_16           0x0200     /* Added to libpng 1.5.2 */
+#define PNG_16_TO_8             0x0400     /* Becomes 'chop' in 1.5.4 */
+#define PNG_RGBA                0x0800
+#define PNG_EXPAND              0x1000
+#define PNG_GAMMA               0x2000
+#define PNG_GRAY_TO_RGB         0x4000
+#define PNG_FILLER              0x8000
+#define PNG_PACKSWAP           0x10000
+#define PNG_SWAP_ALPHA         0x20000
+#define PNG_STRIP_ALPHA        0x40000
+#define PNG_INVERT_ALPHA       0x80000
+#define PNG_USER_TRANSFORM    0x100000
+#define PNG_RGB_TO_GRAY_ERR   0x200000
+#define PNG_RGB_TO_GRAY_WARN  0x400000
+#define PNG_RGB_TO_GRAY       0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */
+#define PNG_ENCODE_ALPHA      0x800000 /* Added to libpng-1.5.4 */
+#define PNG_ADD_ALPHA        0x1000000 /* Added to libpng-1.2.7 */
+#define PNG_EXPAND_tRNS      0x2000000 /* Added to libpng-1.2.9 */
+#define PNG_SCALE_16_TO_8    0x4000000 /* Added to libpng-1.5.4 */
+                       /*    0x8000000 unused */
+                       /*   0x10000000 unused */
+                       /*   0x20000000 unused */
+                       /*   0x40000000 unused */
+/* Flags for png_create_struct */
+#define PNG_STRUCT_PNG   0x0001
+#define PNG_STRUCT_INFO  0x0002
+
+/* Flags for the png_ptr->flags rather than declaring a byte for each one */
+#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001
+#define PNG_FLAG_ZSTREAM_INITIALIZED      0x0002 /* Added to libpng-1.6.0 */
+                                  /*      0x0004    unused */
+#define PNG_FLAG_ZSTREAM_ENDED            0x0008 /* Added to libpng-1.6.0 */
+                                  /*      0x0010    unused */
+                                  /*      0x0020    unused */
+#define PNG_FLAG_ROW_INIT                 0x0040
+#define PNG_FLAG_FILLER_AFTER             0x0080
+#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100
+#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200
+#define PNG_FLAG_CRC_CRITICAL_USE         0x0400
+#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800
+#define PNG_FLAG_ASSUME_sRGB              0x1000 /* Added to libpng-1.5.4 */
+#define PNG_FLAG_OPTIMIZE_ALPHA           0x2000 /* Added to libpng-1.5.4 */
+#define PNG_FLAG_DETECT_UNINITIALIZED     0x4000 /* Added to libpng-1.5.4 */
+/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000 */
+/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS      0x10000 */
+#define PNG_FLAG_LIBRARY_MISMATCH        0x20000
+#define PNG_FLAG_STRIP_ERROR_NUMBERS     0x40000
+#define PNG_FLAG_STRIP_ERROR_TEXT        0x80000
+#define PNG_FLAG_BENIGN_ERRORS_WARN     0x100000 /* Added to libpng-1.4.0 */
+#define PNG_FLAG_APP_WARNINGS_WARN      0x200000 /* Added to libpng-1.6.0 */
+#define PNG_FLAG_APP_ERRORS_WARN        0x400000 /* Added to libpng-1.6.0 */
+                                  /*    0x800000    unused */
+                                  /*   0x1000000    unused */
+                                  /*   0x2000000    unused */
+                                  /*   0x4000000    unused */
+                                  /*   0x8000000    unused */
+                                  /*  0x10000000    unused */
+                                  /*  0x20000000    unused */
+                                  /*  0x40000000    unused */
+
+#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
+                                     PNG_FLAG_CRC_ANCILLARY_NOWARN)
+
+#define PNG_FLAG_CRC_CRITICAL_MASK  (PNG_FLAG_CRC_CRITICAL_USE | \
+                                     PNG_FLAG_CRC_CRITICAL_IGNORE)
+
+#define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \
+                                     PNG_FLAG_CRC_CRITICAL_MASK)
+
+/* Save typing and make code easier to understand */
+
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+   abs((int)((c1).green) - (int)((c2).green)) + \
+   abs((int)((c1).blue) - (int)((c2).blue)))
+
+/* Added to libpng-1.6.0: scale a 16-bit value in the range 0..65535 to 0..255
+ * by dividing by 257 *with rounding*.  This macro is exact for the given range.
+ * See the discourse in pngrtran.c png_do_scale_16_to_8.  The values in the
+ * macro were established by experiment (modifying the added value).  The macro
+ * has a second variant that takes a value already scaled by 255 and divides by
+ * 65535 - this has a maximum error of .502.  Over the range 0..65535*65535 it
+ * only gives off-by-one errors and only for 0.5% (1 in 200) of the values.
+ */
+#define PNG_DIV65535(v24) (((v24) + 32895) >> 16)
+#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255)
+
+/* Added to libpng-1.2.6 JB */
+#define PNG_ROWBYTES(pixel_bits, width) \
+    ((pixel_bits) >= 8 ? \
+    ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \
+    (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) )
+
+/* PNG_OUT_OF_RANGE returns true if value is outside the range
+ * ideal-delta..ideal+delta.  Each argument is evaluated twice.
+ * "ideal" and "delta" should be constants, normally simple
+ * integers, "value" a variable. Added to libpng-1.2.6 JB
+ */
+#define PNG_OUT_OF_RANGE(value, ideal, delta) \
+   ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )
+
+/* Conversions between fixed and floating point, only defined if
+ * required (to make sure the code doesn't accidentally use float
+ * when it is supposedly disabled.)
+ */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+/* The floating point conversion can't overflow, though it can and
+ * does lose accuracy relative to the original fixed point value.
+ * In practice this doesn't matter because png_fixed_point only
+ * stores numbers with very low precision.  The png_ptr and s
+ * arguments are unused by default but are there in case error
+ * checking becomes a requirement.
+ */
+#define png_float(png_ptr, fixed, s) (.00001 * (fixed))
+
+/* The fixed point conversion performs range checking and evaluates
+ * its argument multiple times, so must be used with care.  The
+ * range checking uses the PNG specification values for a signed
+ * 32-bit fixed point value except that the values are deliberately
+ * rounded-to-zero to an integral value - 21474 (21474.83 is roughly
+ * (2^31-1) * 100000). 's' is a string that describes the value being
+ * converted.
+ *
+ * NOTE: this macro will raise a png_error if the range check fails,
+ * therefore it is normally only appropriate to use this on values
+ * that come from API calls or other sources where an out of range
+ * error indicates a programming error, not a data error!
+ *
+ * NOTE: by default this is off - the macro is not used - because the
+ * function call saves a lot of code.
+ */
+#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED
+#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\
+    ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0))
+#endif
+/* else the corresponding function is defined below, inside the scope of the
+ * cplusplus test.
+ */
+#endif
+
+/* Constants for known chunk types.  If you need to add a chunk, define the name
+ * here.  For historical reasons these constants have the form png_<name>; i.e.
+ * the prefix is lower case.  Please use decimal values as the parameters to
+ * match the ISO PNG specification and to avoid relying on the C locale
+ * interpretation of character values.
+ *
+ * Prior to 1.5.6 these constants were strings, as of 1.5.6 png_uint_32 values
+ * are computed and a new macro (PNG_STRING_FROM_CHUNK) added to allow a string
+ * to be generated if required.
+ *
+ * PNG_32b correctly produces a value shifted by up to 24 bits, even on
+ * architectures where (int) is only 16 bits.
+ */
+#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
+#define PNG_U32(b1,b2,b3,b4) \
+   (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
+
+/* Constants for known chunk types.
+ *
+ * MAINTAINERS: If you need to add a chunk, define the name here.
+ * For historical reasons these constants have the form png_<name>; i.e.
+ * the prefix is lower case.  Please use decimal values as the parameters to
+ * match the ISO PNG specification and to avoid relying on the C locale
+ * interpretation of character values.  Please keep the list sorted.
+ *
+ * Notice that PNG_U32 is used to define a 32-bit value for the 4 byte chunk
+ * type.  In fact the specification does not express chunk types this way,
+ * however using a 32-bit value means that the chunk type can be read from the
+ * stream using exactly the same code as used for a 32-bit unsigned value and
+ * can be examined far more efficiently (using one arithmetic compare).
+ *
+ * Prior to 1.5.6 the chunk type constants were expressed as C strings.  The
+ * libpng API still uses strings for 'unknown' chunks and a macro,
+ * PNG_STRING_FROM_CHUNK, allows a string to be generated if required.  Notice
+ * that for portable code numeric values must still be used; the string "IHDR"
+ * is not portable and neither is PNG_U32('I', 'H', 'D', 'R').
+ *
+ * In 1.7.0 the definitions will be made public in png.h to avoid having to
+ * duplicate the same definitions in application code.
+ */
+#define png_IDAT PNG_U32( 73,  68,  65,  84)
+#define png_IEND PNG_U32( 73,  69,  78,  68)
+#define png_IHDR PNG_U32( 73,  72,  68,  82)
+#define png_PLTE PNG_U32( 80,  76,  84,  69)
+#define png_bKGD PNG_U32( 98,  75,  71,  68)
+#define png_cHRM PNG_U32( 99,  72,  82,  77)
+#define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
+#define png_gAMA PNG_U32(103,  65,  77,  65)
+#define png_gIFg PNG_U32(103,  73,  70, 103)
+#define png_gIFt PNG_U32(103,  73,  70, 116) /* deprecated */
+#define png_gIFx PNG_U32(103,  73,  70, 120)
+#define png_hIST PNG_U32(104,  73,  83,  84)
+#define png_iCCP PNG_U32(105,  67,  67,  80)
+#define png_iTXt PNG_U32(105,  84,  88, 116)
+#define png_oFFs PNG_U32(111,  70,  70, 115)
+#define png_pCAL PNG_U32(112,  67,  65,  76)
+#define png_pHYs PNG_U32(112,  72,  89, 115)
+#define png_sBIT PNG_U32(115,  66,  73,  84)
+#define png_sCAL PNG_U32(115,  67,  65,  76)
+#define png_sPLT PNG_U32(115,  80,  76,  84)
+#define png_sRGB PNG_U32(115,  82,  71,  66)
+#define png_sTER PNG_U32(115,  84,  69,  82)
+#define png_tEXt PNG_U32(116,  69,  88, 116)
+#define png_tIME PNG_U32(116,  73,  77,  69)
+#define png_tRNS PNG_U32(116,  82,  78,  83)
+#define png_zTXt PNG_U32(122,  84,  88, 116)
+
+/* The following will work on (signed char*) strings, whereas the get_uint_32
+ * macro will fail on top-bit-set values because of the sign extension.
+ */
+#define PNG_CHUNK_FROM_STRING(s)\
+   PNG_U32(0xff & (s)[0], 0xff & (s)[1], 0xff & (s)[2], 0xff & (s)[3])
+
+/* This uses (char), not (png_byte) to avoid warnings on systems where (char) is
+ * signed and the argument is a (char[])  This macro will fail miserably on
+ * systems where (char) is more than 8 bits.
+ */
+#define PNG_STRING_FROM_CHUNK(s,c)\
+   (void)(((char*)(s))[0]=(char)(((c)>>24) & 0xff), \
+   ((char*)(s))[1]=(char)(((c)>>16) & 0xff),\
+   ((char*)(s))[2]=(char)(((c)>>8) & 0xff), \
+   ((char*)(s))[3]=(char)((c & 0xff)))
+
+/* Do the same but terminate with a null character. */
+#define PNG_CSTRING_FROM_CHUNK(s,c)\
+   (void)(PNG_STRING_FROM_CHUNK(s,c), ((char*)(s))[4] = 0)
+
+/* Test on flag values as defined in the spec (section 5.4): */
+#define PNG_CHUNK_ANCILLARY(c)   (1 & ((c) >> 29))
+#define PNG_CHUNK_CRITICAL(c)     (!PNG_CHUNK_ANCILLARY(c))
+#define PNG_CHUNK_PRIVATE(c)      (1 & ((c) >> 21))
+#define PNG_CHUNK_RESERVED(c)     (1 & ((c) >> 13))
+#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >>  5))
+
+/* Gamma values (new at libpng-1.5.4): */
+#define PNG_GAMMA_MAC_OLD 151724  /* Assume '1.8' is really 2.2/1.45! */
+#define PNG_GAMMA_MAC_INVERSE 65909
+#define PNG_GAMMA_sRGB_INVERSE 45455
+
+/* Almost everything below is C specific; the #defines above can be used in
+ * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot.
+ */
+#ifndef PNG_VERSION_INFO_ONLY
+
+#include "pngstruct.h"
+#include "pnginfo.h"
+
+/* Validate the include paths - the include path used to generate pnglibconf.h
+ * must match that used in the build, or we must be using pnglibconf.h.prebuilt:
+ */
+#if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM
+#  error ZLIB_VERNUM != PNG_ZLIB_VERNUM \
+      "-I (include path) error: see the notes in pngpriv.h"
+   /* This means that when pnglibconf.h was built the copy of zlib.h that it
+    * used is not the same as the one being used here.  Because the build of
+    * libpng makes decisions to use inflateInit2 and inflateReset2 based on the
+    * zlib version number and because this affects handling of certain broken
+    * PNG files the -I directives must match.
+    *
+    * The most likely explanation is that you passed a -I in CFLAGS. This will
+    * not work; all the preprocessor directories and in particular all the -I
+    * directives must be in CPPFLAGS.
+    */
+#endif
+
+/* This is used for 16-bit gamma tables -- only the top level pointers are
+ * const; this could be changed:
+ */
+typedef const png_uint_16p * png_const_uint_16pp;
+
+/* Added to libpng-1.5.7: sRGB conversion tables */
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
+   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_table, [256]);
+   /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value,
+    * 0..65535.  This table gives the closest 16-bit answers (no errors).
+    */
+#endif
+
+PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_base, [512]);
+PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]);
+
+#define PNG_sRGB_FROM_LINEAR(linear) \
+  ((png_byte)(0xff & ((png_sRGB_base[(linear)>>15] \
+   + ((((linear) & 0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8)))
+   /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB
+    * encoded value with maximum error 0.646365.  Note that the input is not a
+    * 16-bit value; it has been multiplied by 255! */
+#endif /* SIMPLIFIED_READ/WRITE */
+
+
+/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Internal functions; these are not exported from a DLL however because they
+ * are used within several of the C source files they have to be C extern.
+ *
+ * All of these functions must be declared with PNG_INTERNAL_FUNCTION.
+ */
+
+/* Zlib support */
+#define PNG_UNEXPECTED_ZLIB_RETURN (-7)
+PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
+   PNG_EMPTY);
+   /* Used by the zlib handling functions to ensure that z_stream::msg is always
+    * set before they return.
+    */
+
+#ifdef PNG_WRITE_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
+   png_compression_bufferp *list),PNG_EMPTY);
+   /* Free the buffer list used by the compressed write code. */
+#endif
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
+   !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
+   (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \
+   defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \
+   (defined(PNG_sCAL_SUPPORTED) && \
+   defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))
+PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr,
+   double fp, png_const_charp text),PNG_EMPTY);
+#endif
+
+/* Check the user version string for compatibility, returns false if the version
+ * numbers aren't compatible.
+ */
+PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
+   png_const_charp user_png_ver),PNG_EMPTY);
+
+/* Internal base allocator - no messages, NULL on failure to allocate.  This
+ * does, however, call the application provided allocator and that could call
+ * png_error (although that would be a bug in the application implementation.)
+ */
+PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr,
+   png_alloc_size_t size),PNG_ALLOCATED);
+
+#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
+   defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
+/* Internal array allocator, outputs no error or warning messages on failure,
+ * just returns NULL.
+ */
+PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr,
+   int nelements, size_t element_size),PNG_ALLOCATED);
+
+/* The same but an existing array is extended by add_elements.  This function
+ * also memsets the new elements to 0 and copies the old elements.  The old
+ * array is not freed or altered.
+ */
+PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,
+   png_const_voidp array, int old_elements, int add_elements,
+   size_t element_size),PNG_ALLOCATED);
+#endif /* text, sPLT or unknown chunks */
+
+/* Magic to create a struct when there is no struct to call the user supplied
+ * memory allocators.  Because error handling has not been set up the memory
+ * handlers can't safely call png_error, but this is an obscure and undocumented
+ * restriction so libpng has to assume that the 'free' handler, at least, might
+ * call png_error.
+ */
+PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct,
+   (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+    png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn,
+    png_free_ptr free_fn),PNG_ALLOCATED);
+
+/* Free memory from internal libpng struct */
+PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr),
+   PNG_EMPTY);
+
+/* Free an allocated jmp_buf (always succeeds) */
+PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY);
+
+/* Function to allocate memory for zlib.  PNGAPI is disallowed. */
+PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size),
+   PNG_ALLOCATED);
+
+/* Function to free memory for zlib.  PNGAPI is disallowed. */
+PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY);
+
+/* Next four functions are used internally as callbacks.  PNGCBAPI is required
+ * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3, changed to
+ * PNGCBAPI at 1.5.0
+ */
+
+PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,
+    png_bytep data, png_size_t length),PNG_EMPTY);
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,
+    png_bytep buffer, png_size_t length),PNG_EMPTY);
+#endif
+
+PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,
+    png_bytep data, png_size_t length),PNG_EMPTY);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#  ifdef PNG_STDIO_SUPPORTED
+PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr),
+   PNG_EMPTY);
+#  endif
+#endif
+
+/* Reset the CRC variable */
+PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY);
+
+/* Write the "data" buffer to whatever output you are using */
+PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,
+    png_const_bytep data, png_size_t length),PNG_EMPTY);
+
+/* Read and check the PNG file signature */
+PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,
+   png_inforp info_ptr),PNG_EMPTY);
+
+/* Read the chunk header (length + type name) */
+PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr),
+   PNG_EMPTY);
+
+/* Read data from whatever input you are using into the "data" buffer */
+PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,
+    png_size_t length),PNG_EMPTY);
+
+/* Read bytes into buf, and update png_ptr->crc */
+PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
+    png_uint_32 length),PNG_EMPTY);
+
+/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
+PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
+   png_uint_32 skip),PNG_EMPTY);
+
+/* Read the CRC from the file and compare it to the libpng calculated CRC */
+PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY);
+
+/* Calculate the CRC over a section of data.  Note that we are only
+ * passing a maximum of 64K on systems that have this as a memory limit,
+ * since this is the maximum buffer size we can specify.
+ */
+PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
+   png_const_bytep ptr, png_size_t length),PNG_EMPTY);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
+#endif
+
+/* Write various chunks */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.
+ */
+PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
+   png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
+   int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
+   png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
+   png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
+   PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
+
+#ifdef PNG_WRITE_gAMA_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr,
+    png_fixed_point file_gamma),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_sBIT_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr,
+    png_const_color_8p sbit, int color_type),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_cHRM_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,
+    const png_xy *xy), PNG_EMPTY);
+    /* The xy value must have been previously validated */
+#endif
+
+#ifdef PNG_WRITE_sRGB_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr,
+    int intent),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_iCCP_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
+   png_const_charp name, png_const_bytep profile), PNG_EMPTY);
+   /* The profile must have been previously validated for correctness, the
+    * length comes from the first four bytes.  Only the base, deflate,
+    * compression is supported.
+    */
+#endif
+
+#ifdef PNG_WRITE_sPLT_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr,
+    png_const_sPLT_tp palette),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr,
+    png_const_bytep trans, png_const_color_16p values, int number,
+    int color_type),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_bKGD_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr,
+    png_const_color_16p values, int color_type),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_hIST_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,
+    png_const_uint_16p hist, int num_hist),PNG_EMPTY);
+#endif
+
+/* Chunks that have keywords */
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
+   png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp
+    key, png_const_charp text, int compression),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr,
+    int compression, png_const_charp key, png_const_charp lang,
+    png_const_charp lang_key, png_const_charp text),PNG_EMPTY);
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED  /* Added at version 1.0.14 and 1.2.4 */
+PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_oFFs_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr,
+    png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_pCAL_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr,
+    png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
+    png_const_charp units, png_charpp params),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_pHYs_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr,
+    png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
+    int unit_type),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_tIME_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr,
+    png_const_timep mod_time),PNG_EMPTY);
+#endif
+
+#ifdef PNG_WRITE_sCAL_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
+    int unit, png_const_charp width, png_const_charp height),PNG_EMPTY);
+#endif
+
+/* Called when finished processing a row of data */
+PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr),
+    PNG_EMPTY);
+
+/* Internal use only.   Called before first row of data */
+PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
+    PNG_EMPTY);
+
+/* Combine a row of data, dealing with alpha, etc. if requested.  'row' is an
+ * array of png_ptr->width pixels.  If the image is not interlaced or this
+ * is the final pass this just does a memcpy, otherwise the "display" flag
+ * is used to determine whether to copy pixels that are not in the current pass.
+ *
+ * Because 'png_do_read_interlace' (below) replicates pixels this allows this
+ * function to achieve the documented 'blocky' appearance during interlaced read
+ * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row'
+ * are not changed if they are not in the current pass, when display is 0.
+ *
+ * 'display' must be 0 or 1, otherwise the memcpy will be done regardless.
+ *
+ * The API always reads from the png_struct row buffer and always assumes that
+ * it is full width (png_do_read_interlace has already been called.)
+ *
+ * This function is only ever used to write to row buffers provided by the
+ * caller of the relevant libpng API and the row must have already been
+ * transformed by the read transformations.
+ *
+ * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed
+ * bitmasks for use within the code, otherwise runtime generated masks are used.
+ * The default is compile time masks.
+ */
+#ifndef PNG_USE_COMPILE_TIME_MASKS
+#  define PNG_USE_COMPILE_TIME_MASKS 1
+#endif
+PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
+    png_bytep row, int display),PNG_EMPTY);
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+/* Expand an interlaced row: the 'row_info' describes the pass data that has
+ * been read in and must correspond to the pixels in 'row', the pixels are
+ * expanded (moved apart) in 'row' to match the final layout, when doing this
+ * the pixels are *replicated* to the intervening space.  This is essential for
+ * the correct operation of png_combine_row, above.
+ */
+PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info,
+    png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY);
+#endif
+
+/* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+/* Grab pixels out of a row for an interlaced pass */
+PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info,
+    png_bytep row, int pass),PNG_EMPTY);
+#endif
+
+/* Unfilter a row: check the filter value before calling this, there is no point
+ * calling it for PNG_FILTER_VALUE_NONE.
+ */
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,
+    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
+    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+
+/* Choose the best filter to use and filter the row data */
+PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
+    png_row_infop row_info),PNG_EMPTY);
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
+   png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
+   /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer
+    * is NULL the function checks, instead, for the end of the stream.  In this
+    * case a benign error will be issued if the stream end is not found or if
+    * extra data has to be consumed.
+    */
+PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
+   PNG_EMPTY);
+   /* This cleans up when the IDAT LZ stream does not end when the last image
+    * byte is read; there is still some pending input.
+    */
+
+PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
+   PNG_EMPTY);
+   /* Finish a row while reading, dealing with interlacing passes, etc. */
+#endif /* SEQUENTIAL_READ */
+
+/* Initialize the row buffers, etc. */
+PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
+
+#if PNG_ZLIB_VERNUM >= 0x1240
+PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
+      PNG_EMPTY);
+#  define PNG_INFLATE(pp, flush) png_zlib_inflate(pp, flush)
+#else /* Zlib < 1.2.4 */
+#  define PNG_INFLATE(pp, flush) inflate(&(pp)->zstream, flush)
+#endif /* Zlib < 1.2.4 */
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Optional call to update the users info structure */
+PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+#endif
+
+/* Shared transform functions, defined in pngtran.c */
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info,
+    png_bytep row, int at_start),PNG_EMPTY);
+#endif
+
+#ifdef PNG_16BIT_SUPPORTED
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info,
+    png_bytep row),PNG_EMPTY);
+#endif
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
+    defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info,
+    png_bytep row),PNG_EMPTY);
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info,
+    png_bytep row),PNG_EMPTY);
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,
+    png_bytep row),PNG_EMPTY);
+#endif
+
+/* The following decodes the appropriate chunks, and does error correction,
+ * then calls the appropriate callback for the chunk if it is valid.
+ */
+
+/* Decode the IHDR chunk */
+PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif /* READ_iCCP */
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif /* READ_sPLT */
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
+PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr,
+    png_uint_32 chunk_name),PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY);
+   /* This is the function that gets called for unknown chunks.  The 'keep'
+    * argument is either non-zero for a known chunk that has been set to be
+    * handled as unknown or zero for an unknown chunk.  By default the function
+    * just skips the chunk or errors out if it is critical.
+    */
+
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
+    defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
+PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
+    (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY);
+   /* Exactly as the API png_handle_as_unknown() except that the argument is a
+    * 32-bit chunk name, not a string.
+    */
+#endif /* READ_UNKNOWN_CHUNKS || HANDLE_AS_UNKNOWN */
+
+/* Handle the transformations for reading and writing */
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr,
+   png_row_infop row_info),PNG_EMPTY);
+#endif
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr,
+   png_row_infop row_info),PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr),
+    PNG_EMPTY);
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),
+    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,
+    png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
+    png_bytep buffer, png_size_t buffer_length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
+    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr,
+   png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,
+   png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
+   png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,
+     png_bytep row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),
+    PNG_EMPTY);
+#  ifdef PNG_READ_tEXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+#  endif
+#  ifdef PNG_READ_zTXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+#  endif
+#  ifdef PNG_READ_iTXt_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr,
+    png_inforp info_ptr),PNG_EMPTY);
+#  endif
+
+#endif /* PROGRESSIVE_READ */
+
+/* Added at libpng version 1.6.0 */
+#ifdef PNG_GAMMA_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr,
+    png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY);
+   /* Set the colorspace gamma with a value provided by the application or by
+    * the gAMA chunk on read.  The value will override anything set by an ICC
+    * profile.
+    */
+
+PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr,
+    png_inforp info_ptr), PNG_EMPTY);
+    /* Synchronize the info 'valid' flags with the colorspace */
+
+PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr,
+    png_inforp info_ptr), PNG_EMPTY);
+    /* Copy the png_struct colorspace to the info_struct and call the above to
+     * synchronize the flags.  Checks for NULL info_ptr and does nothing.
+     */
+#endif
+
+/* Added at libpng version 1.4.0 */
+#ifdef PNG_COLORSPACE_SUPPORTED
+/* These internal functions are for maintaining the colorspace structure within
+ * a png_info or png_struct (or, indeed, both).
+ */
+PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities,
+   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy,
+    int preferred), PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints,
+   (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ,
+    int preferred), PNG_EMPTY);
+
+#ifdef PNG_sRGB_SUPPORTED
+PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, int intent), PNG_EMPTY);
+   /* This does set the colorspace gAMA and cHRM values too, but doesn't set the
+    * flags to write them, if it returns false there was a problem and an error
+    * message has already been output (but the colorspace may still need to be
+    * synced to record the invalid flag).
+    */
+#endif /* sRGB */
+
+#ifdef PNG_iCCP_SUPPORTED
+PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_const_charp name,
+   png_uint_32 profile_length, png_const_bytep profile, int color_type),
+   PNG_EMPTY);
+   /* The 'name' is used for information only */
+
+/* Routines for checking parts of an ICC profile. */
+PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_const_charp name,
+   png_uint_32 profile_length), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_const_charp name,
+   png_uint_32 profile_length,
+   png_const_bytep profile /* first 132 bytes only */, int color_type),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_const_charp name,
+   png_uint_32 profile_length,
+   png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY);
+#ifdef PNG_sRGB_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,(
+   png_const_structrp png_ptr, png_colorspacerp colorspace,
+   png_const_bytep profile, uLong adler), PNG_EMPTY);
+   /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may
+    * be zero to indicate that it is not available.  It is used, if provided,
+    * as a fast check on the profile when checking to see if it is sRGB.
+    */
+#endif
+#endif /* iCCP */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients,
+   (png_structrp png_ptr), PNG_EMPTY);
+   /* Set the rgb_to_gray coefficients from the colorspace Y values */
+#endif /* READ_RGB_TO_GRAY */
+#endif /* COLORSPACE */
+
+/* Added at libpng version 1.4.0 */
+PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
+    png_uint_32 width, png_uint_32 height, int bit_depth,
+    int color_type, int interlace_type, int compression_type,
+    int filter_type),PNG_EMPTY);
+
+/* Added at libpng version 1.5.10 */
+#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
+    defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,
+   (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);
+#endif
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
+PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr,
+   png_const_charp name),PNG_NORETURN);
+#endif
+
+/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite
+ * the end.  Always leaves the buffer nul terminated.  Never errors out (and
+ * there is no error code.)
+ */
+PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,
+   size_t pos, png_const_charp string),PNG_EMPTY);
+
+/* Various internal functions to handle formatted warning messages, currently
+ * only implemented for warnings.
+ */
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Utility to dump an unsigned value into a buffer, given a start pointer and
+ * and end pointer (which should point just *beyond* the end of the buffer!)
+ * Returns the pointer to the start of the formatted string.  This utility only
+ * does unsigned values.
+ */
+PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,
+   png_charp end, int format, png_alloc_size_t number),PNG_EMPTY);
+
+/* Convenience macro that takes an array: */
+#define PNG_FORMAT_NUMBER(buffer,format,number) \
+   png_format_number(buffer, buffer + (sizeof buffer), format, number)
+
+/* Suggested size for a number buffer (enough for 64 bits and a sign!) */
+#define PNG_NUMBER_BUFFER_SIZE 24
+
+/* These are the integer formats currently supported, the name is formed from
+ * the standard printf(3) format string.
+ */
+#define PNG_NUMBER_FORMAT_u     1 /* chose unsigned API! */
+#define PNG_NUMBER_FORMAT_02u   2
+#define PNG_NUMBER_FORMAT_d     1 /* chose signed API! */
+#define PNG_NUMBER_FORMAT_02d   2
+#define PNG_NUMBER_FORMAT_x     3
+#define PNG_NUMBER_FORMAT_02x   4
+#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */
+#endif
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* New defines and members adding in libpng-1.5.4 */
+#  define PNG_WARNING_PARAMETER_SIZE 32
+#  define PNG_WARNING_PARAMETER_COUNT 8 /* Maximum 9; see pngerror.c */
+
+/* An l-value of this type has to be passed to the APIs below to cache the
+ * values of the parameters to a formatted warning message.
+ */
+typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][
+   PNG_WARNING_PARAMETER_SIZE];
+
+PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p,
+   int number, png_const_charp string),PNG_EMPTY);
+   /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,
+    * including the trailing '\0'.
+    */
+PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned,
+   (png_warning_parameters p, int number, int format, png_alloc_size_t value),
+   PNG_EMPTY);
+   /* Use png_alloc_size_t because it is an unsigned type as big as any we
+    * need to output.  Use the following for a signed value.
+    */
+PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed,
+   (png_warning_parameters p, int number, int format, png_int_32 value),
+   PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,
+   png_warning_parameters p, png_const_charp message),PNG_EMPTY);
+   /* 'message' follows the X/Open approach of using @1, @2 to insert
+    * parameters previously supplied using the above functions.  Errors in
+    * specifying the parameters will simply result in garbage substitutions.
+    */
+#endif
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+/* Application errors (new in 1.6); use these functions (declared below) for
+ * errors in the parameters or order of API function calls on read.  The
+ * 'warning' should be used for an error that can be handled completely; the
+ * 'error' for one which can be handled safely but which may lose application
+ * information or settings.
+ *
+ * By default these both result in a png_error call prior to release, while in a
+ * released version the 'warning' is just a warning.  However if the application
+ * explicitly disables benign errors (explicitly permitting the code to lose
+ * information) they both turn into warnings.
+ *
+ * If benign errors aren't supported they end up as the corresponding base call
+ * (png_warning or png_error.)
+ */
+PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr,
+   png_const_charp message),PNG_EMPTY);
+   /* The application provided invalid parameters to an API function or called
+    * an API function at the wrong time, libpng can completely recover.
+    */
+
+PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,
+   png_const_charp message),PNG_EMPTY);
+   /* As above but libpng will ignore the call, or attempt some other partial
+    * recovery from the error.
+    */
+#else
+#  define png_app_warning(pp,s) png_warning(pp,s)
+#  define png_app_error(pp,s) png_error(pp,s)
+#endif
+
+PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
+   png_const_charp message, int error),PNG_EMPTY);
+   /* Report a recoverable issue in chunk data.  On read this is used to report
+    * a problem found while reading a particular chunk and the
+    * png_chunk_benign_error or png_chunk_warning function is used as
+    * appropriate.  On write this is used to report an error that comes from
+    * data set via an application call to a png_set_ API and png_app_error or
+    * png_app_warning is used as appropriate.
+    *
+    * The 'error' parameter must have one of the following values:
+    */
+#define PNG_CHUNK_WARNING     0 /* never an error */
+#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */
+#define PNG_CHUNK_ERROR       2 /* always an error */
+
+/* ASCII to FP interfaces, currently only implemented if sCAL
+ * support is required.
+ */
+#if defined(PNG_sCAL_SUPPORTED)
+/* MAX_DIGITS is actually the maximum number of characters in an sCAL
+ * width or height, derived from the precision (number of significant
+ * digits - a build time settable option) and assumptions about the
+ * maximum ridiculous exponent.
+ */
+#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/)
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr,
+   png_charp ascii, png_size_t size, double fp, unsigned int precision),
+   PNG_EMPTY);
+#endif /* FLOATING_POINT */
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
+   png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY);
+#endif /* FIXED_POINT */
+#endif /* sCAL */
+
+#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
+/* An internal API to validate the format of a floating point number.
+ * The result is the index of the next character.  If the number is
+ * not valid it will be the index of a character in the supposed number.
+ *
+ * The format of a number is defined in the PNG extensions specification
+ * and this API is strictly conformant to that spec, not anyone elses!
+ *
+ * The format as a regular expression is:
+ *
+ * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)?
+ *
+ * or:
+ *
+ * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)?
+ *
+ * The complexity is that either integer or fraction must be present and the
+ * fraction is permitted to have no digits only if the integer is present.
+ *
+ * NOTE: The dangling E problem.
+ *   There is a PNG valid floating point number in the following:
+ *
+ *       PNG floating point numbers are not greedy.
+ *
+ *   Working this out requires *TWO* character lookahead (because of the
+ *   sign), the parser does not do this - it will fail at the 'r' - this
+ *   doesn't matter for PNG sCAL chunk values, but it requires more care
+ *   if the value were ever to be embedded in something more complex.  Use
+ *   ANSI-C strtod if you need the lookahead.
+ */
+/* State table for the parser. */
+#define PNG_FP_INTEGER    0  /* before or in integer */
+#define PNG_FP_FRACTION   1  /* before or in fraction */
+#define PNG_FP_EXPONENT   2  /* before or in exponent */
+#define PNG_FP_STATE      3  /* mask for the above */
+#define PNG_FP_SAW_SIGN   4  /* Saw +/- in current state */
+#define PNG_FP_SAW_DIGIT  8  /* Saw a digit in current state */
+#define PNG_FP_SAW_DOT   16  /* Saw a dot in current state */
+#define PNG_FP_SAW_E     32  /* Saw an E (or e) in current state */
+#define PNG_FP_SAW_ANY   60  /* Saw any of the above 4 */
+
+/* These three values don't affect the parser.  They are set but not used.
+ */
+#define PNG_FP_WAS_VALID 64  /* Preceding substring is a valid fp number */
+#define PNG_FP_NEGATIVE 128  /* A negative number, including "-0" */
+#define PNG_FP_NONZERO  256  /* A non-zero value */
+#define PNG_FP_STICKY   448  /* The above three flags */
+
+/* This is available for the caller to store in 'state' if required.  Do not
+ * call the parser after setting it (the parser sometimes clears it.)
+ */
+#define PNG_FP_INVALID  512  /* Available for callers as a distinct value */
+
+/* Result codes for the parser (boolean - true meants ok, false means
+ * not ok yet.)
+ */
+#define PNG_FP_MAYBE      0  /* The number may be valid in the future */
+#define PNG_FP_OK         1  /* The number is valid */
+
+/* Tests on the sticky non-zero and negative flags.  To pass these checks
+ * the state must also indicate that the whole number is valid - this is
+ * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this
+ * is equivalent to PNG_FP_OK above.)
+ */
+#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO)
+   /* NZ_MASK: the string is valid and a non-zero negative value */
+#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO)
+   /* Z MASK: the string is valid and a non-zero value. */
+   /* PNG_FP_SAW_DIGIT: the string is valid. */
+#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT)
+#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK)
+#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK)
+
+/* The actual parser.  This can be called repeatedly. It updates
+ * the index into the string and the state variable (which must
+ * be initialized to 0).  It returns a result code, as above.  There
+ * is no point calling the parser any more if it fails to advance to
+ * the end of the string - it is stuck on an invalid character (or
+ * terminated by '\0').
+ *
+ * Note that the pointer will consume an E or even an E+ and then leave
+ * a 'maybe' state even though a preceding integer.fraction is valid.
+ * The PNG_FP_WAS_VALID flag indicates that a preceding substring was
+ * a valid number.  It's possible to recover from this by calling
+ * the parser again (from the start, with state 0) but with a string
+ * that omits the last character (i.e. set the size to the index of
+ * the problem character.)  This has not been tested within libpng.
+ */
+PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
+   png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY);
+
+/* This is the same but it checks a complete string and returns true
+ * only if it just contains a floating point number.  As of 1.5.4 this
+ * function also returns the state at the end of parsing the number if
+ * it was valid (otherwise it returns 0.)  This can be used for testing
+ * for negative or zero values using the sticky flag.
+ */
+PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
+   png_size_t size),PNG_EMPTY);
+#endif /* pCAL || sCAL */
+
+#if defined(PNG_GAMMA_SUPPORTED) ||\
+    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
+/* Added at libpng version 1.5.0 */
+/* This is a utility to provide a*times/div (rounded) and indicate
+ * if there is an overflow.  The result is a boolean - false (0)
+ * for overflow, true (1) if no overflow, in which case *res
+ * holds the result.
+ */
+PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,
+   png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
+/* Same deal, but issue a warning on overflow and return 0. */
+PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn,
+   (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by,
+   png_int_32 divided_by),PNG_EMPTY);
+#endif
+
+#ifdef PNG_GAMMA_SUPPORTED
+/* Calculate a reciprocal - used for gamma values.  This returns
+ * 0 if the argument is 0 in order to maintain an undefined value;
+ * there are no warnings.
+ */
+PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
+   PNG_EMPTY);
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* The same but gives a reciprocal of the product of two fixed point
+ * values.  Accuracy is suitable for gamma calculations but this is
+ * not exact - use png_muldiv for that.  Only required at present on read.
+ */
+PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,
+   png_fixed_point b),PNG_EMPTY);
+#endif
+
+/* Return true if the gamma value is significantly different from 1.0 */
+PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
+   PNG_EMPTY);
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Internal fixed point gamma correction.  These APIs are called as
+ * required to convert single values - they don't need to be fast,
+ * they are not used when processing image pixel values.
+ *
+ * While the input is an 'unsigned' value it must actually be the
+ * correct bit value - 0..255 or 0..65535 as required.
+ */
+PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr,
+   unsigned int value, png_fixed_point gamma_value),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value,
+   png_fixed_point gamma_value),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value,
+   png_fixed_point gamma_value),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr,
+   int bit_depth),PNG_EMPTY);
+#endif
+
+/* SIMPLIFIED READ/WRITE SUPPORT */
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
+   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+/* The internal structure that png_image::opaque points to. */
+typedef struct png_control
+{
+   png_structp png_ptr;
+   png_infop   info_ptr;
+   png_voidp   error_buf;           /* Always a jmp_buf at present. */
+
+   png_const_bytep memory;          /* Memory buffer. */
+   png_size_t      size;            /* Size of the memory buffer. */
+
+   unsigned int for_write       :1; /* Otherwise it is a read structure */
+   unsigned int owned_file      :1; /* We own the file in io_ptr */
+} png_control;
+
+/* Return the pointer to the jmp_buf from a png_control: necessary because C
+ * does not reveal the type of the elements of jmp_buf.
+ */
+#ifdef __cplusplus
+#  define png_control_jmp_buf(pc) (((jmp_buf*)((pc)->error_buf))[0])
+#else
+#  define png_control_jmp_buf(pc) ((pc)->error_buf)
+#endif
+
+/* Utility to safely execute a piece of libpng code catching and logging any
+ * errors that might occur.  Returns true on success, false on failure (either
+ * of the function or as a result of a png_error.)
+ */
+PNG_INTERNAL_CALLBACK(void,png_safe_error,(png_structp png_ptr,
+   png_const_charp error_message),PNG_NORETURN);
+
+#ifdef PNG_WARNINGS_SUPPORTED
+PNG_INTERNAL_CALLBACK(void,png_safe_warning,(png_structp png_ptr,
+   png_const_charp warning_message),PNG_EMPTY);
+#else
+#  define png_safe_warning 0/*dummy argument*/
+#endif
+
+PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image,
+   int (*function)(png_voidp), png_voidp arg),PNG_EMPTY);
+
+/* Utility to log an error; this also cleans up the png_image; the function
+ * always returns 0 (false).
+ */
+PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image,
+   png_const_charp error_message),PNG_EMPTY);
+
+#ifndef PNG_SIMPLIFIED_READ_SUPPORTED
+/* png_image_free is used by the write code but not exported */
+PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
+#endif /* !SIMPLIFIED_READ */
+
+#endif /* SIMPLIFIED READ/WRITE */
+
+/* These are initialization functions for hardware specific PNG filter
+ * optimizations; list these here then select the appropriate one at compile
+ * time using the macro PNG_FILTER_OPTIMIZATIONS.  If the macro is not defined
+ * the generic code is used.
+ */
+#ifdef PNG_FILTER_OPTIMIZATIONS
+PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,
+   unsigned int bpp), PNG_EMPTY);
+   /* Just declare the optimization that will be used */
+#else
+   /* List *all* the possible optimizations here - this branch is required if
+    * the builder of libpng passes the definition of PNG_FILTER_OPTIMIZATIONS in
+    * CFLAGS in place of CPPFLAGS *and* uses symbol prefixing.
+    */
+PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
+   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+#endif
+
+/* Maintainer: Put new private prototypes here ^ */
+
+#include "pngdebug.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNG_VERSION_INFO_ONLY */
+#endif /* PNGPRIV_H */
diff --git a/third_party/lpng_v163/pngread.c b/third_party/libpng/pngread.c
similarity index 88%
rename from third_party/lpng_v163/pngread.c
rename to third_party/libpng/pngread.c
index b30c5a1..9cb4d2e 100644
--- a/third_party/lpng_v163/pngread.c
+++ b/third_party/libpng/pngread.c
@@ -1,3999 +1,4136 @@
-/* pngread.c - read a PNG file

- *

- * Last changed in libpng 1.6.1 [March 28, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file contains routines that an application calls directly to

- * read a PNG file or stream.

- */

-

-#include "pngpriv.h"

-#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)

-#  include <errno.h>

-#endif

-

-#ifdef PNG_READ_SUPPORTED

-

-/* Create a PNG structure for reading, and allocate any memory needed. */

-PNG_FUNCTION(png_structp,PNGAPI

-png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,

-    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)

-{

-#ifndef PNG_USER_MEM_SUPPORTED

-   png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,

-      error_fn, warn_fn, NULL, NULL, NULL);

-#else

-   return png_create_read_struct_2(user_png_ver, error_ptr, error_fn,

-       warn_fn, NULL, NULL, NULL);

-}

-

-/* Alternate create PNG structure for reading, and allocate any memory

- * needed.

- */

-PNG_FUNCTION(png_structp,PNGAPI

-png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,

-    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,

-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)

-{

-   png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,

-      error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);

-#endif /* PNG_USER_MEM_SUPPORTED */

-

-   if (png_ptr != NULL)

-   {

-      png_ptr->mode = PNG_IS_READ_STRUCT;

-

-      /* Added in libpng-1.6.0; this can be used to detect a read structure if

-       * required (it will be zero in a write structure.)

-       */

-#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-         png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE;

-#     endif

-

-#     ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED

-         png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;

-

-         /* In stable builds only warn if an application error can be completely

-          * handled.

-          */

-#        if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC

-            png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;

-#        endif

-#     endif

-

-      /* TODO: delay this, it can be done in png_init_io (if the app doesn't

-       * do it itself) avoiding setting the default function if it is not

-       * required.

-       */

-      png_set_read_fn(png_ptr, NULL, NULL);

-   }

-

-   return png_ptr;

-}

-

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-/* Read the information before the actual image data.  This has been

- * changed in v0.90 to allow reading a file that already has the magic

- * bytes read from the stream.  You can tell libpng how many bytes have

- * been read from the beginning of the stream (up to the maximum of 8)

- * via png_set_sig_bytes(), and we will only check the remaining bytes

- * here.  The application can then have access to the signature bytes we

- * read if it is determined that this isn't a valid PNG file.

- */

-void PNGAPI

-png_read_info(png_structrp png_ptr, png_inforp info_ptr)

-{

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-   int keep;

-#endif

-

-   png_debug(1, "in png_read_info");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   /* Read and check the PNG file signature. */

-   png_read_sig(png_ptr, info_ptr);

-

-   for (;;)

-   {

-      png_uint_32 length = png_read_chunk_header(png_ptr);

-      png_uint_32 chunk_name = png_ptr->chunk_name;

-

-      /* IDAT logic needs to happen here to simplify getting the two flags

-       * right.

-       */

-      if (chunk_name == png_IDAT)

-      {

-         if (!(png_ptr->mode & PNG_HAVE_IHDR))

-            png_chunk_error(png_ptr, "Missing IHDR before IDAT");

-

-         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&

-             !(png_ptr->mode & PNG_HAVE_PLTE))

-            png_chunk_error(png_ptr, "Missing PLTE before IDAT");

-

-         else if (png_ptr->mode & PNG_AFTER_IDAT)

-            png_chunk_benign_error(png_ptr, "Too many IDATs found");

-

-         png_ptr->mode |= PNG_HAVE_IDAT;

-      }

-

-      else if (png_ptr->mode & PNG_HAVE_IDAT)

-         png_ptr->mode |= PNG_AFTER_IDAT;

-

-      /* This should be a binary subdivision search or a hash for

-       * matching the chunk name rather than a linear search.

-       */

-      if (chunk_name == png_IHDR)

-         png_handle_IHDR(png_ptr, info_ptr, length);

-

-      else if (chunk_name == png_IEND)

-         png_handle_IEND(png_ptr, info_ptr, length);

-

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-      else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)

-      {

-         png_handle_unknown(png_ptr, info_ptr, length, keep);

-

-         if (chunk_name == png_PLTE)

-            png_ptr->mode |= PNG_HAVE_PLTE;

-

-         else if (chunk_name == png_IDAT)

-         {

-            png_ptr->idat_size = 0; /* It has been consumed */

-            break;

-         }

-      }

-#endif

-      else if (chunk_name == png_PLTE)

-         png_handle_PLTE(png_ptr, info_ptr, length);

-

-      else if (chunk_name == png_IDAT)

-      {

-         png_ptr->idat_size = length;

-         break;

-      }

-

-#ifdef PNG_READ_bKGD_SUPPORTED

-      else if (chunk_name == png_bKGD)

-         png_handle_bKGD(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_cHRM_SUPPORTED

-      else if (chunk_name == png_cHRM)

-         png_handle_cHRM(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_gAMA_SUPPORTED

-      else if (chunk_name == png_gAMA)

-         png_handle_gAMA(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_hIST_SUPPORTED

-      else if (chunk_name == png_hIST)

-         png_handle_hIST(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_oFFs_SUPPORTED

-      else if (chunk_name == png_oFFs)

-         png_handle_oFFs(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_pCAL_SUPPORTED

-      else if (chunk_name == png_pCAL)

-         png_handle_pCAL(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sCAL_SUPPORTED

-      else if (chunk_name == png_sCAL)

-         png_handle_sCAL(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_pHYs_SUPPORTED

-      else if (chunk_name == png_pHYs)

-         png_handle_pHYs(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sBIT_SUPPORTED

-      else if (chunk_name == png_sBIT)

-         png_handle_sBIT(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sRGB_SUPPORTED

-      else if (chunk_name == png_sRGB)

-         png_handle_sRGB(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_iCCP_SUPPORTED

-      else if (chunk_name == png_iCCP)

-         png_handle_iCCP(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sPLT_SUPPORTED

-      else if (chunk_name == png_sPLT)

-         png_handle_sPLT(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_tEXt_SUPPORTED

-      else if (chunk_name == png_tEXt)

-         png_handle_tEXt(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_tIME_SUPPORTED

-      else if (chunk_name == png_tIME)

-         png_handle_tIME(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_tRNS_SUPPORTED

-      else if (chunk_name == png_tRNS)

-         png_handle_tRNS(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_zTXt_SUPPORTED

-      else if (chunk_name == png_zTXt)

-         png_handle_zTXt(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_iTXt_SUPPORTED

-      else if (chunk_name == png_iTXt)

-         png_handle_iTXt(png_ptr, info_ptr, length);

-#endif

-

-      else

-         png_handle_unknown(png_ptr, info_ptr, length,

-            PNG_HANDLE_CHUNK_AS_DEFAULT);

-   }

-}

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-/* Optional call to update the users info_ptr structure */

-void PNGAPI

-png_read_update_info(png_structrp png_ptr, png_inforp info_ptr)

-{

-   png_debug(1, "in png_read_update_info");

-

-   if (png_ptr != NULL)

-   {

-      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)

-      {

-         png_read_start_row(png_ptr);

-

-#        ifdef PNG_READ_TRANSFORMS_SUPPORTED

-            png_read_transform_info(png_ptr, info_ptr);

-#        else

-            PNG_UNUSED(info_ptr)

-#        endif

-      }

-

-      /* New in 1.6.0 this avoids the bug of doing the initializations twice */

-      else

-         png_app_error(png_ptr,

-            "png_read_update_info/png_start_read_image: duplicate call");

-   }

-}

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-/* Initialize palette, background, etc, after transformations

- * are set, but before any reading takes place.  This allows

- * the user to obtain a gamma-corrected palette, for example.

- * If the user doesn't call this, we will do it ourselves.

- */

-void PNGAPI

-png_start_read_image(png_structrp png_ptr)

-{

-   png_debug(1, "in png_start_read_image");

-

-   if (png_ptr != NULL)

-   {

-      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)

-         png_read_start_row(png_ptr);

-

-      /* New in 1.6.0 this avoids the bug of doing the initializations twice */

-      else

-         png_app_error(png_ptr,

-            "png_start_read_image/png_read_update_info: duplicate call");

-   }

-}

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-void PNGAPI

-png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)

-{

-   png_row_info row_info;

-

-   if (png_ptr == NULL)

-      return;

-

-   png_debug2(1, "in png_read_row (row %lu, pass %d)",

-       (unsigned long)png_ptr->row_number, png_ptr->pass);

-

-   /* png_read_start_row sets the information (in particular iwidth) for this

-    * interlace pass.

-    */

-   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))

-      png_read_start_row(png_ptr);

-

-   /* 1.5.6: row_info moved out of png_struct to a local here. */

-   row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */

-   row_info.color_type = png_ptr->color_type;

-   row_info.bit_depth = png_ptr->bit_depth;

-   row_info.channels = png_ptr->channels;

-   row_info.pixel_depth = png_ptr->pixel_depth;

-   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);

-

-   if (png_ptr->row_number == 0 && png_ptr->pass == 0)

-   {

-   /* Check for transforms that have been set but were defined out */

-#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)

-   if (png_ptr->transformations & PNG_INVERT_MONO)

-      png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined");

-#endif

-

-#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)

-   if (png_ptr->transformations & PNG_FILLER)

-      png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined");

-#endif

-

-#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \

-    !defined(PNG_READ_PACKSWAP_SUPPORTED)

-   if (png_ptr->transformations & PNG_PACKSWAP)

-      png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined");

-#endif

-

-#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)

-   if (png_ptr->transformations & PNG_PACK)

-      png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined");

-#endif

-

-#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)

-   if (png_ptr->transformations & PNG_SHIFT)

-      png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined");

-#endif

-

-#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)

-   if (png_ptr->transformations & PNG_BGR)

-      png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined");

-#endif

-

-#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)

-   if (png_ptr->transformations & PNG_SWAP_BYTES)

-      png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined");

-#endif

-   }

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   /* If interlaced and we do not need a new row, combine row and return.

-    * Notice that the pixels we have from previous rows have been transformed

-    * already; we can only combine like with like (transformed or

-    * untransformed) and, because of the libpng API for interlaced images, this

-    * means we must transform before de-interlacing.

-    */

-   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))

-   {

-      switch (png_ptr->pass)

-      {

-         case 0:

-            if (png_ptr->row_number & 0x07)

-            {

-               if (dsp_row != NULL)

-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 1:

-            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)

-            {

-               if (dsp_row != NULL)

-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);

-

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 2:

-            if ((png_ptr->row_number & 0x07) != 4)

-            {

-               if (dsp_row != NULL && (png_ptr->row_number & 4))

-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);

-

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 3:

-            if ((png_ptr->row_number & 3) || png_ptr->width < 3)

-            {

-               if (dsp_row != NULL)

-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);

-

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 4:

-            if ((png_ptr->row_number & 3) != 2)

-            {

-               if (dsp_row != NULL && (png_ptr->row_number & 2))

-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);

-

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 5:

-            if ((png_ptr->row_number & 1) || png_ptr->width < 2)

-            {

-               if (dsp_row != NULL)

-                  png_combine_row(png_ptr, dsp_row, 1/*display*/);

-

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         default:

-         case 6:

-            if (!(png_ptr->row_number & 1))

-            {

-               png_read_finish_row(png_ptr);

-               return;

-            }

-            break;

-      }

-   }

-#endif

-

-   if (!(png_ptr->mode & PNG_HAVE_IDAT))

-      png_error(png_ptr, "Invalid attempt to read row data");

-

-   /* Fill the row with IDAT data: */

-   png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);

-

-   if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)

-   {

-      if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)

-         png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,

-            png_ptr->prev_row + 1, png_ptr->row_buf[0]);

-      else

-         png_error(png_ptr, "bad adaptive filter value");

-   }

-

-   /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before

-    * 1.5.6, while the buffer really is this big in current versions of libpng

-    * it may not be in the future, so this was changed just to copy the

-    * interlaced count:

-    */

-   memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&

-       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))

-   {

-      /* Intrapixel differencing */

-      png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1);

-   }

-#endif

-

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-   if (png_ptr->transformations)

-      png_do_read_transformations(png_ptr, &row_info);

-#endif

-

-   /* The transformed pixel depth should match the depth now in row_info. */

-   if (png_ptr->transformed_pixel_depth == 0)

-   {

-      png_ptr->transformed_pixel_depth = row_info.pixel_depth;

-      if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)

-         png_error(png_ptr, "sequential row overflow");

-   }

-

-   else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)

-      png_error(png_ptr, "internal sequential row size calculation error");

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   /* Blow up interlaced rows to full size */

-   if (png_ptr->interlaced &&

-      (png_ptr->transformations & PNG_INTERLACE))

-   {

-      if (png_ptr->pass < 6)

-         png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,

-            png_ptr->transformations);

-

-      if (dsp_row != NULL)

-         png_combine_row(png_ptr, dsp_row, 1/*display*/);

-

-      if (row != NULL)

-         png_combine_row(png_ptr, row, 0/*row*/);

-   }

-

-   else

-#endif

-   {

-      if (row != NULL)

-         png_combine_row(png_ptr, row, -1/*ignored*/);

-

-      if (dsp_row != NULL)

-         png_combine_row(png_ptr, dsp_row, -1/*ignored*/);

-   }

-   png_read_finish_row(png_ptr);

-

-   if (png_ptr->read_row_fn != NULL)

-      (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);

-

-}

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-/* Read one or more rows of image data.  If the image is interlaced,

- * and png_set_interlace_handling() has been called, the rows need to

- * contain the contents of the rows from the previous pass.  If the

- * image has alpha or transparency, and png_handle_alpha()[*] has been

- * called, the rows contents must be initialized to the contents of the

- * screen.

- *

- * "row" holds the actual image, and pixels are placed in it

- * as they arrive.  If the image is displayed after each pass, it will

- * appear to "sparkle" in.  "display_row" can be used to display a

- * "chunky" progressive image, with finer detail added as it becomes

- * available.  If you do not want this "chunky" display, you may pass

- * NULL for display_row.  If you do not want the sparkle display, and

- * you have not called png_handle_alpha(), you may pass NULL for rows.

- * If you have called png_handle_alpha(), and the image has either an

- * alpha channel or a transparency chunk, you must provide a buffer for

- * rows.  In this case, you do not have to provide a display_row buffer

- * also, but you may.  If the image is not interlaced, or if you have

- * not called png_set_interlace_handling(), the display_row buffer will

- * be ignored, so pass NULL to it.

- *

- * [*] png_handle_alpha() does not exist yet, as of this version of libpng

- */

-

-void PNGAPI

-png_read_rows(png_structrp png_ptr, png_bytepp row,

-    png_bytepp display_row, png_uint_32 num_rows)

-{

-   png_uint_32 i;

-   png_bytepp rp;

-   png_bytepp dp;

-

-   png_debug(1, "in png_read_rows");

-

-   if (png_ptr == NULL)

-      return;

-

-   rp = row;

-   dp = display_row;

-   if (rp != NULL && dp != NULL)

-      for (i = 0; i < num_rows; i++)

-      {

-         png_bytep rptr = *rp++;

-         png_bytep dptr = *dp++;

-

-         png_read_row(png_ptr, rptr, dptr);

-      }

-

-   else if (rp != NULL)

-      for (i = 0; i < num_rows; i++)

-      {

-         png_bytep rptr = *rp;

-         png_read_row(png_ptr, rptr, NULL);

-         rp++;

-      }

-

-   else if (dp != NULL)

-      for (i = 0; i < num_rows; i++)

-      {

-         png_bytep dptr = *dp;

-         png_read_row(png_ptr, NULL, dptr);

-         dp++;

-      }

-}

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-/* Read the entire image.  If the image has an alpha channel or a tRNS

- * chunk, and you have called png_handle_alpha()[*], you will need to

- * initialize the image to the current image that PNG will be overlaying.

- * We set the num_rows again here, in case it was incorrectly set in

- * png_read_start_row() by a call to png_read_update_info() or

- * png_start_read_image() if png_set_interlace_handling() wasn't called

- * prior to either of these functions like it should have been.  You can

- * only call this function once.  If you desire to have an image for

- * each pass of a interlaced image, use png_read_rows() instead.

- *

- * [*] png_handle_alpha() does not exist yet, as of this version of libpng

- */

-void PNGAPI

-png_read_image(png_structrp png_ptr, png_bytepp image)

-{

-   png_uint_32 i, image_height;

-   int pass, j;

-   png_bytepp rp;

-

-   png_debug(1, "in png_read_image");

-

-   if (png_ptr == NULL)

-      return;

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))

-   {

-      pass = png_set_interlace_handling(png_ptr);

-      /* And make sure transforms are initialized. */

-      png_start_read_image(png_ptr);

-   }

-   else

-   {

-      if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE))

-      {

-         /* Caller called png_start_read_image or png_read_update_info without

-          * first turning on the PNG_INTERLACE transform.  We can fix this here,

-          * but the caller should do it!

-          */

-         png_warning(png_ptr, "Interlace handling should be turned on when "

-            "using png_read_image");

-         /* Make sure this is set correctly */

-         png_ptr->num_rows = png_ptr->height;

-      }

-

-      /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in

-       * the above error case.

-       */

-      pass = png_set_interlace_handling(png_ptr);

-   }

-#else

-   if (png_ptr->interlaced)

-      png_error(png_ptr,

-          "Cannot read interlaced image -- interlace handler disabled");

-

-   pass = 1;

-#endif

-

-   image_height=png_ptr->height;

-

-   for (j = 0; j < pass; j++)

-   {

-      rp = image;

-      for (i = 0; i < image_height; i++)

-      {

-         png_read_row(png_ptr, *rp, NULL);

-         rp++;

-      }

-   }

-}

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-/* Read the end of the PNG file.  Will not read past the end of the

- * file, will verify the end is accurate, and will read any comments

- * or time information at the end of the file, if info is not NULL.

- */

-void PNGAPI

-png_read_end(png_structrp png_ptr, png_inforp info_ptr)

-{

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-   int keep;

-#endif

-

-   png_debug(1, "in png_read_end");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* If png_read_end is called in the middle of reading the rows there may

-    * still be pending IDAT data and an owned zstream.  Deal with this here.

-    */

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-   if (!png_chunk_unknown_handling(png_ptr, png_IDAT))

-#endif

-      png_read_finish_IDAT(png_ptr);

-

-#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED

-   /* Report invalid palette index; added at libng-1.5.10 */

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&

-      png_ptr->num_palette_max > png_ptr->num_palette)

-     png_benign_error(png_ptr, "Read palette index exceeding num_palette");

-#endif

-

-   do

-   {

-      png_uint_32 length = png_read_chunk_header(png_ptr);

-      png_uint_32 chunk_name = png_ptr->chunk_name;

-

-      if (chunk_name == png_IHDR)

-         png_handle_IHDR(png_ptr, info_ptr, length);

-

-      else if (chunk_name == png_IEND)

-         png_handle_IEND(png_ptr, info_ptr, length);

-

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-      else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)

-      {

-         if (chunk_name == png_IDAT)

-         {

-            if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))

-               png_benign_error(png_ptr, "Too many IDATs found");

-         }

-         png_handle_unknown(png_ptr, info_ptr, length, keep);

-         if (chunk_name == png_PLTE)

-            png_ptr->mode |= PNG_HAVE_PLTE;

-      }

-#endif

-

-      else if (chunk_name == png_IDAT)

-      {

-         /* Zero length IDATs are legal after the last IDAT has been

-          * read, but not after other chunks have been read.

-          */

-         if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))

-            png_benign_error(png_ptr, "Too many IDATs found");

-

-         png_crc_finish(png_ptr, length);

-      }

-      else if (chunk_name == png_PLTE)

-         png_handle_PLTE(png_ptr, info_ptr, length);

-

-#ifdef PNG_READ_bKGD_SUPPORTED

-      else if (chunk_name == png_bKGD)

-         png_handle_bKGD(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_cHRM_SUPPORTED

-      else if (chunk_name == png_cHRM)

-         png_handle_cHRM(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_gAMA_SUPPORTED

-      else if (chunk_name == png_gAMA)

-         png_handle_gAMA(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_hIST_SUPPORTED

-      else if (chunk_name == png_hIST)

-         png_handle_hIST(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_oFFs_SUPPORTED

-      else if (chunk_name == png_oFFs)

-         png_handle_oFFs(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_pCAL_SUPPORTED

-      else if (chunk_name == png_pCAL)

-         png_handle_pCAL(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sCAL_SUPPORTED

-      else if (chunk_name == png_sCAL)

-         png_handle_sCAL(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_pHYs_SUPPORTED

-      else if (chunk_name == png_pHYs)

-         png_handle_pHYs(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sBIT_SUPPORTED

-      else if (chunk_name == png_sBIT)

-         png_handle_sBIT(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sRGB_SUPPORTED

-      else if (chunk_name == png_sRGB)

-         png_handle_sRGB(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_iCCP_SUPPORTED

-      else if (chunk_name == png_iCCP)

-         png_handle_iCCP(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_sPLT_SUPPORTED

-      else if (chunk_name == png_sPLT)

-         png_handle_sPLT(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_tEXt_SUPPORTED

-      else if (chunk_name == png_tEXt)

-         png_handle_tEXt(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_tIME_SUPPORTED

-      else if (chunk_name == png_tIME)

-         png_handle_tIME(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_tRNS_SUPPORTED

-      else if (chunk_name == png_tRNS)

-         png_handle_tRNS(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_zTXt_SUPPORTED

-      else if (chunk_name == png_zTXt)

-         png_handle_zTXt(png_ptr, info_ptr, length);

-#endif

-

-#ifdef PNG_READ_iTXt_SUPPORTED

-      else if (chunk_name == png_iTXt)

-         png_handle_iTXt(png_ptr, info_ptr, length);

-#endif

-

-      else

-         png_handle_unknown(png_ptr, info_ptr, length,

-            PNG_HANDLE_CHUNK_AS_DEFAULT);

-   } while (!(png_ptr->mode & PNG_HAVE_IEND));

-}

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-/* Free all memory used in the read struct */

-static void

-png_read_destroy(png_structrp png_ptr)

-{

-   png_debug(1, "in png_read_destroy");

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   png_destroy_gamma_table(png_ptr);

-#endif

-

-   png_free(png_ptr, png_ptr->big_row_buf);

-   png_free(png_ptr, png_ptr->big_prev_row);

-   png_free(png_ptr, png_ptr->read_buffer);

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-   png_free(png_ptr, png_ptr->palette_lookup);

-   png_free(png_ptr, png_ptr->quantize_index);

-#endif

-

-   if (png_ptr->free_me & PNG_FREE_PLTE)

-      png_zfree(png_ptr, png_ptr->palette);

-   png_ptr->free_me &= ~PNG_FREE_PLTE;

-

-#if defined(PNG_tRNS_SUPPORTED) || \

-    defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)

-   if (png_ptr->free_me & PNG_FREE_TRNS)

-      png_free(png_ptr, png_ptr->trans_alpha);

-   png_ptr->free_me &= ~PNG_FREE_TRNS;

-#endif

-

-   inflateEnd(&png_ptr->zstream);

-

-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED

-   png_free(png_ptr, png_ptr->save_buffer);

-#endif

-

-#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) &&\

-   defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)

-   png_free(png_ptr, png_ptr->unknown_chunk.data);

-#endif

-

-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-   png_free(png_ptr, png_ptr->chunk_list);

-#endif

-

-   /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error

-    * callbacks are still set at this point.  They are required to complete the

-    * destruction of the png_struct itself.

-    */

-}

-

-/* Free all memory used by the read */

-void PNGAPI

-png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,

-    png_infopp end_info_ptr_ptr)

-{

-   png_structrp png_ptr = NULL;

-

-   png_debug(1, "in png_destroy_read_struct");

-

-   if (png_ptr_ptr != NULL)

-      png_ptr = *png_ptr_ptr;

-

-   if (png_ptr == NULL)

-      return;

-

-   /* libpng 1.6.0: use the API to destroy info structs to ensure consistent

-    * behavior.  Prior to 1.6.0 libpng did extra 'info' destruction in this API.

-    * The extra was, apparently, unnecessary yet this hides memory leak bugs.

-    */

-   png_destroy_info_struct(png_ptr, end_info_ptr_ptr);

-   png_destroy_info_struct(png_ptr, info_ptr_ptr);

-

-   *png_ptr_ptr = NULL;

-   png_read_destroy(png_ptr);

-   png_destroy_png_struct(png_ptr);

-}

-

-void PNGAPI

-png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->read_row_fn = read_row_fn;

-}

-

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-#ifdef PNG_INFO_IMAGE_SUPPORTED

-void PNGAPI

-png_read_png(png_structrp png_ptr, png_inforp info_ptr,

-                           int transforms,

-                           voidp params)

-{

-   int row;

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   /* png_read_info() gives us all of the information from the

-    * PNG file before the first IDAT (image data chunk).

-    */

-   png_read_info(png_ptr, info_ptr);

-   if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep)))

-      png_error(png_ptr, "Image is too high to process with png_read_png()");

-

-   /* -------------- image transformations start here ------------------- */

-

-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-   /* Tell libpng to strip 16-bit/color files down to 8 bits per color.

-    */

-   if (transforms & PNG_TRANSFORM_SCALE_16)

-   {

-     /* Added at libpng-1.5.4. "strip_16" produces the same result that it

-      * did in earlier versions, while "scale_16" is now more accurate.

-      */

-      png_set_scale_16(png_ptr);

-   }

-#endif

-

-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-   /* If both SCALE and STRIP are required pngrtran will effectively cancel the

-    * latter by doing SCALE first.  This is ok and allows apps not to check for

-    * which is supported to get the right answer.

-    */

-   if (transforms & PNG_TRANSFORM_STRIP_16)

-      png_set_strip_16(png_ptr);

-#endif

-

-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED

-   /* Strip alpha bytes from the input data without combining with

-    * the background (not recommended).

-    */

-   if (transforms & PNG_TRANSFORM_STRIP_ALPHA)

-      png_set_strip_alpha(png_ptr);

-#endif

-

-#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED)

-   /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single

-    * byte into separate bytes (useful for paletted and grayscale images).

-    */

-   if (transforms & PNG_TRANSFORM_PACKING)

-      png_set_packing(png_ptr);

-#endif

-

-#ifdef PNG_READ_PACKSWAP_SUPPORTED

-   /* Change the order of packed pixels to least significant bit first

-    * (not useful if you are using png_set_packing).

-    */

-   if (transforms & PNG_TRANSFORM_PACKSWAP)

-      png_set_packswap(png_ptr);

-#endif

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-   /* Expand paletted colors into true RGB triplets

-    * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel

-    * Expand paletted or RGB images with transparency to full alpha

-    * channels so the data will be available as RGBA quartets.

-    */

-   if (transforms & PNG_TRANSFORM_EXPAND)

-      if ((png_ptr->bit_depth < 8) ||

-          (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||

-          (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))

-         png_set_expand(png_ptr);

-#endif

-

-   /* We don't handle background color or gamma transformation or quantizing.

-    */

-

-#ifdef PNG_READ_INVERT_SUPPORTED

-   /* Invert monochrome files to have 0 as white and 1 as black

-    */

-   if (transforms & PNG_TRANSFORM_INVERT_MONO)

-      png_set_invert_mono(png_ptr);

-#endif

-

-#ifdef PNG_READ_SHIFT_SUPPORTED

-   /* If you want to shift the pixel values from the range [0,255] or

-    * [0,65535] to the original [0,7] or [0,31], or whatever range the

-    * colors were originally in:

-    */

-   if ((transforms & PNG_TRANSFORM_SHIFT)

-       && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))

-   {

-      png_color_8p sig_bit;

-

-      png_get_sBIT(png_ptr, info_ptr, &sig_bit);

-      png_set_shift(png_ptr, sig_bit);

-   }

-#endif

-

-#ifdef PNG_READ_BGR_SUPPORTED

-   /* Flip the RGB pixels to BGR (or RGBA to BGRA) */

-   if (transforms & PNG_TRANSFORM_BGR)

-      png_set_bgr(png_ptr);

-#endif

-

-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED

-   /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */

-   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)

-      png_set_swap_alpha(png_ptr);

-#endif

-

-#ifdef PNG_READ_SWAP_SUPPORTED

-   /* Swap bytes of 16-bit files to least significant byte first */

-   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)

-      png_set_swap(png_ptr);

-#endif

-

-/* Added at libpng-1.2.41 */

-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED

-   /* Invert the alpha channel from opacity to transparency */

-   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)

-      png_set_invert_alpha(png_ptr);

-#endif

-

-/* Added at libpng-1.2.41 */

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-   /* Expand grayscale image to RGB */

-   if (transforms & PNG_TRANSFORM_GRAY_TO_RGB)

-      png_set_gray_to_rgb(png_ptr);

-#endif

-

-/* Added at libpng-1.5.4 */

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-   if (transforms & PNG_TRANSFORM_EXPAND_16)

-      png_set_expand_16(png_ptr);

-#endif

-

-   /* We don't handle adding filler bytes */

-

-   /* We use png_read_image and rely on that for interlace handling, but we also

-    * call png_read_update_info therefore must turn on interlace handling now:

-    */

-   (void)png_set_interlace_handling(png_ptr);

-

-   /* Optional call to gamma correct and add the background to the palette

-    * and update info structure.  REQUIRED if you are expecting libpng to

-    * update the palette for you (i.e., you selected such a transform above).

-    */

-   png_read_update_info(png_ptr, info_ptr);

-

-   /* -------------- image transformations end here ------------------- */

-

-   png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);

-   if (info_ptr->row_pointers == NULL)

-   {

-      png_uint_32 iptr;

-

-      info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,

-          info_ptr->height * (sizeof (png_bytep)));

-      for (iptr=0; iptr<info_ptr->height; iptr++)

-         info_ptr->row_pointers[iptr] = NULL;

-

-      info_ptr->free_me |= PNG_FREE_ROWS;

-

-      for (row = 0; row < (int)info_ptr->height; row++)

-         info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,

-            png_get_rowbytes(png_ptr, info_ptr));

-   }

-

-   png_read_image(png_ptr, info_ptr->row_pointers);

-   info_ptr->valid |= PNG_INFO_IDAT;

-

-   /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */

-   png_read_end(png_ptr, info_ptr);

-

-   PNG_UNUSED(transforms)   /* Quiet compiler warnings */

-   PNG_UNUSED(params)

-

-}

-#endif /* PNG_INFO_IMAGE_SUPPORTED */

-#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */

-

-#ifdef PNG_SIMPLIFIED_READ_SUPPORTED

-/* SIMPLIFIED READ

- *

- * This code currently relies on the sequential reader, though it could easily

- * be made to work with the progressive one.

- */

-/* Arguments to png_image_finish_read: */

-

-/* Encoding of PNG data (used by the color-map code) */

-/* TODO: change these, dang, ANSI-C reserves the 'E' namespace. */

-#  define E_NOTSET  0 /* File encoding not yet known */

-#  define E_sRGB    1 /* 8-bit encoded to sRGB gamma */

-#  define E_LINEAR  2 /* 16-bit linear: not encoded, NOT pre-multiplied! */

-#  define E_FILE    3 /* 8-bit encoded to file gamma, not sRGB or linear */

-#  define E_LINEAR8 4 /* 8-bit linear: only from a file value */

-

-/* Color-map processing: after libpng has run on the PNG image further

- * processing may be needed to conver the data to color-map indicies.

- */

-#define PNG_CMAP_NONE      0

-#define PNG_CMAP_GA        1 /* Process GA data to a color-map with alpha */

-#define PNG_CMAP_TRANS     2 /* Process GA data to a background index */

-#define PNG_CMAP_RGB       3 /* Process RGB data */

-#define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */

-

-/* The following document where the background is for each processing case. */

-#define PNG_CMAP_NONE_BACKGROUND      256

-#define PNG_CMAP_GA_BACKGROUND        231

-#define PNG_CMAP_TRANS_BACKGROUND     254

-#define PNG_CMAP_RGB_BACKGROUND       256

-#define PNG_CMAP_RGB_ALPHA_BACKGROUND 216

-

-typedef struct

-{

-   /* Arguments: */

-   png_imagep image;

-   png_voidp  buffer;

-   png_int_32 row_stride;

-   png_voidp  colormap;

-   png_const_colorp background;

-   /* Local variables: */

-   png_voidp       local_row;

-   png_voidp       first_row;

-   ptrdiff_t       row_bytes;           /* step between rows */

-   int             file_encoding;       /* E_ values above */

-   png_fixed_point gamma_to_linear;     /* For E_FILE, reciprocal of gamma */

-   int             colormap_processing; /* PNG_CMAP_ values above */

-} png_image_read_control;

-

-/* Do all the *safe* initialization - 'safe' means that png_error won't be

- * called, so setting up the jmp_buf is not required.  This means that anything

- * called from here must *not* call png_malloc - it has to call png_malloc_warn

- * instead so that control is returned safely back to this routine.

- */

-static int

-png_image_read_init(png_imagep image)

-{

-   if (image->opaque == NULL)

-   {

-      png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image,

-          png_safe_error, png_safe_warning);

-

-      /* And set the rest of the structure to NULL to ensure that the various

-       * fields are consistent.

-       */

-      memset(image, 0, (sizeof *image));

-      image->version = PNG_IMAGE_VERSION;

-

-      if (png_ptr != NULL)

-      {

-         png_infop info_ptr = png_create_info_struct(png_ptr);

-

-         if (info_ptr != NULL)

-         {

-            png_controlp control = png_voidcast(png_controlp,

-               png_malloc_warn(png_ptr, (sizeof *control)));

-

-            if (control != NULL)

-            {

-               memset(control, 0, (sizeof *control));

-

-               control->png_ptr = png_ptr;

-               control->info_ptr = info_ptr;

-               control->for_write = 0;

-

-               image->opaque = control;

-               return 1;

-            }

-

-            /* Error clean up */

-            png_destroy_info_struct(png_ptr, &info_ptr);

-         }

-

-         png_destroy_read_struct(&png_ptr, NULL, NULL);

-      }

-

-      return png_image_error(image, "png_image_read: out of memory");

-   }

-

-   return png_image_error(image, "png_image_read: opaque pointer not NULL");

-}

-

-/* Utility to find the base format of a PNG file from a png_struct. */

-static png_uint_32

-png_image_format(png_structrp png_ptr)

-{

-   png_uint_32 format = 0;

-

-   if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)

-      format |= PNG_FORMAT_FLAG_COLOR;

-

-   if (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)

-      format |= PNG_FORMAT_FLAG_ALPHA;

-

-   /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS

-    * sets the png_struct fields; that's all we are interested in here.  The

-    * precise interaction with an app call to png_set_tRNS and PNG file reading

-    * is unclear.

-    */

-   else if (png_ptr->num_trans > 0)

-      format |= PNG_FORMAT_FLAG_ALPHA;

-

-   if (png_ptr->bit_depth == 16)

-      format |= PNG_FORMAT_FLAG_LINEAR;

-

-   if (png_ptr->color_type & PNG_COLOR_MASK_PALETTE)

-      format |= PNG_FORMAT_FLAG_COLORMAP;

-

-   return format;

-}

-

-/* Is the given gamma significantly different from sRGB?  The test is the same

- * one used in pngrtran.c when deciding whether to do gamma correction.  The

- * arithmetic optimizes the division by using the fact that the inverse of the

- * file sRGB gamma is 2.2

- */

-static int

-png_gamma_not_sRGB(png_fixed_point g)

-{

-   if (g < PNG_FP_1)

-   {

-      /* An uninitialized gamma is assumed to be sRGB for the simplified API. */

-      if (g == 0)

-         return 0;

-

-      return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */);

-   }

-

-   return 1;

-}

-

-/* Do the main body of a 'png_image_begin_read' function; read the PNG file

- * header and fill in all the information.  This is executed in a safe context,

- * unlike the init routine above.

- */

-static int

-png_image_read_header(png_voidp argument)

-{

-   png_imagep image = png_voidcast(png_imagep, argument);

-   png_structrp png_ptr = image->opaque->png_ptr;

-   png_inforp info_ptr = image->opaque->info_ptr;

-

-   png_set_benign_errors(png_ptr, 1/*warn*/);

-   png_read_info(png_ptr, info_ptr);

-

-   /* Do this the fast way; just read directly out of png_struct. */

-   image->width = png_ptr->width;

-   image->height = png_ptr->height;

-

-   {

-      png_uint_32 format = png_image_format(png_ptr);

-

-      image->format = format;

-

-#ifdef PNG_COLORSPACE_SUPPORTED

-      /* Does the colorspace match sRGB?  If there is no color endpoint

-       * (colorant) information assume yes, otherwise require the

-       * 'ENDPOINTS_MATCHE_sRGB' colorspace flag to have been set.  If the

-       * colorspace has been determined to be invalid ignore it.

-       */

-      if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags

-         & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB|

-            PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS))

-         image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB;

-#endif

-   }

-

-   /* We need the maximum number of entries regardless of the format the

-    * application sets here.

-    */

-   {

-      png_uint_32 cmap_entries;

-

-      switch (png_ptr->color_type)

-      {

-         case PNG_COLOR_TYPE_GRAY:

-            cmap_entries = 1U << png_ptr->bit_depth;

-            break;

-

-         case PNG_COLOR_TYPE_PALETTE:

-            cmap_entries = png_ptr->num_palette;

-            break;

-

-         default:

-            cmap_entries = 256;

-            break;

-      }

-

-      if (cmap_entries > 256)

-         cmap_entries = 256;

-

-      image->colormap_entries = cmap_entries;

-   }

-

-   return 1;

-}

-

-#ifdef PNG_STDIO_SUPPORTED

-int PNGAPI

-png_image_begin_read_from_stdio(png_imagep image, FILE* file)

-{

-   if (image != NULL && image->version == PNG_IMAGE_VERSION)

-   {

-      if (file != NULL)

-      {

-         if (png_image_read_init(image))

-         {

-            /* This is slightly evil, but png_init_io doesn't do anything other

-             * than this and we haven't changed the standard IO functions so

-             * this saves a 'safe' function.

-             */

-            image->opaque->png_ptr->io_ptr = file;

-            return png_safe_execute(image, png_image_read_header, image);

-         }

-      }

-

-      else

-         return png_image_error(image,

-            "png_image_begin_read_from_stdio: invalid argument");

-   }

-

-   else if (image != NULL)

-      return png_image_error(image,

-         "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");

-

-   return 0;

-}

-

-int PNGAPI

-png_image_begin_read_from_file(png_imagep image, const char *file_name)

-{

-   if (image != NULL && image->version == PNG_IMAGE_VERSION)

-   {

-      if (file_name != NULL)

-      {

-         FILE *fp = fopen(file_name, "rb");

-

-         if (fp != NULL)

-         {

-            if (png_image_read_init(image))

-            {

-               image->opaque->png_ptr->io_ptr = fp;

-               image->opaque->owned_file = 1;

-               return png_safe_execute(image, png_image_read_header, image);

-            }

-

-            /* Clean up: just the opened file. */

-            (void)fclose(fp);

-         }

-

-         else

-            return png_image_error(image, strerror(errno));

-      }

-

-      else

-         return png_image_error(image,

-            "png_image_begin_read_from_file: invalid argument");

-   }

-

-   else if (image != NULL)

-      return png_image_error(image,

-         "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");

-

-   return 0;

-}

-#endif /* PNG_STDIO_SUPPORTED */

-

-static void PNGCBAPI

-png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need)

-{

-   if (png_ptr != NULL)

-   {

-      png_imagep image = png_voidcast(png_imagep, png_ptr->io_ptr);

-      if (image != NULL)

-      {

-         png_controlp cp = image->opaque;

-         if (cp != NULL)

-         {

-            png_const_bytep memory = cp->memory;

-            png_size_t size = cp->size;

-

-            if (memory != NULL && size >= need)

-            {

-               memcpy(out, memory, need);

-               cp->memory = memory + need;

-               cp->size = size - need;

-               return;

-            }

-

-            png_error(png_ptr, "read beyond end of data");

-         }

-      }

-

-      png_error(png_ptr, "invalid memory read");

-   }

-}

-

-int PNGAPI png_image_begin_read_from_memory(png_imagep image,

-   png_const_voidp memory, png_size_t size)

-{

-   if (image != NULL && image->version == PNG_IMAGE_VERSION)

-   {

-      if (memory != NULL && size > 0)

-      {

-         if (png_image_read_init(image))

-         {

-            /* Now set the IO functions to read from the memory buffer and

-             * store it into io_ptr.  Again do this in-place to avoid calling a

-             * libpng function that requires error handling.

-             */

-            image->opaque->memory = png_voidcast(png_const_bytep, memory);

-            image->opaque->size = size;

-            image->opaque->png_ptr->io_ptr = image;

-            image->opaque->png_ptr->read_data_fn = png_image_memory_read;

-

-            return png_safe_execute(image, png_image_read_header, image);

-         }

-      }

-

-      else

-         return png_image_error(image,

-            "png_image_begin_read_from_memory: invalid argument");

-   }

-

-   else if (image != NULL)

-      return png_image_error(image,

-         "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");

-

-   return 0;

-}

-

-/* Utility function to skip chunks that are not used by the simplified image

- * read functions and an appropriate macro to call it.

- */

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-static void

-png_image_skip_unused_chunks(png_structrp png_ptr)

-{

-   /* Prepare the reader to ignore all recognized chunks whose data will not

-    * be used, i.e., all chunks recognized by libpng except for those

-    * involved in basic image reading:

-    *

-    *    IHDR, PLTE, IDAT, IEND

-    *

-    * Or image data handling:

-    *

-    *    tRNS, bKGD, gAMA, cHRM, sRGB, iCCP and sBIT.

-    *

-    * This provides a small performance improvement and eliminates any

-    * potential vulnerability to security problems in the unused chunks.

-    */

-   {

-         static PNG_CONST png_byte chunks_to_process[] = {

-            98,  75,  71,  68, '\0',  /* bKGD */

-            99,  72,  82,  77, '\0',  /* cHRM */

-           103,  65,  77,  65, '\0',  /* gAMA */

-           105,  67,  67,  80, '\0',  /* iCCP */

-           115,  66,  73,  84, '\0',  /* sBIT */

-           115,  82,  71,  66, '\0',  /* sRGB */

-           };

-

-       /* Ignore unknown chunks and all other chunks except for the

-        * IHDR, PLTE, tRNS, IDAT, and IEND chunks.

-        */

-       png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER,

-         NULL, -1);

-

-       /* But do not ignore image data handling chunks */

-       png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT,

-         chunks_to_process, (sizeof chunks_to_process)/5);

-    }

-}

-

-#  define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p)

-#else

-#  define PNG_SKIP_CHUNKS(p) ((void)0)

-#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */

-

-/* The following macro gives the exact rounded answer for all values in the

- * range 0..255 (it actually divides by 51.2, but the rounding still generates

- * the correct numbers 0..5

- */

-#define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8)

-

-/* Utility functions to make particular color-maps */

-static void

-set_file_encoding(png_image_read_control *display)

-{

-   png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma;

-   if (png_gamma_significant(g))

-   {

-      if (png_gamma_not_sRGB(g))

-      {

-         display->file_encoding = E_FILE;

-         display->gamma_to_linear = png_reciprocal(g);

-      }

-

-      else

-         display->file_encoding = E_sRGB;

-   }

-

-   else

-      display->file_encoding = E_LINEAR8;

-}

-

-static unsigned int

-decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding)

-{

-   if (encoding == E_FILE) /* double check */

-      encoding = display->file_encoding;

-

-   if (encoding == E_NOTSET) /* must be the file encoding */

-   {

-      set_file_encoding(display);

-      encoding = display->file_encoding;

-   }

-

-   switch (encoding)

-   {

-      case E_FILE:

-         value = png_gamma_16bit_correct(value*257, display->gamma_to_linear);

-         break;

-

-      case E_sRGB:

-         value = png_sRGB_table[value];

-         break;

-

-      case E_LINEAR:

-         break;

-

-      case E_LINEAR8:

-         value *= 257;

-         break;

-

-      default:

-         png_error(display->image->opaque->png_ptr,

-            "unexpected encoding (internal error)");

-         break;

-   }

-

-   return value;

-}

-

-static png_uint_32

-png_colormap_compose(png_image_read_control *display,

-   png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha,

-   png_uint_32 background, int encoding)

-{

-   /* The file value is composed on the background, the background has the given

-    * encoding and so does the result, the file is encoded with E_FILE and the

-    * file and alpha are 8-bit values.  The (output) encoding will always be

-    * E_LINEAR or E_sRGB.

-    */

-   png_uint_32 f = decode_gamma(display, foreground, foreground_encoding);

-   png_uint_32 b = decode_gamma(display, background, encoding);

-

-   /* The alpha is always an 8-bit value (it comes from the palette), the value

-    * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires.

-    */

-   f = f * alpha + b * (255-alpha);

-

-   if (encoding == E_LINEAR)

-   {

-      /* Scale to 65535; divide by 255, approximately (in fact this is extremely

-       * accurate, it divides by 255.00000005937181414556, with no overflow.)

-       */

-      f *= 257; /* Now scaled by 65535 */

-      f += f >> 16;

-      f = (f+32768) >> 16;

-   }

-

-   else /* E_sRGB */

-      f = PNG_sRGB_FROM_LINEAR(f);

-

-   return f;

-}

-

-/* NOTE: E_LINEAR values to this routine must be 16-bit, but E_FILE values must

- * be 8-bit.

- */

-static void

-png_create_colormap_entry(png_image_read_control *display,

-   png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,

-   png_uint_32 alpha, int encoding)

-{

-   png_imagep image = display->image;

-   const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) ?

-      E_LINEAR : E_sRGB;

-   const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 &&

-      (red != green || green != blue);

-

-   if (ip > 255)

-      png_error(image->opaque->png_ptr, "color-map index out of range");

-

-   /* Update the cache with whether the file gamma is significantly different

-    * from sRGB.

-    */

-   if (encoding == E_FILE)

-   {

-      if (display->file_encoding == E_NOTSET)

-         set_file_encoding(display);

-

-      /* Note that the cached value may be E_FILE too, but if it is then the

-       * gamma_to_linear member has been set.

-       */

-      encoding = display->file_encoding;

-   }

-

-   if (encoding == E_FILE)

-   {

-      png_fixed_point g = display->gamma_to_linear;

-

-      red = png_gamma_16bit_correct(red*257, g);

-      green = png_gamma_16bit_correct(green*257, g);

-      blue = png_gamma_16bit_correct(blue*257, g);

-

-      if (convert_to_Y || output_encoding == E_LINEAR)

-      {

-         alpha *= 257;

-         encoding = E_LINEAR;

-      }

-

-      else

-      {

-         red = PNG_sRGB_FROM_LINEAR(red * 255);

-         green = PNG_sRGB_FROM_LINEAR(green * 255);

-         blue = PNG_sRGB_FROM_LINEAR(blue * 255);

-         encoding = E_sRGB;

-      }

-   }

-

-   else if (encoding == E_LINEAR8)

-   {

-      /* This encoding occurs quite frequently in test cases because PngSuite

-       * includes a gAMA 1.0 chunk with most images.

-       */

-      red *= 257;

-      green *= 257;

-      blue *= 257;

-      alpha *= 257;

-      encoding = E_LINEAR;

-   }

-

-   else if (encoding == E_sRGB && (convert_to_Y || output_encoding == E_LINEAR))

-   {

-      /* The values are 8-bit sRGB values, but must be converted to 16-bit

-       * linear.

-       */

-      red = png_sRGB_table[red];

-      green = png_sRGB_table[green];

-      blue = png_sRGB_table[blue];

-      alpha *= 257;

-      encoding = E_LINEAR;

-   }

-

-   /* This is set if the color isn't gray but the output is. */

-   if (encoding == E_LINEAR)

-   {

-      if (convert_to_Y)

-      {

-         /* NOTE: these values are copied from png_do_rgb_to_gray */

-         png_uint_32 y = (png_uint_32)6968 * red  + (png_uint_32)23434 * green +

-            (png_uint_32)2366 * blue;

-

-         if (output_encoding == E_LINEAR)

-            y = (y + 16384) >> 15;

-

-         else

-         {

-            /* y is scaled by 32768, we need it scaled by 255: */

-            y = (y + 128) >> 8;

-            y *= 255;

-            y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7);

-            encoding = E_sRGB;

-         }

-

-         blue = red = green = y;

-      }

-

-      else if (output_encoding == E_sRGB)

-      {

-         red = PNG_sRGB_FROM_LINEAR(red * 255);

-         green = PNG_sRGB_FROM_LINEAR(green * 255);

-         blue = PNG_sRGB_FROM_LINEAR(blue * 255);

-         alpha = PNG_DIV257(alpha);

-         encoding = E_sRGB;

-      }

-   }

-

-   if (encoding != output_encoding)

-      png_error(image->opaque->png_ptr, "bad encoding (internal error)");

-

-   /* Store the value. */

-   {

-#     ifdef PNG_FORMAT_BGR_SUPPORTED

-         const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&

-            (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;

-#     else

-#        define afirst 0

-#     endif

-#     ifdef PNG_FORMAT_BGR_SUPPORTED

-         const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) ? 2 : 0;

-#     else

-#        define bgr 0

-#     endif

-

-      if (output_encoding == E_LINEAR)

-      {

-         png_uint_16p entry = png_voidcast(png_uint_16p, display->colormap);

-

-         entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);

-

-         /* The linear 16-bit values must be pre-multiplied by the alpha channel

-          * value, if less than 65535 (this is, effectively, composite on black

-          * if the alpha channel is removed.)

-          */

-         switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))

-         {

-            case 4:

-               entry[afirst ? 0 : 3] = (png_uint_16)alpha;

-               /* FALL THROUGH */

-

-            case 3:

-               if (alpha < 65535)

-               {

-                  if (alpha > 0)

-                  {

-                     blue = (blue * alpha + 32767U)/65535U;

-                     green = (green * alpha + 32767U)/65535U;

-                     red = (red * alpha + 32767U)/65535U;

-                  }

-

-                  else

-                     red = green = blue = 0;

-               }

-               entry[afirst + (2 ^ bgr)] = (png_uint_16)blue;

-               entry[afirst + 1] = (png_uint_16)green;

-               entry[afirst + bgr] = (png_uint_16)red;

-               break;

-

-            case 2:

-               entry[1 ^ afirst] = (png_uint_16)alpha;

-               /* FALL THROUGH */

-

-            case 1:

-               if (alpha < 65535)

-               {

-                  if (alpha > 0)

-                     green = (green * alpha + 32767U)/65535U;

-

-                  else

-                     green = 0;

-               }

-               entry[afirst] = (png_uint_16)green;

-               break;

-

-            default:

-               break;

-         }

-      }

-

-      else /* output encoding is E_sRGB */

-      {

-         png_bytep entry = png_voidcast(png_bytep, display->colormap);

-

-         entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);

-

-         switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))

-         {

-            case 4:

-               entry[afirst ? 0 : 3] = (png_byte)alpha;

-            case 3:

-               entry[afirst + (2 ^ bgr)] = (png_byte)blue;

-               entry[afirst + 1] = (png_byte)green;

-               entry[afirst + bgr] = (png_byte)red;

-               break;

-

-            case 2:

-               entry[1 ^ afirst] = (png_byte)alpha;

-            case 1:

-               entry[afirst] = (png_byte)green;

-               break;

-

-            default:

-               break;

-         }

-      }

-

-#     ifdef afirst

-#        undef afirst

-#     endif

-#     ifdef bgr

-#        undef bgr

-#     endif

-   }

-}

-

-static int

-make_gray_file_colormap(png_image_read_control *display)

-{

-   unsigned int i;

-

-   for (i=0; i<256; ++i)

-      png_create_colormap_entry(display, i, i, i, i, 255, E_FILE);

-

-   return i;

-}

-

-static int

-make_gray_colormap(png_image_read_control *display)

-{

-   unsigned int i;

-

-   for (i=0; i<256; ++i)

-      png_create_colormap_entry(display, i, i, i, i, 255, E_sRGB);

-

-   return i;

-}

-#define PNG_GRAY_COLORMAP_ENTRIES 256

-

-static int

-make_ga_colormap(png_image_read_control *display)

-{

-   unsigned int i, a;

-

-   /* Alpha is retained, the output will be a color-map with entries

-    * selected by six levels of alpha.  One transparent entry, 6 gray

-    * levels for all the intermediate alpha values, leaving 230 entries

-    * for the opaque grays.  The color-map entries are the six values

-    * [0..5]*51, the GA processing uses PNG_DIV51(value) to find the

-    * relevant entry.

-    *

-    * if (alpha > 229) // opaque

-    * {

-    *    // The 231 entries are selected to make the math below work:

-    *    base = 0;

-    *    entry = (231 * gray + 128) >> 8;

-    * }

-    * else if (alpha < 26) // transparent

-    * {

-    *    base = 231;

-    *    entry = 0;

-    * }

-    * else // partially opaque

-    * {

-    *    base = 226 + 6 * PNG_DIV51(alpha);

-    *    entry = PNG_DIV51(gray);

-    * }

-    */

-   i = 0;

-   while (i < 231)

-   {

-      unsigned int gray = (i * 256 + 115) / 231;

-      png_create_colormap_entry(display, i++, gray, gray, gray, 255, E_sRGB);

-   }

-

-   /* 255 is used here for the component values for consistency with the code

-    * that undoes premultiplication in pngwrite.c.

-    */

-   png_create_colormap_entry(display, i++, 255, 255, 255, 0, E_sRGB);

-

-   for (a=1; a<5; ++a)

-   {

-      unsigned int g;

-

-      for (g=0; g<6; ++g)

-         png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51,

-            E_sRGB);

-   }

-

-   return i;

-}

-

-#define PNG_GA_COLORMAP_ENTRIES 256

-

-static int

-make_rgb_colormap(png_image_read_control *display)

-{

-   unsigned int i, r;

-

-   /* Build a 6x6x6 opaque RGB cube */

-   for (i=r=0; r<6; ++r)

-   {

-      unsigned int g;

-

-      for (g=0; g<6; ++g)

-      {

-         unsigned int b;

-

-         for (b=0; b<6; ++b)

-            png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255,

-               E_sRGB);

-      }

-   }

-

-   return i;

-}

-

-#define PNG_RGB_COLORMAP_ENTRIES 216

-

-/* Return a palette index to the above palette given three 8-bit sRGB values. */

-#define PNG_RGB_INDEX(r,g,b) \

-   ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b)))

-

-static int

-png_image_read_colormap(png_voidp argument)

-{

-   png_image_read_control *display =

-      png_voidcast(png_image_read_control*, argument);

-   const png_imagep image = display->image;

-

-   const png_structrp png_ptr = image->opaque->png_ptr;

-   const png_uint_32 output_format = image->format;

-   const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) ?

-      E_LINEAR : E_sRGB;

-

-   unsigned int cmap_entries;

-   unsigned int output_processing;        /* Output processing option */

-   unsigned int data_encoding = E_NOTSET; /* Encoding libpng must produce */

-

-   /* Background information; the background color and the index of this color

-    * in the color-map if it exists (else 256).

-    */

-   unsigned int background_index = 256;

-   png_uint_32 back_r, back_g, back_b;

-

-   /* Flags to accumulate things that need to be done to the input. */

-   int expand_tRNS = 0;

-

-   /* Exclude the NYI feature of compositing onto a color-mapped buffer; it is

-    * very difficult to do, the results look awful, and it is difficult to see

-    * what possible use it is because the application can't control the

-    * color-map.

-    */

-   if (((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 ||

-         png_ptr->num_trans > 0) /* alpha in input */ &&

-      ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) /* no alpha in output */)

-   {

-      if (output_encoding == E_LINEAR) /* compose on black */

-         back_b = back_g = back_r = 0;

-

-      else if (display->background == NULL /* no way to remove it */)

-         png_error(png_ptr,

-            "a background color must be supplied to remove alpha/transparency");

-

-      /* Get a copy of the background color (this avoids repeating the checks

-       * below.)  The encoding is 8-bit sRGB or 16-bit linear, depending on the

-       * output format.

-       */

-      else

-      {

-         back_g = display->background->green;

-         if (output_format & PNG_FORMAT_FLAG_COLOR)

-         {

-            back_r = display->background->red;

-            back_b = display->background->blue;

-         }

-         else

-            back_b = back_r = back_g;

-      }

-   }

-

-   else if (output_encoding == E_LINEAR)

-      back_b = back_r = back_g = 65535;

-

-   else

-      back_b = back_r = back_g = 255;

-

-   /* Default the input file gamma if required - this is necessary because

-    * libpng assumes that if no gamma information is present the data is in the

-    * output format, but the simplified API deduces the gamma from the input

-    * format.

-    */

-   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0)

-   {

-      /* Do this directly, not using the png_colorspace functions, to ensure

-       * that it happens even if the colorspace is invalid (though probably if

-       * it is the setting will be ignored)  Note that the same thing can be

-       * achieved at the application interface with png_set_gAMA.

-       */

-      if (png_ptr->bit_depth == 16 &&

-         (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)

-         png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR;

-

-      else

-         png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE;

-

-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;

-   }

-

-   /* Decide what to do based on the PNG color type of the input data.  The

-    * utility function png_create_colormap_entry deals with most aspects of the

-    * output transformations; this code works out how to produce bytes of

-    * color-map entries from the original format.

-    */

-   switch (png_ptr->color_type)

-   {

-      case PNG_COLOR_TYPE_GRAY:

-         if (png_ptr->bit_depth <= 8)

-         {

-            /* There at most 256 colors in the output, regardless of

-             * transparency.

-             */

-            unsigned int step, i, val, trans = 256/*ignore*/, back_alpha = 0;

-

-            cmap_entries = 1U << png_ptr->bit_depth;

-            if (cmap_entries > image->colormap_entries)

-               png_error(png_ptr, "gray[8] color-map: too few entries");

-

-            step = 255 / (cmap_entries - 1);

-            output_processing = PNG_CMAP_NONE;

-

-            /* If there is a tRNS chunk then this either selects a transparent

-             * value or, if the output has no alpha, the background color.

-             */

-            if (png_ptr->num_trans > 0)

-            {

-               trans = png_ptr->trans_color.gray;

-

-               if ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0)

-                  back_alpha = output_encoding == E_LINEAR ? 65535 : 255;

-            }

-

-            /* png_create_colormap_entry just takes an RGBA and writes the

-             * corresponding color-map entry using the format from 'image',

-             * including the required conversion to sRGB or linear as

-             * appropriate.  The input values are always either sRGB (if the

-             * gamma correction flag is 0) or 0..255 scaled file encoded values

-             * (if the function must gamma correct them).

-             */

-            for (i=val=0; i<cmap_entries; ++i, val += step)

-            {

-               /* 'i' is a file value.  While this will result in duplicated

-                * entries for 8-bit non-sRGB encoded files it is necessary to

-                * have non-gamma corrected values to do tRNS handling.

-                */

-               if (i != trans)

-                  png_create_colormap_entry(display, i, val, val, val, 255,

-                     E_FILE/*8-bit with file gamma*/);

-

-               /* Else this entry is transparent.  The colors don't matter if

-                * there is an alpha channel (back_alpha == 0), but it does no

-                * harm to pass them in; the values are not set above so this

-                * passes in white.

-                *

-                * NOTE: this preserves the full precision of the application

-                * supplied background color when it is used.

-                */

-               else

-                  png_create_colormap_entry(display, i, back_r, back_g, back_b,

-                     back_alpha, output_encoding);

-            }

-

-            /* We need libpng to preserve the original encoding. */

-            data_encoding = E_FILE;

-

-            /* The rows from libpng, while technically gray values, are now also

-             * color-map indicies; however, they may need to be expanded to 1

-             * byte per pixel.  This is what png_set_packing does (i.e., it

-             * unpacks the bit values into bytes.)

-             */

-            if (png_ptr->bit_depth < 8)

-               png_set_packing(png_ptr);

-         }

-

-         else /* bit depth is 16 */

-         {

-            /* The 16-bit input values can be converted directly to 8-bit gamma

-             * encoded values; however, if a tRNS chunk is present 257 color-map

-             * entries are required.  This means that the extra entry requires

-             * special processing; add an alpha channel, sacrifice gray level

-             * 254 and convert transparent (alpha==0) entries to that.

-             *

-             * Use libpng to chop the data to 8 bits.  Convert it to sRGB at the

-             * same time to minimize quality loss.  If a tRNS chunk is present

-             * this means libpng must handle it too; otherwise it is impossible

-             * to do the exact match on the 16-bit value.

-             *

-             * If the output has no alpha channel *and* the background color is

-             * gray then it is possible to let libpng handle the substitution by

-             * ensuring that the corresponding gray level matches the background

-             * color exactly.

-             */

-            data_encoding = E_sRGB;

-

-            if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)

-               png_error(png_ptr, "gray[16] color-map: too few entries");

-

-            cmap_entries = make_gray_colormap(display);

-

-            if (png_ptr->num_trans > 0)

-            {

-               unsigned int back_alpha;

-

-               if (output_format & PNG_FORMAT_FLAG_ALPHA)

-                  back_alpha = 0;

-

-               else

-               {

-                  if (back_r == back_g && back_g == back_b)

-                  {

-                     /* Background is gray; no special processing will be

-                      * required.

-                      */

-                     png_color_16 c;

-                     png_uint_32 gray = back_g;

-

-                     if (output_encoding == E_LINEAR)

-                     {

-                        gray = PNG_sRGB_FROM_LINEAR(gray * 255);

-

-                        /* And make sure the corresponding palette entry

-                         * matches.

-                         */

-                        png_create_colormap_entry(display, gray, back_g, back_g,

-                           back_g, 65535, E_LINEAR);

-                     }

-

-                     /* The background passed to libpng, however, must be the

-                      * sRGB value.

-                      */

-                     c.index = 0; /*unused*/

-                     c.gray = c.red = c.green = c.blue = (png_uint_16)gray;

-

-                     /* NOTE: does this work without expanding tRNS to alpha?

-                      * It should be the color->gray case below apparently

-                      * doesn't.

-                      */

-                     png_set_background_fixed(png_ptr, &c,

-                        PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,

-                        0/*gamma: not used*/);

-

-                     output_processing = PNG_CMAP_NONE;

-                     break;

-                  }

-

-                  back_alpha = output_encoding == E_LINEAR ? 65535 : 255;

-               }

-

-               /* output_processing means that the libpng-processed row will be

-                * 8-bit GA and it has to be processing to single byte color-map

-                * values.  Entry 254 is replaced by either a completely

-                * transparent entry or by the background color at full

-                * precision (and the background color is not a simple gray leve

-                * in this case.)

-                */

-               expand_tRNS = 1;

-               output_processing = PNG_CMAP_TRANS;

-               background_index = 254;

-

-               /* And set (overwrite) color-map entry 254 to the actual

-                * background color at full precision.

-                */

-               png_create_colormap_entry(display, 254, back_r, back_g, back_b,

-                  back_alpha, output_encoding);

-            }

-

-            else

-               output_processing = PNG_CMAP_NONE;

-         }

-         break;

-

-      case PNG_COLOR_TYPE_GRAY_ALPHA:

-         /* 8-bit or 16-bit PNG with two channels - gray and alpha.  A minimum

-          * of 65536 combinations.  If, however, the alpha channel is to be

-          * removed there are only 256 possibilities if the background is gray.

-          * (Otherwise there is a subset of the 65536 possibilities defined by

-          * the triangle between black, white and the background color.)

-          *

-          * Reduce 16-bit files to 8-bit and sRGB encode the result.  No need to

-          * worry about tRNS matching - tRNS is ignored if there is an alpha

-          * channel.

-          */

-         data_encoding = E_sRGB;

-

-         if (output_format & PNG_FORMAT_FLAG_ALPHA)

-         {

-            if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)

-               png_error(png_ptr, "gray+alpha color-map: too few entries");

-

-            cmap_entries = make_ga_colormap(display);

-

-            background_index = PNG_CMAP_GA_BACKGROUND;

-            output_processing = PNG_CMAP_GA;

-         }

-

-         else /* alpha is removed */

-         {

-            /* Alpha must be removed as the PNG data is processed when the

-             * background is a color because the G and A channels are

-             * independent and the vector addition (non-parallel vectors) is a

-             * 2-D problem.

-             *

-             * This can be reduced to the same algorithm as above by making a

-             * colormap containing gray levels (for the opaque grays), a

-             * background entry (for a transparent pixel) and a set of four six

-             * level color values, one set for each intermediate alpha value.

-             * See the comments in make_ga_colormap for how this works in the

-             * per-pixel processing.

-             *

-             * If the background is gray, however, we only need a 256 entry gray

-             * level color map.  It is sufficient to make the entry generated

-             * for the background color be exactly the color specified.

-             */

-            if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0 ||

-               (back_r == back_g && back_g == back_b))

-            {

-               /* Background is gray; no special processing will be required. */

-               png_color_16 c;

-               png_uint_32 gray = back_g;

-

-               if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)

-                  png_error(png_ptr, "gray-alpha color-map: too few entries");

-

-               cmap_entries = make_gray_colormap(display);

-

-               if (output_encoding == E_LINEAR)

-               {

-                  gray = PNG_sRGB_FROM_LINEAR(gray * 255);

-

-                  /* And make sure the corresponding palette entry matches. */

-                  png_create_colormap_entry(display, gray, back_g, back_g,

-                     back_g, 65535, E_LINEAR);

-               }

-

-               /* The background passed to libpng, however, must be the sRGB

-                * value.

-                */

-               c.index = 0; /*unused*/

-               c.gray = c.red = c.green = c.blue = (png_uint_16)gray;

-

-               png_set_background_fixed(png_ptr, &c,

-                  PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,

-                  0/*gamma: not used*/);

-

-               output_processing = PNG_CMAP_NONE;

-            }

-

-            else

-            {

-               png_uint_32 i, a;

-

-               /* This is the same as png_make_ga_colormap, above, except that

-                * the entries are all opaque.

-                */

-               if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)

-                  png_error(png_ptr, "ga-alpha color-map: too few entries");

-

-               i = 0;

-               while (i < 231)

-               {

-                  png_uint_32 gray = (i * 256 + 115) / 231;

-                  png_create_colormap_entry(display, i++, gray, gray, gray,

-                     255, E_sRGB);

-               }

-

-               /* NOTE: this preserves the full precision of the application

-                * background color.

-                */

-               background_index = i;

-               png_create_colormap_entry(display, i++, back_r, back_g, back_b,

-                  output_encoding == E_LINEAR ? 65535U : 255U, output_encoding);

-

-               /* For non-opaque input composite on the sRGB background - this

-                * requires inverting the encoding for each component.  The input

-                * is still converted to the sRGB encoding because this is a

-                * reasonable approximate to the logarithmic curve of human

-                * visual sensitivity, at least over the narrow range which PNG

-                * represents.  Consequently 'G' is always sRGB encoded, while

-                * 'A' is linear.  We need the linear background colors.

-                */

-               if (output_encoding == E_sRGB) /* else already linear */

-               {

-                  /* This may produce a value not exactly matching the

-                   * background, but that's ok because these numbers are only

-                   * used when alpha != 0

-                   */

-                  back_r = png_sRGB_table[back_r];

-                  back_g = png_sRGB_table[back_g];

-                  back_b = png_sRGB_table[back_b];

-               }

-

-               for (a=1; a<5; ++a)

-               {

-                  unsigned int g;

-

-                  /* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled

-                   * by an 8-bit alpha value (0..255).

-                   */

-                  png_uint_32 alpha = 51 * a;

-                  png_uint_32 back_rx = (255-alpha) * back_r;

-                  png_uint_32 back_gx = (255-alpha) * back_g;

-                  png_uint_32 back_bx = (255-alpha) * back_b;

-

-                  for (g=0; g<6; ++g)

-                  {

-                     png_uint_32 gray = png_sRGB_table[g*51] * alpha;

-

-                     png_create_colormap_entry(display, i++,

-                        PNG_sRGB_FROM_LINEAR(gray + back_rx),

-                        PNG_sRGB_FROM_LINEAR(gray + back_gx),

-                        PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, E_sRGB);

-                  }

-               }

-

-               cmap_entries = i;

-               output_processing = PNG_CMAP_GA;

-            }

-         }

-         break;

-

-      case PNG_COLOR_TYPE_RGB:

-      case PNG_COLOR_TYPE_RGB_ALPHA:

-         /* Exclude the case where the output is gray; we can always handle this

-          * with the cases above.

-          */

-         if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0)

-         {

-            /* The color-map will be grayscale, so we may as well convert the

-             * input RGB values to a simple grayscale and use the grayscale

-             * code above.

-             *

-             * NOTE: calling this apparently damages the recognition of the

-             * transparent color in background color handling; call

-             * png_set_tRNS_to_alpha before png_set_background_fixed.

-             */

-            png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1,

-               -1);

-            data_encoding = E_sRGB;

-

-            /* The output will now be one or two 8-bit gray or gray+alpha

-             * channels.  The more complex case arises when the input has alpha.

-             */

-            if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||

-               png_ptr->num_trans > 0) &&

-               (output_format & PNG_FORMAT_FLAG_ALPHA) != 0)

-            {

-               /* Both input and output have an alpha channel, so no background

-                * processing is required; just map the GA bytes to the right

-                * color-map entry.

-                */

-               expand_tRNS = 1;

-

-               if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)

-                  png_error(png_ptr, "rgb[ga] color-map: too few entries");

-

-               cmap_entries = make_ga_colormap(display);

-               background_index = PNG_CMAP_GA_BACKGROUND;

-               output_processing = PNG_CMAP_GA;

-            }

-

-            else

-            {

-               /* Either the input or the output has no alpha channel, so there

-                * will be no non-opaque pixels in the color-map; it will just be

-                * grayscale.

-                */

-               if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)

-                  png_error(png_ptr, "rgb[gray] color-map: too few entries");

-

-               /* Ideally this code would use libpng to do the gamma correction,

-                * but if an input alpha channel is to be removed we will hit the

-                * libpng bug in gamma+compose+rgb-to-gray (the double gamma

-                * correction bug).  Fix this by dropping the gamma correction in

-                * this case and doing it in the palette; this will result in

-                * duplicate palette entries, but that's better than the

-                * alternative of double gamma correction.

-                */

-               if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||

-                  png_ptr->num_trans > 0) &&

-                  png_gamma_not_sRGB(png_ptr->colorspace.gamma))

-               {

-                  cmap_entries = make_gray_file_colormap(display);

-                  data_encoding = E_FILE;

-               }

-

-               else

-                  cmap_entries = make_gray_colormap(display);

-

-               /* But if the input has alpha or transparency it must be removed

-                */

-               if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||

-                  png_ptr->num_trans > 0)

-               {

-                  png_color_16 c;

-                  png_uint_32 gray = back_g;

-

-                  /* We need to ensure that the application background exists in

-                   * the colormap and that completely transparent pixels map to

-                   * it.  Achieve this simply by ensuring that the entry

-                   * selected for the background really is the background color.

-                   */

-                  if (data_encoding == E_FILE) /* from the fixup above */

-                  {

-                     /* The app supplied a gray which is in output_encoding, we

-                      * need to convert it to a value of the input (E_FILE)

-                      * encoding then set this palette entry to the required

-                      * output encoding.

-                      */

-                     if (output_encoding == E_sRGB)

-                        gray = png_sRGB_table[gray]; /* now E_LINEAR */

-

-                     gray = PNG_DIV257(png_gamma_16bit_correct(gray,

-                        png_ptr->colorspace.gamma)); /* now E_FILE */

-

-                     /* And make sure the corresponding palette entry contains

-                      * exactly the required sRGB value.

-                      */

-                     png_create_colormap_entry(display, gray, back_g, back_g,

-                        back_g, 0/*unused*/, output_encoding);

-                  }

-

-                  else if (output_encoding == E_LINEAR)

-                  {

-                     gray = PNG_sRGB_FROM_LINEAR(gray * 255);

-

-                     /* And make sure the corresponding palette entry matches.

-                      */

-                     png_create_colormap_entry(display, gray, back_g, back_g,

-                        back_g, 0/*unused*/, E_LINEAR);

-                  }

-

-                  /* The background passed to libpng, however, must be the

-                   * output (normally sRGB) value.

-                   */

-                  c.index = 0; /*unused*/

-                  c.gray = c.red = c.green = c.blue = (png_uint_16)gray;

-

-                  /* NOTE: the following is apparently a bug in libpng. Without

-                   * it the transparent color recognition in

-                   * png_set_background_fixed seems to go wrong.

-                   */

-                  expand_tRNS = 1;

-                  png_set_background_fixed(png_ptr, &c,

-                     PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,

-                     0/*gamma: not used*/);

-               }

-

-               output_processing = PNG_CMAP_NONE;

-            }

-         }

-

-         else /* output is color */

-         {

-            /* We could use png_quantize here so long as there is no transparent

-             * color or alpha; png_quantize ignores alpha.  Easier overall just

-             * to do it once and using PNG_DIV51 on the 6x6x6 reduced RGB cube.

-             * Consequently we always want libpng to produce sRGB data.

-             */

-            data_encoding = E_sRGB;

-

-            /* Is there any transparency or alpha? */

-            if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||

-               png_ptr->num_trans > 0)

-            {

-               /* Is there alpha in the output too?  If so all four channels are

-                * processed into a special RGB cube with alpha support.

-                */

-               if (output_format & PNG_FORMAT_FLAG_ALPHA)

-               {

-                  png_uint_32 r;

-

-                  if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)

-                     png_error(png_ptr, "rgb+alpha color-map: too few entries");

-

-                  cmap_entries = make_rgb_colormap(display);

-

-                  /* Add a transparent entry. */

-                  png_create_colormap_entry(display, cmap_entries, 255, 255,

-                     255, 0, E_sRGB);

-

-                  /* This is stored as the background index for the processing

-                   * algorithm.

-                   */

-                  background_index = cmap_entries++;

-

-                  /* Add 27 r,g,b entries each with alpha 0.5. */

-                  for (r=0; r<256; r = (r << 1) | 0x7f)

-                  {

-                     png_uint_32 g;

-

-                     for (g=0; g<256; g = (g << 1) | 0x7f)

-                     {

-                        png_uint_32 b;

-

-                        /* This generates components with the values 0, 127 and

-                         * 255

-                         */

-                        for (b=0; b<256; b = (b << 1) | 0x7f)

-                           png_create_colormap_entry(display, cmap_entries++,

-                              r, g, b, 128, E_sRGB);

-                     }

-                  }

-

-                  expand_tRNS = 1;

-                  output_processing = PNG_CMAP_RGB_ALPHA;

-               }

-

-               else

-               {

-                  /* Alpha/transparency must be removed.  The background must

-                   * exist in the color map (achieved by setting adding it after

-                   * the 666 color-map).  If the standard processing code will

-                   * pick up this entry automatically that's all that is

-                   * required; libpng can be called to do the background

-                   * processing.

-                   */

-                  unsigned int sample_size =

-                     PNG_IMAGE_SAMPLE_SIZE(output_format);

-                  png_uint_32 r, g, b; /* sRGB background */

-

-                  if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)

-                     png_error(png_ptr, "rgb-alpha color-map: too few entries");

-

-                  cmap_entries = make_rgb_colormap(display);

-

-                  png_create_colormap_entry(display, cmap_entries, back_r,

-                        back_g, back_b, 0/*unused*/, output_encoding);

-

-                  if (output_encoding == E_LINEAR)

-                  {

-                     r = PNG_sRGB_FROM_LINEAR(back_r * 255);

-                     g = PNG_sRGB_FROM_LINEAR(back_g * 255);

-                     b = PNG_sRGB_FROM_LINEAR(back_b * 255);

-                  }

-

-                  else

-                  {

-                     r = back_r;

-                     g = back_g;

-                     b = back_g;

-                  }

-

-                  /* Compare the newly-created color-map entry with the one the

-                   * PNG_CMAP_RGB algorithm will use.  If the two entries don't

-                   * match, add the new one and set this as the background

-                   * index.

-                   */

-                  if (memcmp((png_const_bytep)display->colormap +

-                        sample_size * cmap_entries,

-                     (png_const_bytep)display->colormap +

-                        sample_size * PNG_RGB_INDEX(r,g,b),

-                     sample_size) != 0)

-                  {

-                     /* The background color must be added. */

-                     background_index = cmap_entries++;

-

-                     /* Add 27 r,g,b entries each with created by composing with

-                      * the background at alpha 0.5.

-                      */

-                     for (r=0; r<256; r = (r << 1) | 0x7f)

-                     {

-                        for (g=0; g<256; g = (g << 1) | 0x7f)

-                        {

-                           /* This generates components with the values 0, 127

-                            * and 255

-                            */

-                           for (b=0; b<256; b = (b << 1) | 0x7f)

-                              png_create_colormap_entry(display, cmap_entries++,

-                                 png_colormap_compose(display, r, E_sRGB, 128,

-                                    back_r, output_encoding),

-                                 png_colormap_compose(display, g, E_sRGB, 128,

-                                    back_g, output_encoding),

-                                 png_colormap_compose(display, b, E_sRGB, 128,

-                                    back_b, output_encoding),

-                                 0/*unused*/, output_encoding);

-                        }

-                     }

-

-                     expand_tRNS = 1;

-                     output_processing = PNG_CMAP_RGB_ALPHA;

-                  }

-

-                  else /* background color is in the standard color-map */

-                  {

-                     png_color_16 c;

-

-                     c.index = 0; /*unused*/

-                     c.red = (png_uint_16)back_r;

-                     c.gray = c.green = (png_uint_16)back_g;

-                     c.blue = (png_uint_16)back_b;

-

-                     png_set_background_fixed(png_ptr, &c,

-                        PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,

-                        0/*gamma: not used*/);

-

-                     output_processing = PNG_CMAP_RGB;

-                  }

-               }

-            }

-

-            else /* no alpha or transparency in the input */

-            {

-               /* Alpha in the output is irrelevant, simply map the opaque input

-                * pixels to the 6x6x6 color-map.

-                */

-               if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries)

-                  png_error(png_ptr, "rgb color-map: too few entries");

-

-               cmap_entries = make_rgb_colormap(display);

-               output_processing = PNG_CMAP_RGB;

-            }

-         }

-         break;

-

-      case PNG_COLOR_TYPE_PALETTE:

-         /* It's already got a color-map.  It may be necessary to eliminate the

-          * tRNS entries though.

-          */

-         {

-            unsigned int num_trans = png_ptr->num_trans;

-            png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL;

-            png_const_colorp colormap = png_ptr->palette;

-            const int do_background = trans != NULL &&

-               (output_format & PNG_FORMAT_FLAG_ALPHA) == 0;

-            unsigned int i;

-

-            /* Just in case: */

-            if (trans == NULL)

-               num_trans = 0;

-

-            output_processing = PNG_CMAP_NONE;

-            data_encoding = E_FILE; /* Don't change from color-map indicies */

-            cmap_entries = png_ptr->num_palette;

-            if (cmap_entries > 256)

-               cmap_entries = 256;

-

-            if (cmap_entries > image->colormap_entries)

-               png_error(png_ptr, "palette color-map: too few entries");

-

-            for (i=0; i < cmap_entries; ++i)

-            {

-               if (do_background && i < num_trans && trans[i] < 255)

-               {

-                  if (trans[i] == 0)

-                     png_create_colormap_entry(display, i, back_r, back_g,

-                        back_b, 0, output_encoding);

-

-                  else

-                  {

-                     /* Must compose the PNG file color in the color-map entry

-                      * on the sRGB color in 'back'.

-                      */

-                     png_create_colormap_entry(display, i,

-                        png_colormap_compose(display, colormap[i].red, E_FILE,

-                           trans[i], back_r, output_encoding),

-                        png_colormap_compose(display, colormap[i].green, E_FILE,

-                           trans[i], back_g, output_encoding),

-                        png_colormap_compose(display, colormap[i].blue, E_FILE,

-                           trans[i], back_b, output_encoding),

-                        output_encoding == E_LINEAR ? trans[i] * 257U :

-                           trans[i],

-                        output_encoding);

-                  }

-               }

-

-               else

-                  png_create_colormap_entry(display, i, colormap[i].red,

-                     colormap[i].green, colormap[i].blue,

-                     i < num_trans ? trans[i] : 255U, E_FILE/*8-bit*/);

-            }

-

-            /* The PNG data may have indicies packed in fewer than 8 bits, it

-             * must be expanded if so.

-             */

-            if (png_ptr->bit_depth < 8)

-               png_set_packing(png_ptr);

-         }

-         break;

-

-      default:

-         png_error(png_ptr, "invalid PNG color type");

-         /*NOT REACHED*/

-         break;

-   }

-

-   /* Now deal with the output processing */

-   if (expand_tRNS && png_ptr->num_trans > 0 &&

-      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0)

-      png_set_tRNS_to_alpha(png_ptr);

-

-   switch (data_encoding)

-   {

-      default:

-         png_error(png_ptr, "bad data option (internal error)");

-         break;

-

-      case E_sRGB:

-         /* Change to 8-bit sRGB */

-         png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB);

-         /* FALL THROUGH */

-

-      case E_FILE:

-         if (png_ptr->bit_depth > 8)

-            png_set_scale_16(png_ptr);

-         break;

-   }

-

-   if (cmap_entries > 256 || cmap_entries > image->colormap_entries)

-      png_error(png_ptr, "color map overflow (BAD internal error)");

-

-   image->colormap_entries = cmap_entries;

-

-   /* Double check using the recorded background index */

-   switch (output_processing)

-   {

-      case PNG_CMAP_NONE:

-         if (background_index != PNG_CMAP_NONE_BACKGROUND)

-            goto bad_background;

-         break;

-

-      case PNG_CMAP_GA:

-         if (background_index != PNG_CMAP_GA_BACKGROUND)

-            goto bad_background;

-         break;

-

-      case PNG_CMAP_TRANS:

-         if (background_index >= cmap_entries ||

-            background_index != PNG_CMAP_TRANS_BACKGROUND)

-            goto bad_background;

-         break;

-

-      case PNG_CMAP_RGB:

-         if (background_index != PNG_CMAP_RGB_BACKGROUND)

-            goto bad_background;

-         break;

-

-      case PNG_CMAP_RGB_ALPHA:

-         if (background_index != PNG_CMAP_RGB_ALPHA_BACKGROUND)

-            goto bad_background;

-         break;

-

-      default:

-         png_error(png_ptr, "bad processing option (internal error)");

-

-      bad_background:

-         png_error(png_ptr, "bad background index (internal error)");

-   }

-

-   display->colormap_processing = output_processing;

-

-   return 1/*ok*/;

-}

-

-/* The final part of the color-map read called from png_image_finish_read. */

-static int

-png_image_read_and_map(png_voidp argument)

-{

-   png_image_read_control *display = png_voidcast(png_image_read_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-   int passes;

-

-   /* Called when the libpng data must be transformed into the color-mapped

-    * form.  There is a local row buffer in display->local and this routine must

-    * do the interlace handling.

-    */

-   switch (png_ptr->interlaced)

-   {

-      case PNG_INTERLACE_NONE:

-         passes = 1;

-         break;

-

-      case PNG_INTERLACE_ADAM7:

-         passes = PNG_INTERLACE_ADAM7_PASSES;

-         break;

-

-      default:

-         passes = 0;

-         png_error(png_ptr, "unknown interlace type");

-   }

-

-   {

-      png_uint_32  height = image->height;

-      png_uint_32  width = image->width;

-      int          proc = display->colormap_processing;

-      png_bytep    first_row = png_voidcast(png_bytep, display->first_row);

-      ptrdiff_t    step_row = display->row_bytes;

-      int pass;

-

-      for (pass = 0; pass < passes; ++pass)

-      {

-         unsigned int     startx, stepx, stepy;

-         png_uint_32      y;

-

-         if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)

-         {

-            /* The row may be empty for a short image: */

-            if (PNG_PASS_COLS(width, pass) == 0)

-               continue;

-

-            startx = PNG_PASS_START_COL(pass);

-            stepx = PNG_PASS_COL_OFFSET(pass);

-            y = PNG_PASS_START_ROW(pass);

-            stepy = PNG_PASS_ROW_OFFSET(pass);

-         }

-

-         else

-         {

-            y = 0;

-            startx = 0;

-            stepx = stepy = 1;

-         }

-

-         for (; y<height; y += stepy)

-         {

-            png_bytep inrow = png_voidcast(png_bytep, display->local_row);

-            png_bytep outrow = first_row + y * step_row;

-            png_const_bytep end_row = outrow + width;

-

-            /* Read read the libpng data into the temporary buffer. */

-            png_read_row(png_ptr, inrow, NULL);

-

-            /* Now process the row according to the processing option, note

-             * that the caller verifies that the format of the libpng output

-             * data is as required.

-             */

-            outrow += startx;

-            switch (proc)

-            {

-               case PNG_CMAP_GA:

-                  for (; outrow < end_row; outrow += stepx)

-                  {

-                     /* The data is always in the PNG order */

-                     unsigned int gray = *inrow++;

-                     unsigned int alpha = *inrow++;

-                     unsigned int entry;

-

-                     /* NOTE: this code is copied as a comment in

-                      * make_ga_colormap above.  Please update the

-                      * comment if you change this code!

-                      */

-                     if (alpha > 229) /* opaque */

-                     {

-                        entry = (231 * gray + 128) >> 8;

-                     }

-                     else if (alpha < 26) /* transparent */

-                     {

-                        entry = 231;

-                     }

-                     else /* partially opaque */

-                     {

-                        entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray);

-                     }

-

-                     *outrow = (png_byte)entry;

-                  }

-                  break;

-

-               case PNG_CMAP_TRANS:

-                  for (; outrow < end_row; outrow += stepx)

-                  {

-                     png_byte gray = *inrow++;

-                     png_byte alpha = *inrow++;

-

-                     if (alpha == 0)

-                        *outrow = PNG_CMAP_TRANS_BACKGROUND;

-

-                     else if (gray != PNG_CMAP_TRANS_BACKGROUND)

-                        *outrow = gray;

-

-                     else

-                        *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1);

-                  }

-                  break;

-

-               case PNG_CMAP_RGB:

-                  for (; outrow < end_row; outrow += stepx)

-                  {

-                     *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]);

-                     inrow += 3;

-                  }

-                  break;

-

-               case PNG_CMAP_RGB_ALPHA:

-                  for (; outrow < end_row; outrow += stepx)

-                  {

-                     unsigned int alpha = inrow[3];

-

-                     /* Because the alpha entries only hold alpha==0.5 values

-                      * split the processing at alpha==0.25 (64) and 0.75

-                      * (196).

-                      */

-

-                     if (alpha >= 196)

-                        *outrow = PNG_RGB_INDEX(inrow[0], inrow[1],

-                           inrow[2]);

-

-                     else if (alpha < 64)

-                        *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND;

-

-                     else

-                     {

-                        /* Likewise there are three entries for each of r, g

-                         * and b.  We could select the entry by popcount on

-                         * the top two bits on those architectures that

-                         * support it, this is what the code below does,

-                         * crudely.

-                         */

-                        unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND+1;

-

-                        /* Here are how the values map:

-                         *

-                         * 0x00 .. 0x3f -> 0

-                         * 0x40 .. 0xbf -> 1

-                         * 0xc0 .. 0xff -> 2

-                         *

-                         * So, as above with the explicit alpha checks, the

-                         * breakpoints are at 64 and 196.

-                         */

-                        if (inrow[0] & 0x80) back_i += 9; /* red */

-                        if (inrow[0] & 0x40) back_i += 9;

-                        if (inrow[0] & 0x80) back_i += 3; /* green */

-                        if (inrow[0] & 0x40) back_i += 3;

-                        if (inrow[0] & 0x80) back_i += 1; /* blue */

-                        if (inrow[0] & 0x40) back_i += 1;

-

-                        *outrow = (png_byte)back_i;

-                     }

-

-                     inrow += 4;

-                  }

-                  break;

-

-               default:

-                  break;

-            }

-         }

-      }

-   }

-

-   return 1;

-}

-

-static int

-png_image_read_colormapped(png_voidp argument)

-{

-   png_image_read_control *display = png_voidcast(png_image_read_control*,

-      argument);

-   png_imagep image = display->image;

-   png_controlp control = image->opaque;

-   png_structrp png_ptr = control->png_ptr;

-   png_inforp info_ptr = control->info_ptr;

-

-   int passes = 0; /* As a flag */

-

-   PNG_SKIP_CHUNKS(png_ptr);

-

-   /* Update the 'info' structure and make sure the result is as required; first

-    * make sure to turn on the interlace handling if it will be required

-    * (because it can't be turned on *after* the call to png_read_update_info!)

-    */

-   if (display->colormap_processing == PNG_CMAP_NONE)

-      passes = png_set_interlace_handling(png_ptr);

-

-   png_read_update_info(png_ptr, info_ptr);

-

-   /* The expected output can be deduced from the colormap_processing option. */

-   switch (display->colormap_processing)

-   {

-      case PNG_CMAP_NONE:

-         /* Output must be one channel and one byte per pixel, the output

-          * encoding can be anything.

-          */

-         if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||

-            info_ptr->color_type == PNG_COLOR_TYPE_GRAY) &&

-            info_ptr->bit_depth == 8)

-            break;

-

-         goto bad_output;

-

-      case PNG_CMAP_TRANS:

-      case PNG_CMAP_GA:

-         /* Output must be two channels and the 'G' one must be sRGB, the latter

-          * can be checked with an exact number because it should have been set

-          * to this number above!

-          */

-         if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&

-            info_ptr->bit_depth == 8 &&

-            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&

-            image->colormap_entries == 256)

-            break;

-

-         goto bad_output;

-

-      case PNG_CMAP_RGB:

-         /* Output must be 8-bit sRGB encoded RGB */

-         if (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&

-            info_ptr->bit_depth == 8 &&

-            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&

-            image->colormap_entries == 216)

-            break;

-

-         goto bad_output;

-

-      case PNG_CMAP_RGB_ALPHA:

-         /* Output must be 8-bit sRGB encoded RGBA */

-         if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&

-            info_ptr->bit_depth == 8 &&

-            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&

-            image->colormap_entries == 244 /* 216 + 1 + 27 */)

-            break;

-

-         /* goto bad_output; */

-         /* FALL THROUGH */

-

-      default:

-      bad_output:

-         png_error(png_ptr, "bad color-map processing (internal error)");

-   }

-

-   /* Now read the rows.  Do this here if it is possible to read directly into

-    * the output buffer, otherwise allocate a local row buffer of the maximum

-    * size libpng requires and call the relevant processing routine safely.

-    */

-   {

-      png_voidp first_row = display->buffer;

-      ptrdiff_t row_bytes = display->row_stride;

-

-      /* The following expression is designed to work correctly whether it gives

-       * a signed or an unsigned result.

-       */

-      if (row_bytes < 0)

-      {

-         char *ptr = png_voidcast(char*, first_row);

-         ptr += (image->height-1) * (-row_bytes);

-         first_row = png_voidcast(png_voidp, ptr);

-      }

-

-      display->first_row = first_row;

-      display->row_bytes = row_bytes;

-   }

-

-   if (passes == 0)

-   {

-      int result;

-      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));

-

-      display->local_row = row;

-      result = png_safe_execute(image, png_image_read_and_map, display);

-      display->local_row = NULL;

-      png_free(png_ptr, row);

-

-      return result;

-   }

-

-   else

-   {

-      png_alloc_size_t row_bytes = display->row_bytes;

-

-      while (--passes >= 0)

-      {

-         png_uint_32      y = image->height;

-         png_bytep        row = png_voidcast(png_bytep, display->first_row);

-

-         while (y-- > 0)

-         {

-            png_read_row(png_ptr, row, NULL);

-            row += row_bytes;

-         }

-      }

-

-      return 1;

-   }

-}

-

-/* Just the row reading part of png_image_read. */

-static int

-png_image_read_composite(png_voidp argument)

-{

-   png_image_read_control *display = png_voidcast(png_image_read_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-   int passes;

-

-   switch (png_ptr->interlaced)

-   {

-      case PNG_INTERLACE_NONE:

-         passes = 1;

-         break;

-

-      case PNG_INTERLACE_ADAM7:

-         passes = PNG_INTERLACE_ADAM7_PASSES;

-         break;

-

-      default:

-         passes = 0;

-         png_error(png_ptr, "unknown interlace type");

-   }

-

-   {

-      png_uint_32  height = image->height;

-      png_uint_32  width = image->width;

-      ptrdiff_t    step_row = display->row_bytes;

-      unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;

-      int pass;

-

-      for (pass = 0; pass < passes; ++pass)

-      {

-         unsigned int     startx, stepx, stepy;

-         png_uint_32      y;

-

-         if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)

-         {

-            /* The row may be empty for a short image: */

-            if (PNG_PASS_COLS(width, pass) == 0)

-               continue;

-

-            startx = PNG_PASS_START_COL(pass) * channels;

-            stepx = PNG_PASS_COL_OFFSET(pass) * channels;

-            y = PNG_PASS_START_ROW(pass);

-            stepy = PNG_PASS_ROW_OFFSET(pass);

-         }

-

-         else

-         {

-            y = 0;

-            startx = 0;

-            stepx = channels;

-            stepy = 1;

-         }

-

-         for (; y<height; y += stepy)

-         {

-            png_bytep inrow = png_voidcast(png_bytep, display->local_row);

-            png_bytep outrow;

-            png_const_bytep end_row;

-

-            /* Read the row, which is packed: */

-            png_read_row(png_ptr, inrow, NULL);

-

-            outrow = png_voidcast(png_bytep, display->first_row);

-            outrow += y * step_row;

-            end_row = outrow + width * channels;

-

-            /* Now do the composition on each pixel in this row. */

-            outrow += startx;

-            for (; outrow < end_row; outrow += stepx)

-            {

-               png_byte alpha = inrow[channels];

-

-               if (alpha > 0) /* else no change to the output */

-               {

-                  unsigned int c;

-

-                  for (c=0; c<channels; ++c)

-                  {

-                     png_uint_32 component = inrow[c];

-

-                     if (alpha < 255) /* else just use component */

-                     {

-                        /* This is PNG_OPTIMIZED_ALPHA, the component value

-                         * is a linear 8-bit value.  Combine this with the

-                         * current outrow[c] value which is sRGB encoded.

-                         * Arithmetic here is 16-bits to preserve the output

-                         * values correctly.

-                         */

-                        component *= 257*255; /* =65535 */

-                        component += (255-alpha)*png_sRGB_table[outrow[c]];

-

-                        /* So 'component' is scaled by 255*65535 and is

-                         * therefore appropriate for the sRGB to linear

-                         * conversion table.

-                         */

-                        component = PNG_sRGB_FROM_LINEAR(component);

-                     }

-

-                     outrow[c] = (png_byte)component;

-                  }

-               }

-

-               inrow += channels+1; /* components and alpha channel */

-            }

-         }

-      }

-   }

-

-   return 1;

-}

-

-/* The do_local_background case; called when all the following transforms are to

- * be done:

- *

- * PNG_RGB_TO_GRAY

- * PNG_COMPOSITE

- * PNG_GAMMA

- *

- * This is a work-round for the fact that both the PNG_RGB_TO_GRAY and

- * PNG_COMPOSITE code performs gamma correction, so we get double gamma

- * correction.  The fix-up is to prevent the PNG_COMPOSITE operation happening

- * inside libpng, so this routine sees an 8 or 16-bit gray+alpha row and handles

- * the removal or pre-multiplication of the alpha channel.

- */

-static int

-png_image_read_background(png_voidp argument)

-{

-   png_image_read_control *display = png_voidcast(png_image_read_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-   png_inforp info_ptr = image->opaque->info_ptr;

-   png_uint_32 height = image->height;

-   png_uint_32 width = image->width;

-   int pass, passes;

-

-   /* Double check the convoluted logic below.  We expect to get here with

-    * libpng doing rgb to gray and gamma correction but background processing

-    * left to the png_image_read_background function.  The rows libpng produce

-    * might be 8 or 16-bit but should always have two channels; gray plus alpha.

-    */

-   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)

-      png_error(png_ptr, "lost rgb to gray");

-

-   if ((png_ptr->transformations & PNG_COMPOSE) != 0)

-      png_error(png_ptr, "unexpected compose");

-

-   if (png_get_channels(png_ptr, info_ptr) != 2)

-      png_error(png_ptr, "lost/gained channels");

-

-   /* Expect the 8-bit case to always remove the alpha channel */

-   if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 &&

-      (image->format & PNG_FORMAT_FLAG_ALPHA) != 0)

-      png_error(png_ptr, "unexpected 8-bit transformation");

-

-   switch (png_ptr->interlaced)

-   {

-      case PNG_INTERLACE_NONE:

-         passes = 1;

-         break;

-

-      case PNG_INTERLACE_ADAM7:

-         passes = PNG_INTERLACE_ADAM7_PASSES;

-         break;

-

-      default:

-         passes = 0;

-         png_error(png_ptr, "unknown interlace type");

-   }

-

-   switch (png_get_bit_depth(png_ptr, info_ptr))

-   {

-      default:

-         png_error(png_ptr, "unexpected bit depth");

-         break;

-

-      case 8:

-         /* 8-bit sRGB gray values with an alpha channel; the alpha channel is

-          * to be removed by composing on a backgroundi: either the row if

-          * display->background is NULL or display->background->green if not.

-          * Unlike the code above ALPHA_OPTIMIZED has *not* been done.

-          */

-         {

-            png_bytep first_row = png_voidcast(png_bytep, display->first_row);

-            ptrdiff_t step_row = display->row_bytes;

-

-            for (pass = 0; pass < passes; ++pass)

-            {

-               png_bytep        row = png_voidcast(png_bytep,

-                                                   display->first_row);

-               unsigned int     startx, stepx, stepy;

-               png_uint_32      y;

-

-               if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)

-               {

-                  /* The row may be empty for a short image: */

-                  if (PNG_PASS_COLS(width, pass) == 0)

-                     continue;

-

-                  startx = PNG_PASS_START_COL(pass);

-                  stepx = PNG_PASS_COL_OFFSET(pass);

-                  y = PNG_PASS_START_ROW(pass);

-                  stepy = PNG_PASS_ROW_OFFSET(pass);

-               }

-

-               else

-               {

-                  y = 0;

-                  startx = 0;

-                  stepx = stepy = 1;

-               }

-

-               if (display->background == NULL)

-               {

-                  for (; y<height; y += stepy)

-                  {

-                     png_bytep inrow = png_voidcast(png_bytep,

-                        display->local_row);

-                     png_bytep outrow = first_row + y * step_row;

-                     png_const_bytep end_row = outrow + width;

-

-                     /* Read the row, which is packed: */

-                     png_read_row(png_ptr, inrow, NULL);

-

-                     /* Now do the composition on each pixel in this row. */

-                     outrow += startx;

-                     for (; outrow < end_row; outrow += stepx)

-                     {

-                        png_byte alpha = inrow[1];

-

-                        if (alpha > 0) /* else no change to the output */

-                        {

-                           png_uint_32 component = inrow[0];

-

-                           if (alpha < 255) /* else just use component */

-                           {

-                              /* Since PNG_OPTIMIZED_ALPHA was not set it is

-                               * necessary to invert the sRGB transfer

-                               * function and multiply the alpha out.

-                               */

-                              component = png_sRGB_table[component] * alpha;

-                              component += png_sRGB_table[outrow[0]] *

-                                 (255-alpha);

-                              component = PNG_sRGB_FROM_LINEAR(component);

-                           }

-

-                           outrow[0] = (png_byte)component;

-                        }

-

-                        inrow += 2; /* gray and alpha channel */

-                     }

-                  }

-               }

-

-               else /* constant background value */

-               {

-                  png_byte background8 = display->background->green;

-                  png_uint_16 background = png_sRGB_table[background8];

-

-                  for (; y<height; y += stepy)

-                  {

-                     png_bytep inrow = png_voidcast(png_bytep,

-                        display->local_row);

-                     png_bytep outrow = first_row + y * step_row;

-                     png_const_bytep end_row = outrow + width;

-

-                     /* Read the row, which is packed: */

-                     png_read_row(png_ptr, inrow, NULL);

-

-                     /* Now do the composition on each pixel in this row. */

-                     outrow += startx;

-                     for (; outrow < end_row; outrow += stepx)

-                     {

-                        png_byte alpha = inrow[1];

-

-                        if (alpha > 0) /* else use background */

-                        {

-                           png_uint_32 component = inrow[0];

-

-                           if (alpha < 255) /* else just use component */

-                           {

-                              component = png_sRGB_table[component] * alpha;

-                              component += background * (255-alpha);

-                              component = PNG_sRGB_FROM_LINEAR(component);

-                           }

-

-                           outrow[0] = (png_byte)component;

-                        }

-

-                        else

-                           outrow[0] = background8;

-

-                        inrow += 2; /* gray and alpha channel */

-                     }

-

-                     row += display->row_bytes;

-                  }

-               }

-            }

-         }

-         break;

-

-      case 16:

-         /* 16-bit linear with pre-multiplied alpha; the pre-multiplication must

-          * still be done and, maybe, the alpha channel removed.  This code also

-          * handles the alpha-first option.

-          */

-         {

-            png_uint_16p first_row = png_voidcast(png_uint_16p,

-               display->first_row);

-            /* The division by two is safe because the caller passed in a

-             * stride which was multiplied by 2 (below) to get row_bytes.

-             */

-            ptrdiff_t    step_row = display->row_bytes / 2;

-            int preserve_alpha = (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;

-            unsigned int outchannels = 1+preserve_alpha;

-            int swap_alpha = 0;

-

-            if (preserve_alpha && (image->format & PNG_FORMAT_FLAG_AFIRST))

-               swap_alpha = 1;

-

-            for (pass = 0; pass < passes; ++pass)

-            {

-               unsigned int     startx, stepx, stepy;

-               png_uint_32      y;

-

-               /* The 'x' start and step are adjusted to output components here.

-                */

-               if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)

-               {

-                  /* The row may be empty for a short image: */

-                  if (PNG_PASS_COLS(width, pass) == 0)

-                     continue;

-

-                  startx = PNG_PASS_START_COL(pass) * outchannels;

-                  stepx = PNG_PASS_COL_OFFSET(pass) * outchannels;

-                  y = PNG_PASS_START_ROW(pass);

-                  stepy = PNG_PASS_ROW_OFFSET(pass);

-               }

-

-               else

-               {

-                  y = 0;

-                  startx = 0;

-                  stepx = outchannels;

-                  stepy = 1;

-               }

-

-               for (; y<height; y += stepy)

-               {

-                  png_const_uint_16p inrow;

-                  png_uint_16p outrow = first_row + y*step_row;

-                  png_uint_16p end_row = outrow + width * outchannels;

-

-                  /* Read the row, which is packed: */

-                  png_read_row(png_ptr, png_voidcast(png_bytep,

-                     display->local_row), NULL);

-                  inrow = png_voidcast(png_const_uint_16p, display->local_row);

-

-                  /* Now do the pre-multiplication on each pixel in this row.

-                   */

-                  outrow += startx;

-                  for (; outrow < end_row; outrow += stepx)

-                  {

-                     png_uint_32 component = inrow[0];

-                     png_uint_16 alpha = inrow[1];

-

-                     if (alpha > 0) /* else 0 */

-                     {

-                        if (alpha < 65535) /* else just use component */

-                        {

-                           component *= alpha;

-                           component += 32767;

-                           component /= 65535;

-                        }

-                     }

-

-                     else

-                        component = 0;

-

-                     outrow[swap_alpha] = (png_uint_16)component;

-                     if (preserve_alpha)

-                        outrow[1 ^ swap_alpha] = alpha;

-

-                     inrow += 2; /* components and alpha channel */

-                  }

-               }

-            }

-         }

-         break;

-   }

-

-   return 1;

-}

-

-/* The guts of png_image_finish_read as a png_safe_execute callback. */

-static int

-png_image_read_direct(png_voidp argument)

-{

-   png_image_read_control *display = png_voidcast(png_image_read_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-   png_inforp info_ptr = image->opaque->info_ptr;

-

-   png_uint_32 format = image->format;

-   int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0;

-   int do_local_compose = 0;

-   int do_local_background = 0; /* to avoid double gamma correction bug */

-   int passes = 0;

-

-   /* Add transforms to ensure the correct output format is produced then check

-    * that the required implementation support is there.  Always expand; always

-    * need 8 bits minimum, no palette and expanded tRNS.

-    */

-   png_set_expand(png_ptr);

-

-   /* Now check the format to see if it was modified. */

-   {

-      png_uint_32 base_format = png_image_format(png_ptr) &

-         ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */;

-      png_uint_32 change = format ^ base_format;

-      png_fixed_point output_gamma;

-      int mode; /* alpha mode */

-

-      /* Do this first so that we have a record if rgb to gray is happening. */

-      if (change & PNG_FORMAT_FLAG_COLOR)

-      {

-         /* gray<->color transformation required. */

-         if (format & PNG_FORMAT_FLAG_COLOR)

-            png_set_gray_to_rgb(png_ptr);

-

-         else

-         {

-            /* libpng can't do both rgb to gray and

-             * background/pre-multiplication if there is also significant gamma

-             * correction, because both operations require linear colors and

-             * the code only supports one transform doing the gamma correction.

-             * Handle this by doing the pre-multiplication or background

-             * operation in this code, if necessary.

-             *

-             * TODO: fix this by rewriting pngrtran.c (!)

-             *

-             * For the moment (given that fixing this in pngrtran.c is an

-             * enormous change) 'do_local_background' is used to indicate that

-             * the problem exists.

-             */

-            if (base_format & PNG_FORMAT_FLAG_ALPHA)

-               do_local_background = 1/*maybe*/;

-

-            png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE,

-               PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);

-         }

-

-         change &= ~PNG_FORMAT_FLAG_COLOR;

-      }

-

-      /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise.

-       */

-      {

-         png_fixed_point input_gamma_default;

-

-         if ((base_format & PNG_FORMAT_FLAG_LINEAR) &&

-            (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)

-            input_gamma_default = PNG_GAMMA_LINEAR;

-         else

-            input_gamma_default = PNG_DEFAULT_sRGB;

-

-         /* Call png_set_alpha_mode to set the default for the input gamma; the

-          * output gamma is set by a second call below.

-          */

-         png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, input_gamma_default);

-      }

-

-      if (linear)

-      {

-         /* If there *is* an alpha channel in the input it must be multiplied

-          * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG.

-          */

-         if (base_format & PNG_FORMAT_FLAG_ALPHA)

-            mode = PNG_ALPHA_STANDARD; /* associated alpha */

-

-         else

-            mode = PNG_ALPHA_PNG;

-

-         output_gamma = PNG_GAMMA_LINEAR;

-      }

-

-      else

-      {

-         mode = PNG_ALPHA_PNG;

-         output_gamma = PNG_DEFAULT_sRGB;

-      }

-

-      /* If 'do_local_background' is set check for the presence of gamma

-       * correction; this is part of the work-round for the libpng bug

-       * described above.

-       *

-       * TODO: fix libpng and remove this.

-       */

-      if (do_local_background)

-      {

-         png_fixed_point gtest;

-

-         /* This is 'png_gamma_threshold' from pngrtran.c; the test used for

-          * gamma correction, the screen gamma hasn't been set on png_struct

-          * yet; it's set below.  png_struct::gamma, however, is set to the

-          * final value.

-          */

-         if (png_muldiv(&gtest, output_gamma, png_ptr->colorspace.gamma,

-               PNG_FP_1) && !png_gamma_significant(gtest))

-            do_local_background = 0;

-

-         else if (mode == PNG_ALPHA_STANDARD)

-         {

-            do_local_background = 2/*required*/;

-            mode = PNG_ALPHA_PNG; /* prevent libpng doing it */

-         }

-

-         /* else leave as 1 for the checks below */

-      }

-

-      /* If the bit-depth changes then handle that here. */

-      if (change & PNG_FORMAT_FLAG_LINEAR)

-      {

-         if (linear /*16-bit output*/)

-            png_set_expand_16(png_ptr);

-

-         else /* 8-bit output */

-            png_set_scale_16(png_ptr);

-

-         change &= ~PNG_FORMAT_FLAG_LINEAR;

-      }

-

-      /* Now the background/alpha channel changes. */

-      if (change & PNG_FORMAT_FLAG_ALPHA)

-      {

-         /* Removing an alpha channel requires composition for the 8-bit

-          * formats; for the 16-bit it is already done, above, by the

-          * pre-multiplication and the channel just needs to be stripped.

-          */

-         if (base_format & PNG_FORMAT_FLAG_ALPHA)

-         {

-            /* If RGB->gray is happening the alpha channel must be left and the

-             * operation completed locally.

-             *

-             * TODO: fix libpng and remove this.

-             */

-            if (do_local_background)

-               do_local_background = 2/*required*/;

-

-            /* 16-bit output: just remove the channel */

-            else if (linear) /* compose on black (well, pre-multiply) */

-               png_set_strip_alpha(png_ptr);

-

-            /* 8-bit output: do an appropriate compose */

-            else if (display->background != NULL)

-            {

-               png_color_16 c;

-

-               c.index = 0; /*unused*/

-               c.red = display->background->red;

-               c.green = display->background->green;

-               c.blue = display->background->blue;

-               c.gray = display->background->green;

-

-               /* This is always an 8-bit sRGB value, using the 'green' channel

-                * for gray is much better than calculating the luminance here;

-                * we can get off-by-one errors in that calculation relative to

-                * the app expectations and that will show up in transparent

-                * pixels.

-                */

-               png_set_background_fixed(png_ptr, &c,

-                  PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,

-                  0/*gamma: not used*/);

-            }

-

-            else /* compose on row: implemented below. */

-            {

-               do_local_compose = 1;

-               /* This leaves the alpha channel in the output, so it has to be

-                * removed by the code below.  Set the encoding to the 'OPTIMIZE'

-                * one so the code only has to hack on the pixels that require

-                * composition.

-                */

-               mode = PNG_ALPHA_OPTIMIZED;

-            }

-         }

-

-         else /* output needs an alpha channel */

-         {

-            /* This is tricky because it happens before the swap operation has

-             * been accomplished; however, the swap does *not* swap the added

-             * alpha channel (weird API), so it must be added in the correct

-             * place.

-             */

-            png_uint_32 filler; /* opaque filler */

-            int where;

-

-            if (linear)

-               filler = 65535;

-

-            else

-               filler = 255;

-

-#           ifdef PNG_FORMAT_AFIRST_SUPPORTED

-               if (format & PNG_FORMAT_FLAG_AFIRST)

-               {

-                  where = PNG_FILLER_BEFORE;

-                  change &= ~PNG_FORMAT_FLAG_AFIRST;

-               }

-

-               else

-#           endif

-               where = PNG_FILLER_AFTER;

-

-            png_set_add_alpha(png_ptr, filler, where);

-         }

-

-         /* This stops the (irrelevant) call to swap_alpha below. */

-         change &= ~PNG_FORMAT_FLAG_ALPHA;

-      }

-

-      /* Now set the alpha mode correctly; this is always done, even if there is

-       * no alpha channel in either the input or the output because it correctly

-       * sets the output gamma.

-       */

-      png_set_alpha_mode_fixed(png_ptr, mode, output_gamma);

-

-#     ifdef PNG_FORMAT_BGR_SUPPORTED

-         if (change & PNG_FORMAT_FLAG_BGR)

-         {

-            /* Check only the output format; PNG is never BGR; don't do this if

-             * the output is gray, but fix up the 'format' value in that case.

-             */

-            if (format & PNG_FORMAT_FLAG_COLOR)

-               png_set_bgr(png_ptr);

-

-            else

-               format &= ~PNG_FORMAT_FLAG_BGR;

-

-            change &= ~PNG_FORMAT_FLAG_BGR;

-         }

-#     endif

-

-#     ifdef PNG_FORMAT_AFIRST_SUPPORTED

-         if (change & PNG_FORMAT_FLAG_AFIRST)

-         {

-            /* Only relevant if there is an alpha channel - it's particularly

-             * important to handle this correctly because do_local_compose may

-             * be set above and then libpng will keep the alpha channel for this

-             * code to remove.

-             */

-            if (format & PNG_FORMAT_FLAG_ALPHA)

-            {

-               /* Disable this if doing a local background,

-                * TODO: remove this when local background is no longer required.

-                */

-               if (do_local_background != 2)

-                  png_set_swap_alpha(png_ptr);

-            }

-

-            else

-               format &= ~PNG_FORMAT_FLAG_AFIRST;

-

-            change &= ~PNG_FORMAT_FLAG_AFIRST;

-         }

-#     endif

-

-      /* If the *output* is 16-bit then we need to check for a byte-swap on this

-       * architecture.

-       */

-      if (linear)

-      {

-         PNG_CONST png_uint_16 le = 0x0001;

-

-         if (*(png_const_bytep)&le)

-            png_set_swap(png_ptr);

-      }

-

-      /* If change is not now 0 some transformation is missing - error out. */

-      if (change)

-         png_error(png_ptr, "png_read_image: unsupported transformation");

-   }

-

-   PNG_SKIP_CHUNKS(png_ptr);

-

-   /* Update the 'info' structure and make sure the result is as required; first

-    * make sure to turn on the interlace handling if it will be required

-    * (because it can't be turned on *after* the call to png_read_update_info!)

-    *

-    * TODO: remove the do_local_background fixup below.

-    */

-   if (!do_local_compose && do_local_background != 2)

-      passes = png_set_interlace_handling(png_ptr);

-

-   png_read_update_info(png_ptr, info_ptr);

-

-   {

-      png_uint_32 info_format = 0;

-

-      if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)

-         info_format |= PNG_FORMAT_FLAG_COLOR;

-

-      if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)

-      {

-         /* do_local_compose removes this channel below. */

-         if (!do_local_compose)

-         {

-            /* do_local_background does the same if required. */

-            if (do_local_background != 2 ||

-               (format & PNG_FORMAT_FLAG_ALPHA) != 0)

-               info_format |= PNG_FORMAT_FLAG_ALPHA;

-         }

-      }

-

-      else if (do_local_compose) /* internal error */

-         png_error(png_ptr, "png_image_read: alpha channel lost");

-

-      if (info_ptr->bit_depth == 16)

-         info_format |= PNG_FORMAT_FLAG_LINEAR;

-

-#     ifdef PNG_FORMAT_BGR_SUPPORTED

-         if (png_ptr->transformations & PNG_BGR)

-            info_format |= PNG_FORMAT_FLAG_BGR;

-#     endif

-

-#     ifdef PNG_FORMAT_AFIRST_SUPPORTED

-         if (do_local_background == 2)

-         {

-            if (format & PNG_FORMAT_FLAG_AFIRST)

-               info_format |= PNG_FORMAT_FLAG_AFIRST;

-         }

-

-         if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 ||

-            ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 &&

-            (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0))

-         {

-            if (do_local_background == 2)

-               png_error(png_ptr, "unexpected alpha swap transformation");

-

-            info_format |= PNG_FORMAT_FLAG_AFIRST;

-         }

-#     endif

-

-      /* This is actually an internal error. */

-      if (info_format != format)

-         png_error(png_ptr, "png_read_image: invalid transformations");

-   }

-

-   /* Now read the rows.  If do_local_compose is set then it is necessary to use

-    * a local row buffer.  The output will be GA, RGBA or BGRA and must be

-    * converted to G, RGB or BGR as appropriate.  The 'local_row' member of the

-    * display acts as a flag.

-    */

-   {

-      png_voidp first_row = display->buffer;

-      ptrdiff_t row_bytes = display->row_stride;

-

-      if (linear)

-         row_bytes *= 2;

-

-      /* The following expression is designed to work correctly whether it gives

-       * a signed or an unsigned result.

-       */

-      if (row_bytes < 0)

-      {

-         char *ptr = png_voidcast(char*, first_row);

-         ptr += (image->height-1) * (-row_bytes);

-         first_row = png_voidcast(png_voidp, ptr);

-      }

-

-      display->first_row = first_row;

-      display->row_bytes = row_bytes;

-   }

-

-   if (do_local_compose)

-   {

-      int result;

-      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));

-

-      display->local_row = row;

-      result = png_safe_execute(image, png_image_read_composite, display);

-      display->local_row = NULL;

-      png_free(png_ptr, row);

-

-      return result;

-   }

-

-   else if (do_local_background == 2)

-   {

-      int result;

-      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));

-

-      display->local_row = row;

-      result = png_safe_execute(image, png_image_read_background, display);

-      display->local_row = NULL;

-      png_free(png_ptr, row);

-

-      return result;

-   }

-

-   else

-   {

-      png_alloc_size_t row_bytes = display->row_bytes;

-

-      while (--passes >= 0)

-      {

-         png_uint_32      y = image->height;

-         png_bytep        row = png_voidcast(png_bytep, display->first_row);

-

-         while (y-- > 0)

-         {

-            png_read_row(png_ptr, row, NULL);

-            row += row_bytes;

-         }

-      }

-

-      return 1;

-   }

-}

-

-int PNGAPI

-png_image_finish_read(png_imagep image, png_const_colorp background,

-   void *buffer, png_int_32 row_stride, void *colormap)

-{

-   if (image != NULL && image->version == PNG_IMAGE_VERSION)

-   {

-      png_uint_32 check;

-

-      if (row_stride == 0)

-         row_stride = PNG_IMAGE_ROW_STRIDE(*image);

-

-      if (row_stride < 0)

-         check = -row_stride;

-

-      else

-         check = row_stride;

-

-      if (image->opaque != NULL && buffer != NULL &&

-         check >= PNG_IMAGE_ROW_STRIDE(*image))

-      {

-         if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||

-            (image->colormap_entries > 0 && colormap != NULL))

-         {

-            int result;

-            png_image_read_control display;

-

-            memset(&display, 0, (sizeof display));

-            display.image = image;

-            display.buffer = buffer;

-            display.row_stride = row_stride;

-            display.colormap = colormap;

-            display.background = background;

-            display.local_row = NULL;

-

-            /* Choose the correct 'end' routine; for the color-map case all the

-             * setup has already been done.

-             */

-            if (image->format & PNG_FORMAT_FLAG_COLORMAP)

-               result =

-                  png_safe_execute(image, png_image_read_colormap, &display) &&

-                  png_safe_execute(image, png_image_read_colormapped, &display);

-

-            else

-               result =

-                  png_safe_execute(image, png_image_read_direct, &display);

-

-            png_image_free(image);

-            return result;

-         }

-

-         else

-            return png_image_error(image,

-               "png_image_finish_read[color-map]: no color-map");

-      }

-

-      else

-         return png_image_error(image,

-            "png_image_finish_read: invalid argument");

-   }

-

-   else if (image != NULL)

-      return png_image_error(image,

-         "png_image_finish_read: damaged PNG_IMAGE_VERSION");

-

-   return 0;

-}

-

-#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */

-#endif /* PNG_READ_SUPPORTED */

+
+/* pngread.c - read a PNG file
+ *
+ * Last changed in libpng 1.6.17 [March 26, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file contains routines that an application calls directly to
+ * read a PNG file or stream.
+ */
+
+#include "pngpriv.h"
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
+#  include <errno.h>
+#endif
+
+#ifdef PNG_READ_SUPPORTED
+
+/* Create a PNG structure for reading, and allocate any memory needed. */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
+{
+#ifndef PNG_USER_MEM_SUPPORTED
+   png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
+      error_fn, warn_fn, NULL, NULL, NULL);
+#else
+   return png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
+       warn_fn, NULL, NULL, NULL);
+}
+
+/* Alternate create PNG structure for reading, and allocate any memory
+ * needed.
+ */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+{
+   png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
+      error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
+#endif /* USER_MEM */
+
+   if (png_ptr != NULL)
+   {
+      png_ptr->mode = PNG_IS_READ_STRUCT;
+
+      /* Added in libpng-1.6.0; this can be used to detect a read structure if
+       * required (it will be zero in a write structure.)
+       */
+#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+         png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE;
+#     endif
+
+#     ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED
+         png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
+
+         /* In stable builds only warn if an application error can be completely
+          * handled.
+          */
+#        if PNG_RELEASE_BUILD
+            png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
+#        endif
+#     endif
+
+      /* TODO: delay this, it can be done in png_init_io (if the app doesn't
+       * do it itself) avoiding setting the default function if it is not
+       * required.
+       */
+      png_set_read_fn(png_ptr, NULL, NULL);
+   }
+
+   return png_ptr;
+}
+
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the information before the actual image data.  This has been
+ * changed in v0.90 to allow reading a file that already has the magic
+ * bytes read from the stream.  You can tell libpng how many bytes have
+ * been read from the beginning of the stream (up to the maximum of 8)
+ * via png_set_sig_bytes(), and we will only check the remaining bytes
+ * here.  The application can then have access to the signature bytes we
+ * read if it is determined that this isn't a valid PNG file.
+ */
+void PNGAPI
+png_read_info(png_structrp png_ptr, png_inforp info_ptr)
+{
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   int keep;
+#endif
+
+   png_debug(1, "in png_read_info");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* Read and check the PNG file signature. */
+   png_read_sig(png_ptr, info_ptr);
+
+   for (;;)
+   {
+      png_uint_32 length = png_read_chunk_header(png_ptr);
+      png_uint_32 chunk_name = png_ptr->chunk_name;
+
+      /* IDAT logic needs to happen here to simplify getting the two flags
+       * right.
+       */
+      if (chunk_name == png_IDAT)
+      {
+         if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+            png_chunk_error(png_ptr, "Missing IHDR before IDAT");
+
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+             (png_ptr->mode & PNG_HAVE_PLTE) == 0)
+            png_chunk_error(png_ptr, "Missing PLTE before IDAT");
+
+         else if ((png_ptr->mode & PNG_AFTER_IDAT) != 0)
+            png_chunk_benign_error(png_ptr, "Too many IDATs found");
+
+         png_ptr->mode |= PNG_HAVE_IDAT;
+      }
+
+      else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+         png_ptr->mode |= PNG_AFTER_IDAT;
+
+      /* This should be a binary subdivision search or a hash for
+       * matching the chunk name rather than a linear search.
+       */
+      if (chunk_name == png_IHDR)
+         png_handle_IHDR(png_ptr, info_ptr, length);
+
+      else if (chunk_name == png_IEND)
+         png_handle_IEND(png_ptr, info_ptr, length);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
+      {
+         png_handle_unknown(png_ptr, info_ptr, length, keep);
+
+         if (chunk_name == png_PLTE)
+            png_ptr->mode |= PNG_HAVE_PLTE;
+
+         else if (chunk_name == png_IDAT)
+         {
+            png_ptr->idat_size = 0; /* It has been consumed */
+            break;
+         }
+      }
+#endif
+      else if (chunk_name == png_PLTE)
+         png_handle_PLTE(png_ptr, info_ptr, length);
+
+      else if (chunk_name == png_IDAT)
+      {
+         png_ptr->idat_size = length;
+         break;
+      }
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+      else if (chunk_name == png_bKGD)
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+      else if (chunk_name == png_cHRM)
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+      else if (chunk_name == png_gAMA)
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+      else if (chunk_name == png_hIST)
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+      else if (chunk_name == png_oFFs)
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+      else if (chunk_name == png_pCAL)
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+      else if (chunk_name == png_sCAL)
+         png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+      else if (chunk_name == png_pHYs)
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+      else if (chunk_name == png_sBIT)
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+      else if (chunk_name == png_sRGB)
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+      else if (chunk_name == png_iCCP)
+         png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+      else if (chunk_name == png_sPLT)
+         png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+      else if (chunk_name == png_tEXt)
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+      else if (chunk_name == png_tIME)
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+      else if (chunk_name == png_tRNS)
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+      else if (chunk_name == png_zTXt)
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+      else if (chunk_name == png_iTXt)
+         png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+
+      else
+         png_handle_unknown(png_ptr, info_ptr, length,
+            PNG_HANDLE_CHUNK_AS_DEFAULT);
+   }
+}
+#endif /* SEQUENTIAL_READ */
+
+/* Optional call to update the users info_ptr structure */
+void PNGAPI
+png_read_update_info(png_structrp png_ptr, png_inforp info_ptr)
+{
+   png_debug(1, "in png_read_update_info");
+
+   if (png_ptr != NULL)
+   {
+      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
+      {
+         png_read_start_row(png_ptr);
+
+#        ifdef PNG_READ_TRANSFORMS_SUPPORTED
+            png_read_transform_info(png_ptr, info_ptr);
+#        else
+            PNG_UNUSED(info_ptr)
+#        endif
+      }
+
+      /* New in 1.6.0 this avoids the bug of doing the initializations twice */
+      else
+         png_app_error(png_ptr,
+            "png_read_update_info/png_start_read_image: duplicate call");
+   }
+}
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Initialize palette, background, etc, after transformations
+ * are set, but before any reading takes place.  This allows
+ * the user to obtain a gamma-corrected palette, for example.
+ * If the user doesn't call this, we will do it ourselves.
+ */
+void PNGAPI
+png_start_read_image(png_structrp png_ptr)
+{
+   png_debug(1, "in png_start_read_image");
+
+   if (png_ptr != NULL)
+   {
+      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
+         png_read_start_row(png_ptr);
+
+      /* New in 1.6.0 this avoids the bug of doing the initializations twice */
+      else
+         png_app_error(png_ptr,
+            "png_start_read_image/png_read_update_info: duplicate call");
+   }
+}
+#endif /* SEQUENTIAL_READ */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+/* Undoes intrapixel differencing,
+ * NOTE: this is apparently only supported in the 'sequential' reader.
+ */
+static void
+png_do_read_intrapixel(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_intrapixel");
+
+   if (
+       (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      int bytes_per_pixel;
+      png_uint_32 row_width = row_info->width;
+
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 3;
+
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 4;
+
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff);
+            *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff);
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 6;
+
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 8;
+
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);
+            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);
+            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);
+            png_uint_32 red  = (s0 + s1 + 65536) & 0xffff;
+            png_uint_32 blue = (s2 + s1 + 65536) & 0xffff;
+            *(rp    ) = (png_byte)((red >> 8) & 0xff);
+            *(rp + 1) = (png_byte)(red & 0xff);
+            *(rp + 4) = (png_byte)((blue >> 8) & 0xff);
+            *(rp + 5) = (png_byte)(blue & 0xff);
+         }
+      }
+   }
+}
+#endif /* MNG_FEATURES */
+
+void PNGAPI
+png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
+{
+   png_row_info row_info;
+
+   if (png_ptr == NULL)
+      return;
+
+   png_debug2(1, "in png_read_row (row %lu, pass %d)",
+       (unsigned long)png_ptr->row_number, png_ptr->pass);
+
+   /* png_read_start_row sets the information (in particular iwidth) for this
+    * interlace pass.
+    */
+   if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
+      png_read_start_row(png_ptr);
+
+   /* 1.5.6: row_info moved out of png_struct to a local here. */
+   row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */
+   row_info.color_type = png_ptr->color_type;
+   row_info.bit_depth = png_ptr->bit_depth;
+   row_info.channels = png_ptr->channels;
+   row_info.pixel_depth = png_ptr->pixel_depth;
+   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
+
+#ifdef PNG_WARNINGS_SUPPORTED
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* Check for transforms that have been set but were defined out */
+#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
+   if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
+      png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
+   if ((png_ptr->transformations & PNG_FILLER) != 0)
+      png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
+    !defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
+      png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) != 0)
+      png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
+   if ((png_ptr->transformations & PNG_SHIFT) != 0)
+      png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
+   if ((png_ptr->transformations & PNG_BGR) != 0)
+      png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
+   if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
+      png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined");
+#endif
+   }
+#endif /* WARNINGS */
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   /* If interlaced and we do not need a new row, combine row and return.
+    * Notice that the pixels we have from previous rows have been transformed
+    * already; we can only combine like with like (transformed or
+    * untransformed) and, because of the libpng API for interlaced images, this
+    * means we must transform before de-interlacing.
+    */
+   if (png_ptr->interlaced != 0 &&
+       (png_ptr->transformations & PNG_INTERLACE) != 0)
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 0x07)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 1:
+            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
+
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 2:
+            if ((png_ptr->row_number & 0x07) != 4)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 4))
+                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
+
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
+
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 2))
+                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
+
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row, 1/*display*/);
+
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         default:
+         case 6:
+            if ((png_ptr->row_number & 1) == 0)
+            {
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
+      png_error(png_ptr, "Invalid attempt to read row data");
+
+   /* Fill the row with IDAT data: */
+   png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1);
+
+   if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE)
+   {
+      if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST)
+         png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1,
+            png_ptr->prev_row + 1, png_ptr->row_buf[0]);
+      else
+         png_error(png_ptr, "bad adaptive filter value");
+   }
+
+   /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before
+    * 1.5.6, while the buffer really is this big in current versions of libpng
+    * it may not be in the future, so this was changed just to copy the
+    * interlaced count:
+    */
+   memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1);
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+   {
+      /* Intrapixel differencing */
+      png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1);
+   }
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+   if (png_ptr->transformations)
+      png_do_read_transformations(png_ptr, &row_info);
+#endif
+
+   /* The transformed pixel depth should match the depth now in row_info. */
+   if (png_ptr->transformed_pixel_depth == 0)
+   {
+      png_ptr->transformed_pixel_depth = row_info.pixel_depth;
+      if (row_info.pixel_depth > png_ptr->maximum_pixel_depth)
+         png_error(png_ptr, "sequential row overflow");
+   }
+
+   else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth)
+      png_error(png_ptr, "internal sequential row size calculation error");
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   /* Expand interlaced rows to full size */
+   if (png_ptr->interlaced != 0 &&
+      (png_ptr->transformations & PNG_INTERLACE) != 0)
+   {
+      if (png_ptr->pass < 6)
+         png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass,
+            png_ptr->transformations);
+
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row, 1/*display*/);
+
+      if (row != NULL)
+         png_combine_row(png_ptr, row, 0/*row*/);
+   }
+
+   else
+#endif
+   {
+      if (row != NULL)
+         png_combine_row(png_ptr, row, -1/*ignored*/);
+
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row, -1/*ignored*/);
+   }
+   png_read_finish_row(png_ptr);
+
+   if (png_ptr->read_row_fn != NULL)
+      (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+
+}
+#endif /* SEQUENTIAL_READ */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read one or more rows of image data.  If the image is interlaced,
+ * and png_set_interlace_handling() has been called, the rows need to
+ * contain the contents of the rows from the previous pass.  If the
+ * image has alpha or transparency, and png_handle_alpha()[*] has been
+ * called, the rows contents must be initialized to the contents of the
+ * screen.
+ *
+ * "row" holds the actual image, and pixels are placed in it
+ * as they arrive.  If the image is displayed after each pass, it will
+ * appear to "sparkle" in.  "display_row" can be used to display a
+ * "chunky" progressive image, with finer detail added as it becomes
+ * available.  If you do not want this "chunky" display, you may pass
+ * NULL for display_row.  If you do not want the sparkle display, and
+ * you have not called png_handle_alpha(), you may pass NULL for rows.
+ * If you have called png_handle_alpha(), and the image has either an
+ * alpha channel or a transparency chunk, you must provide a buffer for
+ * rows.  In this case, you do not have to provide a display_row buffer
+ * also, but you may.  If the image is not interlaced, or if you have
+ * not called png_set_interlace_handling(), the display_row buffer will
+ * be ignored, so pass NULL to it.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of this version of libpng
+ */
+
+void PNGAPI
+png_read_rows(png_structrp png_ptr, png_bytepp row,
+    png_bytepp display_row, png_uint_32 num_rows)
+{
+   png_uint_32 i;
+   png_bytepp rp;
+   png_bytepp dp;
+
+   png_debug(1, "in png_read_rows");
+
+   if (png_ptr == NULL)
+      return;
+
+   rp = row;
+   dp = display_row;
+   if (rp != NULL && dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp++;
+         png_bytep dptr = *dp++;
+
+         png_read_row(png_ptr, rptr, dptr);
+      }
+
+   else if (rp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp;
+         png_read_row(png_ptr, rptr, NULL);
+         rp++;
+      }
+
+   else if (dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep dptr = *dp;
+         png_read_row(png_ptr, NULL, dptr);
+         dp++;
+      }
+}
+#endif /* SEQUENTIAL_READ */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the entire image.  If the image has an alpha channel or a tRNS
+ * chunk, and you have called png_handle_alpha()[*], you will need to
+ * initialize the image to the current image that PNG will be overlaying.
+ * We set the num_rows again here, in case it was incorrectly set in
+ * png_read_start_row() by a call to png_read_update_info() or
+ * png_start_read_image() if png_set_interlace_handling() wasn't called
+ * prior to either of these functions like it should have been.  You can
+ * only call this function once.  If you desire to have an image for
+ * each pass of a interlaced image, use png_read_rows() instead.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of this version of libpng
+ */
+void PNGAPI
+png_read_image(png_structrp png_ptr, png_bytepp image)
+{
+   png_uint_32 i, image_height;
+   int pass, j;
+   png_bytepp rp;
+
+   png_debug(1, "in png_read_image");
+
+   if (png_ptr == NULL)
+      return;
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
+   {
+      pass = png_set_interlace_handling(png_ptr);
+      /* And make sure transforms are initialized. */
+      png_start_read_image(png_ptr);
+   }
+   else
+   {
+      if (png_ptr->interlaced != 0 &&
+          (png_ptr->transformations & PNG_INTERLACE) == 0)
+      {
+         /* Caller called png_start_read_image or png_read_update_info without
+          * first turning on the PNG_INTERLACE transform.  We can fix this here,
+          * but the caller should do it!
+          */
+         png_warning(png_ptr, "Interlace handling should be turned on when "
+            "using png_read_image");
+         /* Make sure this is set correctly */
+         png_ptr->num_rows = png_ptr->height;
+      }
+
+      /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in
+       * the above error case.
+       */
+      pass = png_set_interlace_handling(png_ptr);
+   }
+#else
+   if (png_ptr->interlaced)
+      png_error(png_ptr,
+          "Cannot read interlaced image -- interlace handler disabled");
+
+   pass = 1;
+#endif
+
+   image_height=png_ptr->height;
+
+   for (j = 0; j < pass; j++)
+   {
+      rp = image;
+      for (i = 0; i < image_height; i++)
+      {
+         png_read_row(png_ptr, *rp, NULL);
+         rp++;
+      }
+   }
+}
+#endif /* SEQUENTIAL_READ */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the end of the PNG file.  Will not read past the end of the
+ * file, will verify the end is accurate, and will read any comments
+ * or time information at the end of the file, if info is not NULL.
+ */
+void PNGAPI
+png_read_end(png_structrp png_ptr, png_inforp info_ptr)
+{
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   int keep;
+#endif
+
+   png_debug(1, "in png_read_end");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* If png_read_end is called in the middle of reading the rows there may
+    * still be pending IDAT data and an owned zstream.  Deal with this here.
+    */
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   if (png_chunk_unknown_handling(png_ptr, png_IDAT) == 0)
+#endif
+      png_read_finish_IDAT(png_ptr);
+
+#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+   /* Report invalid palette index; added at libng-1.5.10 */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+      png_ptr->num_palette_max > png_ptr->num_palette)
+     png_benign_error(png_ptr, "Read palette index exceeding num_palette");
+#endif
+
+   do
+   {
+      png_uint_32 length = png_read_chunk_header(png_ptr);
+      png_uint_32 chunk_name = png_ptr->chunk_name;
+
+      if (chunk_name == png_IEND)
+         png_handle_IEND(png_ptr, info_ptr, length);
+
+      else if (chunk_name == png_IHDR)
+         png_handle_IHDR(png_ptr, info_ptr, length);
+
+      else if (info_ptr == NULL)
+         png_crc_finish(png_ptr, length);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0)
+      {
+         if (chunk_name == png_IDAT)
+         {
+            if ((length > 0) ||
+                (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0)
+               png_benign_error(png_ptr, "Too many IDATs found");
+         }
+         png_handle_unknown(png_ptr, info_ptr, length, keep);
+         if (chunk_name == png_PLTE)
+            png_ptr->mode |= PNG_HAVE_PLTE;
+      }
+#endif
+
+      else if (chunk_name == png_IDAT)
+      {
+         /* Zero length IDATs are legal after the last IDAT has been
+          * read, but not after other chunks have been read.
+          */
+         if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT) != 0)
+            png_benign_error(png_ptr, "Too many IDATs found");
+
+         png_crc_finish(png_ptr, length);
+      }
+      else if (chunk_name == png_PLTE)
+         png_handle_PLTE(png_ptr, info_ptr, length);
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+      else if (chunk_name == png_bKGD)
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+      else if (chunk_name == png_cHRM)
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+      else if (chunk_name == png_gAMA)
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+      else if (chunk_name == png_hIST)
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+      else if (chunk_name == png_oFFs)
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+      else if (chunk_name == png_pCAL)
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+      else if (chunk_name == png_sCAL)
+         png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+      else if (chunk_name == png_pHYs)
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+      else if (chunk_name == png_sBIT)
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+      else if (chunk_name == png_sRGB)
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+      else if (chunk_name == png_iCCP)
+         png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+      else if (chunk_name == png_sPLT)
+         png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+      else if (chunk_name == png_tEXt)
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+      else if (chunk_name == png_tIME)
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+      else if (chunk_name == png_tRNS)
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+      else if (chunk_name == png_zTXt)
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+      else if (chunk_name == png_iTXt)
+         png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+
+      else
+         png_handle_unknown(png_ptr, info_ptr, length,
+            PNG_HANDLE_CHUNK_AS_DEFAULT);
+   } while ((png_ptr->mode & PNG_HAVE_IEND) == 0);
+}
+#endif /* SEQUENTIAL_READ */
+
+/* Free all memory used in the read struct */
+static void
+png_read_destroy(png_structrp png_ptr)
+{
+   png_debug(1, "in png_read_destroy");
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   png_destroy_gamma_table(png_ptr);
+#endif
+
+   png_free(png_ptr, png_ptr->big_row_buf);
+   png_ptr->big_row_buf = NULL;
+   png_free(png_ptr, png_ptr->big_prev_row);
+   png_ptr->big_prev_row = NULL;
+   png_free(png_ptr, png_ptr->read_buffer);
+   png_ptr->read_buffer = NULL;
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+   png_free(png_ptr, png_ptr->palette_lookup);
+   png_ptr->palette_lookup = NULL;
+   png_free(png_ptr, png_ptr->quantize_index);
+   png_ptr->quantize_index = NULL;
+#endif
+
+   if ((png_ptr->free_me & PNG_FREE_PLTE) != 0)
+   {
+      png_zfree(png_ptr, png_ptr->palette);
+      png_ptr->palette = NULL;
+   }
+   png_ptr->free_me &= ~PNG_FREE_PLTE;
+
+#if defined(PNG_tRNS_SUPPORTED) || \
+    defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if ((png_ptr->free_me & PNG_FREE_TRNS) != 0)
+   {
+      png_free(png_ptr, png_ptr->trans_alpha);
+      png_ptr->trans_alpha = NULL;
+   }
+   png_ptr->free_me &= ~PNG_FREE_TRNS;
+#endif
+
+   inflateEnd(&png_ptr->zstream);
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_free(png_ptr, png_ptr->save_buffer);
+   png_ptr->save_buffer = NULL;
+#endif
+
+#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) && \
+   defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+   png_free(png_ptr, png_ptr->unknown_chunk.data);
+   png_ptr->unknown_chunk.data = NULL;
+#endif
+
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+   png_free(png_ptr, png_ptr->chunk_list);
+   png_ptr->chunk_list = NULL;
+#endif
+
+   /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error
+    * callbacks are still set at this point.  They are required to complete the
+    * destruction of the png_struct itself.
+    */
+}
+
+/* Free all memory used by the read */
+void PNGAPI
+png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,
+    png_infopp end_info_ptr_ptr)
+{
+   png_structrp png_ptr = NULL;
+
+   png_debug(1, "in png_destroy_read_struct");
+
+   if (png_ptr_ptr != NULL)
+      png_ptr = *png_ptr_ptr;
+
+   if (png_ptr == NULL)
+      return;
+
+   /* libpng 1.6.0: use the API to destroy info structs to ensure consistent
+    * behavior.  Prior to 1.6.0 libpng did extra 'info' destruction in this API.
+    * The extra was, apparently, unnecessary yet this hides memory leak bugs.
+    */
+   png_destroy_info_struct(png_ptr, end_info_ptr_ptr);
+   png_destroy_info_struct(png_ptr, info_ptr_ptr);
+
+   *png_ptr_ptr = NULL;
+   png_read_destroy(png_ptr);
+   png_destroy_png_struct(png_ptr);
+}
+
+void PNGAPI
+png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->read_row_fn = read_row_fn;
+}
+
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+void PNGAPI
+png_read_png(png_structrp png_ptr, png_inforp info_ptr,
+                           int transforms,
+                           voidp params)
+{
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* png_read_info() gives us all of the information from the
+    * PNG file before the first IDAT (image data chunk).
+    */
+   png_read_info(png_ptr, info_ptr);
+   if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep)))
+      png_error(png_ptr, "Image is too high to process with png_read_png()");
+
+   /* -------------- image transformations start here ------------------- */
+   /* libpng 1.6.10: add code to cause a png_app_error if a selected TRANSFORM
+    * is not implemented.  This will only happen in de-configured (non-default)
+    * libpng builds.  The results can be unexpected - png_read_png may return
+    * short or mal-formed rows because the transform is skipped.
+    */
+
+   /* Tell libpng to strip 16-bit/color files down to 8 bits per color.
+    */
+   if ((transforms & PNG_TRANSFORM_SCALE_16) != 0)
+      /* Added at libpng-1.5.4. "strip_16" produces the same result that it
+       * did in earlier versions, while "scale_16" is now more accurate.
+       */
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+      png_set_scale_16(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SCALE_16 not supported");
+#endif
+
+   /* If both SCALE and STRIP are required pngrtran will effectively cancel the
+    * latter by doing SCALE first.  This is ok and allows apps not to check for
+    * which is supported to get the right answer.
+    */
+   if ((transforms & PNG_TRANSFORM_STRIP_16) != 0)
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+      png_set_strip_16(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_16 not supported");
+#endif
+
+   /* Strip alpha bytes from the input data without combining with
+    * the background (not recommended).
+    */
+   if ((transforms & PNG_TRANSFORM_STRIP_ALPHA) != 0)
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+      png_set_strip_alpha(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_ALPHA not supported");
+#endif
+
+   /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
+    * byte into separate bytes (useful for paletted and grayscale images).
+    */
+   if ((transforms & PNG_TRANSFORM_PACKING) != 0)
+#ifdef PNG_READ_PACK_SUPPORTED
+      png_set_packing(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported");
+#endif
+
+   /* Change the order of packed pixels to least significant bit first
+    * (not useful if you are using png_set_packing).
+    */
+   if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+      png_set_packswap(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported");
+#endif
+
+   /* Expand paletted colors into true RGB triplets
+    * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel
+    * Expand paletted or RGB images with transparency to full alpha
+    * channels so the data will be available as RGBA quartets.
+    */
+   if ((transforms & PNG_TRANSFORM_EXPAND) != 0)
+#ifdef PNG_READ_EXPAND_SUPPORTED
+      png_set_expand(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND not supported");
+#endif
+
+   /* We don't handle background color or gamma transformation or quantizing.
+    */
+
+   /* Invert monochrome files to have 0 as white and 1 as black
+    */
+   if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
+#ifdef PNG_READ_INVERT_SUPPORTED
+      png_set_invert_mono(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported");
+#endif
+
+   /* If you want to shift the pixel values from the range [0,255] or
+    * [0,65535] to the original [0,7] or [0,31], or whatever range the
+    * colors were originally in:
+    */
+   if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
+#ifdef PNG_READ_SHIFT_SUPPORTED
+      if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
+         png_set_shift(png_ptr, &info_ptr->sig_bit);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
+#endif
+
+   /* Flip the RGB pixels to BGR (or RGBA to BGRA) */
+   if ((transforms & PNG_TRANSFORM_BGR) != 0)
+#ifdef PNG_READ_BGR_SUPPORTED
+      png_set_bgr(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported");
+#endif
+
+   /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
+   if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+      png_set_swap_alpha(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported");
+#endif
+
+   /* Swap bytes of 16-bit files to least significant byte first */
+   if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
+#ifdef PNG_READ_SWAP_SUPPORTED
+      png_set_swap(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported");
+#endif
+
+/* Added at libpng-1.2.41 */
+   /* Invert the alpha channel from opacity to transparency */
+   if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+      png_set_invert_alpha(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported");
+#endif
+
+/* Added at libpng-1.2.41 */
+   /* Expand grayscale image to RGB */
+   if ((transforms & PNG_TRANSFORM_GRAY_TO_RGB) != 0)
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+      png_set_gray_to_rgb(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_GRAY_TO_RGB not supported");
+#endif
+
+/* Added at libpng-1.5.4 */
+   if ((transforms & PNG_TRANSFORM_EXPAND_16) != 0)
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+      png_set_expand_16(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_EXPAND_16 not supported");
+#endif
+
+   /* We don't handle adding filler bytes */
+
+   /* We use png_read_image and rely on that for interlace handling, but we also
+    * call png_read_update_info therefore must turn on interlace handling now:
+    */
+   (void)png_set_interlace_handling(png_ptr);
+
+   /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (i.e., you selected such a transform above).
+    */
+   png_read_update_info(png_ptr, info_ptr);
+
+   /* -------------- image transformations end here ------------------- */
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+   if (info_ptr->row_pointers == NULL)
+   {
+      png_uint_32 iptr;
+
+      info_ptr->row_pointers = png_voidcast(png_bytepp, png_malloc(png_ptr,
+          info_ptr->height * (sizeof (png_bytep))));
+
+      for (iptr=0; iptr<info_ptr->height; iptr++)
+         info_ptr->row_pointers[iptr] = NULL;
+
+      info_ptr->free_me |= PNG_FREE_ROWS;
+
+      for (iptr = 0; iptr < info_ptr->height; iptr++)
+         info_ptr->row_pointers[iptr] = png_voidcast(png_bytep,
+             png_malloc(png_ptr, info_ptr->rowbytes));
+   }
+
+   png_read_image(png_ptr, info_ptr->row_pointers);
+   info_ptr->valid |= PNG_INFO_IDAT;
+
+   /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */
+   png_read_end(png_ptr, info_ptr);
+
+   PNG_UNUSED(params)
+}
+#endif /* INFO_IMAGE */
+#endif /* SEQUENTIAL_READ */
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+/* SIMPLIFIED READ
+ *
+ * This code currently relies on the sequential reader, though it could easily
+ * be made to work with the progressive one.
+ */
+/* Arguments to png_image_finish_read: */
+
+/* Encoding of PNG data (used by the color-map code) */
+#  define P_NOTSET  0 /* File encoding not yet known */
+#  define P_sRGB    1 /* 8-bit encoded to sRGB gamma */
+#  define P_LINEAR  2 /* 16-bit linear: not encoded, NOT pre-multiplied! */
+#  define P_FILE    3 /* 8-bit encoded to file gamma, not sRGB or linear */
+#  define P_LINEAR8 4 /* 8-bit linear: only from a file value */
+
+/* Color-map processing: after libpng has run on the PNG image further
+ * processing may be needed to convert the data to color-map indices.
+ */
+#define PNG_CMAP_NONE      0
+#define PNG_CMAP_GA        1 /* Process GA data to a color-map with alpha */
+#define PNG_CMAP_TRANS     2 /* Process GA data to a background index */
+#define PNG_CMAP_RGB       3 /* Process RGB data */
+#define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */
+
+/* The following document where the background is for each processing case. */
+#define PNG_CMAP_NONE_BACKGROUND      256
+#define PNG_CMAP_GA_BACKGROUND        231
+#define PNG_CMAP_TRANS_BACKGROUND     254
+#define PNG_CMAP_RGB_BACKGROUND       256
+#define PNG_CMAP_RGB_ALPHA_BACKGROUND 216
+
+typedef struct
+{
+   /* Arguments: */
+   png_imagep image;
+   png_voidp  buffer;
+   png_int_32 row_stride;
+   png_voidp  colormap;
+   png_const_colorp background;
+   /* Local variables: */
+   png_voidp       local_row;
+   png_voidp       first_row;
+   ptrdiff_t       row_bytes;           /* step between rows */
+   int             file_encoding;       /* E_ values above */
+   png_fixed_point gamma_to_linear;     /* For P_FILE, reciprocal of gamma */
+   int             colormap_processing; /* PNG_CMAP_ values above */
+} png_image_read_control;
+
+/* Do all the *safe* initialization - 'safe' means that png_error won't be
+ * called, so setting up the jmp_buf is not required.  This means that anything
+ * called from here must *not* call png_malloc - it has to call png_malloc_warn
+ * instead so that control is returned safely back to this routine.
+ */
+static int
+png_image_read_init(png_imagep image)
+{
+   if (image->opaque == NULL)
+   {
+      png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image,
+          png_safe_error, png_safe_warning);
+
+      /* And set the rest of the structure to NULL to ensure that the various
+       * fields are consistent.
+       */
+      memset(image, 0, (sizeof *image));
+      image->version = PNG_IMAGE_VERSION;
+
+      if (png_ptr != NULL)
+      {
+         png_infop info_ptr = png_create_info_struct(png_ptr);
+
+         if (info_ptr != NULL)
+         {
+            png_controlp control = png_voidcast(png_controlp,
+               png_malloc_warn(png_ptr, (sizeof *control)));
+
+            if (control != NULL)
+            {
+               memset(control, 0, (sizeof *control));
+
+               control->png_ptr = png_ptr;
+               control->info_ptr = info_ptr;
+               control->for_write = 0;
+
+               image->opaque = control;
+               return 1;
+            }
+
+            /* Error clean up */
+            png_destroy_info_struct(png_ptr, &info_ptr);
+         }
+
+         png_destroy_read_struct(&png_ptr, NULL, NULL);
+      }
+
+      return png_image_error(image, "png_image_read: out of memory");
+   }
+
+   return png_image_error(image, "png_image_read: opaque pointer not NULL");
+}
+
+/* Utility to find the base format of a PNG file from a png_struct. */
+static png_uint_32
+png_image_format(png_structrp png_ptr)
+{
+   png_uint_32 format = 0;
+
+   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      format |= PNG_FORMAT_FLAG_COLOR;
+
+   if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      format |= PNG_FORMAT_FLAG_ALPHA;
+
+   /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS
+    * sets the png_struct fields; that's all we are interested in here.  The
+    * precise interaction with an app call to png_set_tRNS and PNG file reading
+    * is unclear.
+    */
+   else if (png_ptr->num_trans > 0)
+      format |= PNG_FORMAT_FLAG_ALPHA;
+
+   if (png_ptr->bit_depth == 16)
+      format |= PNG_FORMAT_FLAG_LINEAR;
+
+   if ((png_ptr->color_type & PNG_COLOR_MASK_PALETTE) != 0)
+      format |= PNG_FORMAT_FLAG_COLORMAP;
+
+   return format;
+}
+
+/* Is the given gamma significantly different from sRGB?  The test is the same
+ * one used in pngrtran.c when deciding whether to do gamma correction.  The
+ * arithmetic optimizes the division by using the fact that the inverse of the
+ * file sRGB gamma is 2.2
+ */
+static int
+png_gamma_not_sRGB(png_fixed_point g)
+{
+   if (g < PNG_FP_1)
+   {
+      /* An uninitialized gamma is assumed to be sRGB for the simplified API. */
+      if (g == 0)
+         return 0;
+
+      return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */);
+   }
+
+   return 1;
+}
+
+/* Do the main body of a 'png_image_begin_read' function; read the PNG file
+ * header and fill in all the information.  This is executed in a safe context,
+ * unlike the init routine above.
+ */
+static int
+png_image_read_header(png_voidp argument)
+{
+   png_imagep image = png_voidcast(png_imagep, argument);
+   png_structrp png_ptr = image->opaque->png_ptr;
+   png_inforp info_ptr = image->opaque->info_ptr;
+
+   png_set_benign_errors(png_ptr, 1/*warn*/);
+   png_read_info(png_ptr, info_ptr);
+
+   /* Do this the fast way; just read directly out of png_struct. */
+   image->width = png_ptr->width;
+   image->height = png_ptr->height;
+
+   {
+      png_uint_32 format = png_image_format(png_ptr);
+
+      image->format = format;
+
+#ifdef PNG_COLORSPACE_SUPPORTED
+      /* Does the colorspace match sRGB?  If there is no color endpoint
+       * (colorant) information assume yes, otherwise require the
+       * 'ENDPOINTS_MATCHP_sRGB' colorspace flag to have been set.  If the
+       * colorspace has been determined to be invalid ignore it.
+       */
+      if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags
+         & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB|
+            PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS))
+         image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB;
+#endif
+   }
+
+   /* We need the maximum number of entries regardless of the format the
+    * application sets here.
+    */
+   {
+      png_uint_32 cmap_entries;
+
+      switch (png_ptr->color_type)
+      {
+         case PNG_COLOR_TYPE_GRAY:
+            cmap_entries = 1U << png_ptr->bit_depth;
+            break;
+
+         case PNG_COLOR_TYPE_PALETTE:
+            cmap_entries = png_ptr->num_palette;
+            break;
+
+         default:
+            cmap_entries = 256;
+            break;
+      }
+
+      if (cmap_entries > 256)
+         cmap_entries = 256;
+
+      image->colormap_entries = cmap_entries;
+   }
+
+   return 1;
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+int PNGAPI
+png_image_begin_read_from_stdio(png_imagep image, FILE* file)
+{
+   if (image != NULL && image->version == PNG_IMAGE_VERSION)
+   {
+      if (file != NULL)
+      {
+         if (png_image_read_init(image) != 0)
+         {
+            /* This is slightly evil, but png_init_io doesn't do anything other
+             * than this and we haven't changed the standard IO functions so
+             * this saves a 'safe' function.
+             */
+            image->opaque->png_ptr->io_ptr = file;
+            return png_safe_execute(image, png_image_read_header, image);
+         }
+      }
+
+      else
+         return png_image_error(image,
+            "png_image_begin_read_from_stdio: invalid argument");
+   }
+
+   else if (image != NULL)
+      return png_image_error(image,
+         "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION");
+
+   return 0;
+}
+
+int PNGAPI
+png_image_begin_read_from_file(png_imagep image, const char *file_name)
+{
+   if (image != NULL && image->version == PNG_IMAGE_VERSION)
+   {
+      if (file_name != NULL)
+      {
+         FILE *fp = fopen(file_name, "rb");
+
+         if (fp != NULL)
+         {
+            if (png_image_read_init(image) != 0)
+            {
+               image->opaque->png_ptr->io_ptr = fp;
+               image->opaque->owned_file = 1;
+               return png_safe_execute(image, png_image_read_header, image);
+            }
+
+            /* Clean up: just the opened file. */
+            (void)fclose(fp);
+         }
+
+         else
+            return png_image_error(image, strerror(errno));
+      }
+
+      else
+         return png_image_error(image,
+            "png_image_begin_read_from_file: invalid argument");
+   }
+
+   else if (image != NULL)
+      return png_image_error(image,
+         "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION");
+
+   return 0;
+}
+#endif /* STDIO */
+
+static void PNGCBAPI
+png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need)
+{
+   if (png_ptr != NULL)
+   {
+      png_imagep image = png_voidcast(png_imagep, png_ptr->io_ptr);
+      if (image != NULL)
+      {
+         png_controlp cp = image->opaque;
+         if (cp != NULL)
+         {
+            png_const_bytep memory = cp->memory;
+            png_size_t size = cp->size;
+
+            if (memory != NULL && size >= need)
+            {
+               memcpy(out, memory, need);
+               cp->memory = memory + need;
+               cp->size = size - need;
+               return;
+            }
+
+            png_error(png_ptr, "read beyond end of data");
+         }
+      }
+
+      png_error(png_ptr, "invalid memory read");
+   }
+}
+
+int PNGAPI png_image_begin_read_from_memory(png_imagep image,
+   png_const_voidp memory, png_size_t size)
+{
+   if (image != NULL && image->version == PNG_IMAGE_VERSION)
+   {
+      if (memory != NULL && size > 0)
+      {
+         if (png_image_read_init(image) != 0)
+         {
+            /* Now set the IO functions to read from the memory buffer and
+             * store it into io_ptr.  Again do this in-place to avoid calling a
+             * libpng function that requires error handling.
+             */
+            image->opaque->memory = png_voidcast(png_const_bytep, memory);
+            image->opaque->size = size;
+            image->opaque->png_ptr->io_ptr = image;
+            image->opaque->png_ptr->read_data_fn = png_image_memory_read;
+
+            return png_safe_execute(image, png_image_read_header, image);
+         }
+      }
+
+      else
+         return png_image_error(image,
+            "png_image_begin_read_from_memory: invalid argument");
+   }
+
+   else if (image != NULL)
+      return png_image_error(image,
+         "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION");
+
+   return 0;
+}
+
+/* Utility function to skip chunks that are not used by the simplified image
+ * read functions and an appropriate macro to call it.
+ */
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+static void
+png_image_skip_unused_chunks(png_structrp png_ptr)
+{
+   /* Prepare the reader to ignore all recognized chunks whose data will not
+    * be used, i.e., all chunks recognized by libpng except for those
+    * involved in basic image reading:
+    *
+    *    IHDR, PLTE, IDAT, IEND
+    *
+    * Or image data handling:
+    *
+    *    tRNS, bKGD, gAMA, cHRM, sRGB, [iCCP] and sBIT.
+    *
+    * This provides a small performance improvement and eliminates any
+    * potential vulnerability to security problems in the unused chunks.
+    *
+    * At present the iCCP chunk data isn't used, so iCCP chunk can be ignored
+    * too.  This allows the simplified API to be compiled without iCCP support,
+    * however if the support is there the chunk is still checked to detect
+    * errors (which are unfortunately quite common.)
+    */
+   {
+         static PNG_CONST png_byte chunks_to_process[] = {
+            98,  75,  71,  68, '\0',  /* bKGD */
+            99,  72,  82,  77, '\0',  /* cHRM */
+           103,  65,  77,  65, '\0',  /* gAMA */
+#        ifdef PNG_READ_iCCP_SUPPORTED
+           105,  67,  67,  80, '\0',  /* iCCP */
+#        endif
+           115,  66,  73,  84, '\0',  /* sBIT */
+           115,  82,  71,  66, '\0',  /* sRGB */
+           };
+
+       /* Ignore unknown chunks and all other chunks except for the
+        * IHDR, PLTE, tRNS, IDAT, and IEND chunks.
+        */
+       png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER,
+         NULL, -1);
+
+       /* But do not ignore image data handling chunks */
+       png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT,
+         chunks_to_process, (int)/*SAFE*/(sizeof chunks_to_process)/5);
+    }
+}
+
+#  define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p)
+#else
+#  define PNG_SKIP_CHUNKS(p) ((void)0)
+#endif /* HANDLE_AS_UNKNOWN */
+
+/* The following macro gives the exact rounded answer for all values in the
+ * range 0..255 (it actually divides by 51.2, but the rounding still generates
+ * the correct numbers 0..5
+ */
+#define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8)
+
+/* Utility functions to make particular color-maps */
+static void
+set_file_encoding(png_image_read_control *display)
+{
+   png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma;
+   if (png_gamma_significant(g) != 0)
+   {
+      if (png_gamma_not_sRGB(g) != 0)
+      {
+         display->file_encoding = P_FILE;
+         display->gamma_to_linear = png_reciprocal(g);
+      }
+
+      else
+         display->file_encoding = P_sRGB;
+   }
+
+   else
+      display->file_encoding = P_LINEAR8;
+}
+
+static unsigned int
+decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding)
+{
+   if (encoding == P_FILE) /* double check */
+      encoding = display->file_encoding;
+
+   if (encoding == P_NOTSET) /* must be the file encoding */
+   {
+      set_file_encoding(display);
+      encoding = display->file_encoding;
+   }
+
+   switch (encoding)
+   {
+      case P_FILE:
+         value = png_gamma_16bit_correct(value*257, display->gamma_to_linear);
+         break;
+
+      case P_sRGB:
+         value = png_sRGB_table[value];
+         break;
+
+      case P_LINEAR:
+         break;
+
+      case P_LINEAR8:
+         value *= 257;
+         break;
+
+#ifdef __GNUC__
+      default:
+         png_error(display->image->opaque->png_ptr,
+            "unexpected encoding (internal error)");
+#endif
+   }
+
+   return value;
+}
+
+static png_uint_32
+png_colormap_compose(png_image_read_control *display,
+   png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha,
+   png_uint_32 background, int encoding)
+{
+   /* The file value is composed on the background, the background has the given
+    * encoding and so does the result, the file is encoded with P_FILE and the
+    * file and alpha are 8-bit values.  The (output) encoding will always be
+    * P_LINEAR or P_sRGB.
+    */
+   png_uint_32 f = decode_gamma(display, foreground, foreground_encoding);
+   png_uint_32 b = decode_gamma(display, background, encoding);
+
+   /* The alpha is always an 8-bit value (it comes from the palette), the value
+    * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires.
+    */
+   f = f * alpha + b * (255-alpha);
+
+   if (encoding == P_LINEAR)
+   {
+      /* Scale to 65535; divide by 255, approximately (in fact this is extremely
+       * accurate, it divides by 255.00000005937181414556, with no overflow.)
+       */
+      f *= 257; /* Now scaled by 65535 */
+      f += f >> 16;
+      f = (f+32768) >> 16;
+   }
+
+   else /* P_sRGB */
+      f = PNG_sRGB_FROM_LINEAR(f);
+
+   return f;
+}
+
+/* NOTE: P_LINEAR values to this routine must be 16-bit, but P_FILE values must
+ * be 8-bit.
+ */
+static void
+png_create_colormap_entry(png_image_read_control *display,
+   png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue,
+   png_uint_32 alpha, int encoding)
+{
+   png_imagep image = display->image;
+   const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
+      P_LINEAR : P_sRGB;
+   const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 &&
+      (red != green || green != blue);
+
+   if (ip > 255)
+      png_error(image->opaque->png_ptr, "color-map index out of range");
+
+   /* Update the cache with whether the file gamma is significantly different
+    * from sRGB.
+    */
+   if (encoding == P_FILE)
+   {
+      if (display->file_encoding == P_NOTSET)
+         set_file_encoding(display);
+
+      /* Note that the cached value may be P_FILE too, but if it is then the
+       * gamma_to_linear member has been set.
+       */
+      encoding = display->file_encoding;
+   }
+
+   if (encoding == P_FILE)
+   {
+      png_fixed_point g = display->gamma_to_linear;
+
+      red = png_gamma_16bit_correct(red*257, g);
+      green = png_gamma_16bit_correct(green*257, g);
+      blue = png_gamma_16bit_correct(blue*257, g);
+
+      if (convert_to_Y != 0 || output_encoding == P_LINEAR)
+      {
+         alpha *= 257;
+         encoding = P_LINEAR;
+      }
+
+      else
+      {
+         red = PNG_sRGB_FROM_LINEAR(red * 255);
+         green = PNG_sRGB_FROM_LINEAR(green * 255);
+         blue = PNG_sRGB_FROM_LINEAR(blue * 255);
+         encoding = P_sRGB;
+      }
+   }
+
+   else if (encoding == P_LINEAR8)
+   {
+      /* This encoding occurs quite frequently in test cases because PngSuite
+       * includes a gAMA 1.0 chunk with most images.
+       */
+      red *= 257;
+      green *= 257;
+      blue *= 257;
+      alpha *= 257;
+      encoding = P_LINEAR;
+   }
+
+   else if (encoding == P_sRGB &&
+       (convert_to_Y  != 0 || output_encoding == P_LINEAR))
+   {
+      /* The values are 8-bit sRGB values, but must be converted to 16-bit
+       * linear.
+       */
+      red = png_sRGB_table[red];
+      green = png_sRGB_table[green];
+      blue = png_sRGB_table[blue];
+      alpha *= 257;
+      encoding = P_LINEAR;
+   }
+
+   /* This is set if the color isn't gray but the output is. */
+   if (encoding == P_LINEAR)
+   {
+      if (convert_to_Y != 0)
+      {
+         /* NOTE: these values are copied from png_do_rgb_to_gray */
+         png_uint_32 y = (png_uint_32)6968 * red  + (png_uint_32)23434 * green +
+            (png_uint_32)2366 * blue;
+
+         if (output_encoding == P_LINEAR)
+            y = (y + 16384) >> 15;
+
+         else
+         {
+            /* y is scaled by 32768, we need it scaled by 255: */
+            y = (y + 128) >> 8;
+            y *= 255;
+            y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7);
+            alpha = PNG_DIV257(alpha);
+            encoding = P_sRGB;
+         }
+
+         blue = red = green = y;
+      }
+
+      else if (output_encoding == P_sRGB)
+      {
+         red = PNG_sRGB_FROM_LINEAR(red * 255);
+         green = PNG_sRGB_FROM_LINEAR(green * 255);
+         blue = PNG_sRGB_FROM_LINEAR(blue * 255);
+         alpha = PNG_DIV257(alpha);
+         encoding = P_sRGB;
+      }
+   }
+
+   if (encoding != output_encoding)
+      png_error(image->opaque->png_ptr, "bad encoding (internal error)");
+
+   /* Store the value. */
+   {
+#     ifdef PNG_FORMAT_AFIRST_SUPPORTED
+         const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
+            (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
+#     else
+#        define afirst 0
+#     endif
+#     ifdef PNG_FORMAT_BGR_SUPPORTED
+         const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
+#     else
+#        define bgr 0
+#     endif
+
+      if (output_encoding == P_LINEAR)
+      {
+         png_uint_16p entry = png_voidcast(png_uint_16p, display->colormap);
+
+         entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);
+
+         /* The linear 16-bit values must be pre-multiplied by the alpha channel
+          * value, if less than 65535 (this is, effectively, composite on black
+          * if the alpha channel is removed.)
+          */
+         switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))
+         {
+            case 4:
+               entry[afirst ? 0 : 3] = (png_uint_16)alpha;
+               /* FALL THROUGH */
+
+            case 3:
+               if (alpha < 65535)
+               {
+                  if (alpha > 0)
+                  {
+                     blue = (blue * alpha + 32767U)/65535U;
+                     green = (green * alpha + 32767U)/65535U;
+                     red = (red * alpha + 32767U)/65535U;
+                  }
+
+                  else
+                     red = green = blue = 0;
+               }
+               entry[afirst + (2 ^ bgr)] = (png_uint_16)blue;
+               entry[afirst + 1] = (png_uint_16)green;
+               entry[afirst + bgr] = (png_uint_16)red;
+               break;
+
+            case 2:
+               entry[1 ^ afirst] = (png_uint_16)alpha;
+               /* FALL THROUGH */
+
+            case 1:
+               if (alpha < 65535)
+               {
+                  if (alpha > 0)
+                     green = (green * alpha + 32767U)/65535U;
+
+                  else
+                     green = 0;
+               }
+               entry[afirst] = (png_uint_16)green;
+               break;
+
+            default:
+               break;
+         }
+      }
+
+      else /* output encoding is P_sRGB */
+      {
+         png_bytep entry = png_voidcast(png_bytep, display->colormap);
+
+         entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format);
+
+         switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format))
+         {
+            case 4:
+               entry[afirst ? 0 : 3] = (png_byte)alpha;
+            case 3:
+               entry[afirst + (2 ^ bgr)] = (png_byte)blue;
+               entry[afirst + 1] = (png_byte)green;
+               entry[afirst + bgr] = (png_byte)red;
+               break;
+
+            case 2:
+               entry[1 ^ afirst] = (png_byte)alpha;
+            case 1:
+               entry[afirst] = (png_byte)green;
+               break;
+
+            default:
+               break;
+         }
+      }
+
+#     ifdef afirst
+#        undef afirst
+#     endif
+#     ifdef bgr
+#        undef bgr
+#     endif
+   }
+}
+
+static int
+make_gray_file_colormap(png_image_read_control *display)
+{
+   unsigned int i;
+
+   for (i=0; i<256; ++i)
+      png_create_colormap_entry(display, i, i, i, i, 255, P_FILE);
+
+   return i;
+}
+
+static int
+make_gray_colormap(png_image_read_control *display)
+{
+   unsigned int i;
+
+   for (i=0; i<256; ++i)
+      png_create_colormap_entry(display, i, i, i, i, 255, P_sRGB);
+
+   return i;
+}
+#define PNG_GRAY_COLORMAP_ENTRIES 256
+
+static int
+make_ga_colormap(png_image_read_control *display)
+{
+   unsigned int i, a;
+
+   /* Alpha is retained, the output will be a color-map with entries
+    * selected by six levels of alpha.  One transparent entry, 6 gray
+    * levels for all the intermediate alpha values, leaving 230 entries
+    * for the opaque grays.  The color-map entries are the six values
+    * [0..5]*51, the GA processing uses PNG_DIV51(value) to find the
+    * relevant entry.
+    *
+    * if (alpha > 229) // opaque
+    * {
+    *    // The 231 entries are selected to make the math below work:
+    *    base = 0;
+    *    entry = (231 * gray + 128) >> 8;
+    * }
+    * else if (alpha < 26) // transparent
+    * {
+    *    base = 231;
+    *    entry = 0;
+    * }
+    * else // partially opaque
+    * {
+    *    base = 226 + 6 * PNG_DIV51(alpha);
+    *    entry = PNG_DIV51(gray);
+    * }
+    */
+   i = 0;
+   while (i < 231)
+   {
+      unsigned int gray = (i * 256 + 115) / 231;
+      png_create_colormap_entry(display, i++, gray, gray, gray, 255, P_sRGB);
+   }
+
+   /* 255 is used here for the component values for consistency with the code
+    * that undoes premultiplication in pngwrite.c.
+    */
+   png_create_colormap_entry(display, i++, 255, 255, 255, 0, P_sRGB);
+
+   for (a=1; a<5; ++a)
+   {
+      unsigned int g;
+
+      for (g=0; g<6; ++g)
+         png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51,
+            P_sRGB);
+   }
+
+   return i;
+}
+
+#define PNG_GA_COLORMAP_ENTRIES 256
+
+static int
+make_rgb_colormap(png_image_read_control *display)
+{
+   unsigned int i, r;
+
+   /* Build a 6x6x6 opaque RGB cube */
+   for (i=r=0; r<6; ++r)
+   {
+      unsigned int g;
+
+      for (g=0; g<6; ++g)
+      {
+         unsigned int b;
+
+         for (b=0; b<6; ++b)
+            png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255,
+               P_sRGB);
+      }
+   }
+
+   return i;
+}
+
+#define PNG_RGB_COLORMAP_ENTRIES 216
+
+/* Return a palette index to the above palette given three 8-bit sRGB values. */
+#define PNG_RGB_INDEX(r,g,b) \
+   ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b)))
+
+static int
+png_image_read_colormap(png_voidp argument)
+{
+   png_image_read_control *display =
+      png_voidcast(png_image_read_control*, argument);
+   const png_imagep image = display->image;
+
+   const png_structrp png_ptr = image->opaque->png_ptr;
+   const png_uint_32 output_format = image->format;
+   const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) != 0 ?
+      P_LINEAR : P_sRGB;
+
+   unsigned int cmap_entries;
+   unsigned int output_processing;        /* Output processing option */
+   unsigned int data_encoding = P_NOTSET; /* Encoding libpng must produce */
+
+   /* Background information; the background color and the index of this color
+    * in the color-map if it exists (else 256).
+    */
+   unsigned int background_index = 256;
+   png_uint_32 back_r, back_g, back_b;
+
+   /* Flags to accumulate things that need to be done to the input. */
+   int expand_tRNS = 0;
+
+   /* Exclude the NYI feature of compositing onto a color-mapped buffer; it is
+    * very difficult to do, the results look awful, and it is difficult to see
+    * what possible use it is because the application can't control the
+    * color-map.
+    */
+   if (((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 ||
+         png_ptr->num_trans > 0) /* alpha in input */ &&
+      ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) /* no alpha in output */)
+   {
+      if (output_encoding == P_LINEAR) /* compose on black */
+         back_b = back_g = back_r = 0;
+
+      else if (display->background == NULL /* no way to remove it */)
+         png_error(png_ptr,
+            "a background color must be supplied to remove alpha/transparency");
+
+      /* Get a copy of the background color (this avoids repeating the checks
+       * below.)  The encoding is 8-bit sRGB or 16-bit linear, depending on the
+       * output format.
+       */
+      else
+      {
+         back_g = display->background->green;
+         if ((output_format & PNG_FORMAT_FLAG_COLOR) != 0)
+         {
+            back_r = display->background->red;
+            back_b = display->background->blue;
+         }
+         else
+            back_b = back_r = back_g;
+      }
+   }
+
+   else if (output_encoding == P_LINEAR)
+      back_b = back_r = back_g = 65535;
+
+   else
+      back_b = back_r = back_g = 255;
+
+   /* Default the input file gamma if required - this is necessary because
+    * libpng assumes that if no gamma information is present the data is in the
+    * output format, but the simplified API deduces the gamma from the input
+    * format.
+    */
+   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0)
+   {
+      /* Do this directly, not using the png_colorspace functions, to ensure
+       * that it happens even if the colorspace is invalid (though probably if
+       * it is the setting will be ignored)  Note that the same thing can be
+       * achieved at the application interface with png_set_gAMA.
+       */
+      if (png_ptr->bit_depth == 16 &&
+         (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
+         png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR;
+
+      else
+         png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE;
+
+      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+   }
+
+   /* Decide what to do based on the PNG color type of the input data.  The
+    * utility function png_create_colormap_entry deals with most aspects of the
+    * output transformations; this code works out how to produce bytes of
+    * color-map entries from the original format.
+    */
+   switch (png_ptr->color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+         if (png_ptr->bit_depth <= 8)
+         {
+            /* There at most 256 colors in the output, regardless of
+             * transparency.
+             */
+            unsigned int step, i, val, trans = 256/*ignore*/, back_alpha = 0;
+
+            cmap_entries = 1U << png_ptr->bit_depth;
+            if (cmap_entries > image->colormap_entries)
+               png_error(png_ptr, "gray[8] color-map: too few entries");
+
+            step = 255 / (cmap_entries - 1);
+            output_processing = PNG_CMAP_NONE;
+
+            /* If there is a tRNS chunk then this either selects a transparent
+             * value or, if the output has no alpha, the background color.
+             */
+            if (png_ptr->num_trans > 0)
+            {
+               trans = png_ptr->trans_color.gray;
+
+               if ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0)
+                  back_alpha = output_encoding == P_LINEAR ? 65535 : 255;
+            }
+
+            /* png_create_colormap_entry just takes an RGBA and writes the
+             * corresponding color-map entry using the format from 'image',
+             * including the required conversion to sRGB or linear as
+             * appropriate.  The input values are always either sRGB (if the
+             * gamma correction flag is 0) or 0..255 scaled file encoded values
+             * (if the function must gamma correct them).
+             */
+            for (i=val=0; i<cmap_entries; ++i, val += step)
+            {
+               /* 'i' is a file value.  While this will result in duplicated
+                * entries for 8-bit non-sRGB encoded files it is necessary to
+                * have non-gamma corrected values to do tRNS handling.
+                */
+               if (i != trans)
+                  png_create_colormap_entry(display, i, val, val, val, 255,
+                     P_FILE/*8-bit with file gamma*/);
+
+               /* Else this entry is transparent.  The colors don't matter if
+                * there is an alpha channel (back_alpha == 0), but it does no
+                * harm to pass them in; the values are not set above so this
+                * passes in white.
+                *
+                * NOTE: this preserves the full precision of the application
+                * supplied background color when it is used.
+                */
+               else
+                  png_create_colormap_entry(display, i, back_r, back_g, back_b,
+                     back_alpha, output_encoding);
+            }
+
+            /* We need libpng to preserve the original encoding. */
+            data_encoding = P_FILE;
+
+            /* The rows from libpng, while technically gray values, are now also
+             * color-map indices; however, they may need to be expanded to 1
+             * byte per pixel.  This is what png_set_packing does (i.e., it
+             * unpacks the bit values into bytes.)
+             */
+            if (png_ptr->bit_depth < 8)
+               png_set_packing(png_ptr);
+         }
+
+         else /* bit depth is 16 */
+         {
+            /* The 16-bit input values can be converted directly to 8-bit gamma
+             * encoded values; however, if a tRNS chunk is present 257 color-map
+             * entries are required.  This means that the extra entry requires
+             * special processing; add an alpha channel, sacrifice gray level
+             * 254 and convert transparent (alpha==0) entries to that.
+             *
+             * Use libpng to chop the data to 8 bits.  Convert it to sRGB at the
+             * same time to minimize quality loss.  If a tRNS chunk is present
+             * this means libpng must handle it too; otherwise it is impossible
+             * to do the exact match on the 16-bit value.
+             *
+             * If the output has no alpha channel *and* the background color is
+             * gray then it is possible to let libpng handle the substitution by
+             * ensuring that the corresponding gray level matches the background
+             * color exactly.
+             */
+            data_encoding = P_sRGB;
+
+            if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
+               png_error(png_ptr, "gray[16] color-map: too few entries");
+
+            cmap_entries = make_gray_colormap(display);
+
+            if (png_ptr->num_trans > 0)
+            {
+               unsigned int back_alpha;
+
+               if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+                  back_alpha = 0;
+
+               else
+               {
+                  if (back_r == back_g && back_g == back_b)
+                  {
+                     /* Background is gray; no special processing will be
+                      * required.
+                      */
+                     png_color_16 c;
+                     png_uint_32 gray = back_g;
+
+                     if (output_encoding == P_LINEAR)
+                     {
+                        gray = PNG_sRGB_FROM_LINEAR(gray * 255);
+
+                        /* And make sure the corresponding palette entry
+                         * matches.
+                         */
+                        png_create_colormap_entry(display, gray, back_g, back_g,
+                           back_g, 65535, P_LINEAR);
+                     }
+
+                     /* The background passed to libpng, however, must be the
+                      * sRGB value.
+                      */
+                     c.index = 0; /*unused*/
+                     c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
+
+                     /* NOTE: does this work without expanding tRNS to alpha?
+                      * It should be the color->gray case below apparently
+                      * doesn't.
+                      */
+                     png_set_background_fixed(png_ptr, &c,
+                        PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                        0/*gamma: not used*/);
+
+                     output_processing = PNG_CMAP_NONE;
+                     break;
+                  }
+#ifdef __COVERITY__
+                 /* Coverity claims that output_encoding cannot be 2 (P_LINEAR)
+                  * here.
+                  */
+                  back_alpha = 255;
+#else
+                  back_alpha = output_encoding == P_LINEAR ? 65535 : 255;
+#endif
+               }
+
+               /* output_processing means that the libpng-processed row will be
+                * 8-bit GA and it has to be processing to single byte color-map
+                * values.  Entry 254 is replaced by either a completely
+                * transparent entry or by the background color at full
+                * precision (and the background color is not a simple gray
+                * level in this case.)
+                */
+               expand_tRNS = 1;
+               output_processing = PNG_CMAP_TRANS;
+               background_index = 254;
+
+               /* And set (overwrite) color-map entry 254 to the actual
+                * background color at full precision.
+                */
+               png_create_colormap_entry(display, 254, back_r, back_g, back_b,
+                  back_alpha, output_encoding);
+            }
+
+            else
+               output_processing = PNG_CMAP_NONE;
+         }
+         break;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         /* 8-bit or 16-bit PNG with two channels - gray and alpha.  A minimum
+          * of 65536 combinations.  If, however, the alpha channel is to be
+          * removed there are only 256 possibilities if the background is gray.
+          * (Otherwise there is a subset of the 65536 possibilities defined by
+          * the triangle between black, white and the background color.)
+          *
+          * Reduce 16-bit files to 8-bit and sRGB encode the result.  No need to
+          * worry about tRNS matching - tRNS is ignored if there is an alpha
+          * channel.
+          */
+         data_encoding = P_sRGB;
+
+         if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+         {
+            if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
+               png_error(png_ptr, "gray+alpha color-map: too few entries");
+
+            cmap_entries = make_ga_colormap(display);
+
+            background_index = PNG_CMAP_GA_BACKGROUND;
+            output_processing = PNG_CMAP_GA;
+         }
+
+         else /* alpha is removed */
+         {
+            /* Alpha must be removed as the PNG data is processed when the
+             * background is a color because the G and A channels are
+             * independent and the vector addition (non-parallel vectors) is a
+             * 2-D problem.
+             *
+             * This can be reduced to the same algorithm as above by making a
+             * colormap containing gray levels (for the opaque grays), a
+             * background entry (for a transparent pixel) and a set of four six
+             * level color values, one set for each intermediate alpha value.
+             * See the comments in make_ga_colormap for how this works in the
+             * per-pixel processing.
+             *
+             * If the background is gray, however, we only need a 256 entry gray
+             * level color map.  It is sufficient to make the entry generated
+             * for the background color be exactly the color specified.
+             */
+            if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0 ||
+               (back_r == back_g && back_g == back_b))
+            {
+               /* Background is gray; no special processing will be required. */
+               png_color_16 c;
+               png_uint_32 gray = back_g;
+
+               if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
+                  png_error(png_ptr, "gray-alpha color-map: too few entries");
+
+               cmap_entries = make_gray_colormap(display);
+
+               if (output_encoding == P_LINEAR)
+               {
+                  gray = PNG_sRGB_FROM_LINEAR(gray * 255);
+
+                  /* And make sure the corresponding palette entry matches. */
+                  png_create_colormap_entry(display, gray, back_g, back_g,
+                     back_g, 65535, P_LINEAR);
+               }
+
+               /* The background passed to libpng, however, must be the sRGB
+                * value.
+                */
+               c.index = 0; /*unused*/
+               c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
+
+               png_set_background_fixed(png_ptr, &c,
+                  PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                  0/*gamma: not used*/);
+
+               output_processing = PNG_CMAP_NONE;
+            }
+
+            else
+            {
+               png_uint_32 i, a;
+
+               /* This is the same as png_make_ga_colormap, above, except that
+                * the entries are all opaque.
+                */
+               if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
+                  png_error(png_ptr, "ga-alpha color-map: too few entries");
+
+               i = 0;
+               while (i < 231)
+               {
+                  png_uint_32 gray = (i * 256 + 115) / 231;
+                  png_create_colormap_entry(display, i++, gray, gray, gray,
+                     255, P_sRGB);
+               }
+
+               /* NOTE: this preserves the full precision of the application
+                * background color.
+                */
+               background_index = i;
+               png_create_colormap_entry(display, i++, back_r, back_g, back_b,
+#ifdef __COVERITY__
+                 /* Coverity claims that output_encoding cannot be 2 (P_LINEAR)
+                  * here.
+                  */ 255U,
+#else
+                  output_encoding == P_LINEAR ? 65535U : 255U,
+#endif
+                  output_encoding);
+
+               /* For non-opaque input composite on the sRGB background - this
+                * requires inverting the encoding for each component.  The input
+                * is still converted to the sRGB encoding because this is a
+                * reasonable approximate to the logarithmic curve of human
+                * visual sensitivity, at least over the narrow range which PNG
+                * represents.  Consequently 'G' is always sRGB encoded, while
+                * 'A' is linear.  We need the linear background colors.
+                */
+               if (output_encoding == P_sRGB) /* else already linear */
+               {
+                  /* This may produce a value not exactly matching the
+                   * background, but that's ok because these numbers are only
+                   * used when alpha != 0
+                   */
+                  back_r = png_sRGB_table[back_r];
+                  back_g = png_sRGB_table[back_g];
+                  back_b = png_sRGB_table[back_b];
+               }
+
+               for (a=1; a<5; ++a)
+               {
+                  unsigned int g;
+
+                  /* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled
+                   * by an 8-bit alpha value (0..255).
+                   */
+                  png_uint_32 alpha = 51 * a;
+                  png_uint_32 back_rx = (255-alpha) * back_r;
+                  png_uint_32 back_gx = (255-alpha) * back_g;
+                  png_uint_32 back_bx = (255-alpha) * back_b;
+
+                  for (g=0; g<6; ++g)
+                  {
+                     png_uint_32 gray = png_sRGB_table[g*51] * alpha;
+
+                     png_create_colormap_entry(display, i++,
+                        PNG_sRGB_FROM_LINEAR(gray + back_rx),
+                        PNG_sRGB_FROM_LINEAR(gray + back_gx),
+                        PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, P_sRGB);
+                  }
+               }
+
+               cmap_entries = i;
+               output_processing = PNG_CMAP_GA;
+            }
+         }
+         break;
+
+      case PNG_COLOR_TYPE_RGB:
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         /* Exclude the case where the output is gray; we can always handle this
+          * with the cases above.
+          */
+         if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0)
+         {
+            /* The color-map will be grayscale, so we may as well convert the
+             * input RGB values to a simple grayscale and use the grayscale
+             * code above.
+             *
+             * NOTE: calling this apparently damages the recognition of the
+             * transparent color in background color handling; call
+             * png_set_tRNS_to_alpha before png_set_background_fixed.
+             */
+            png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1,
+               -1);
+            data_encoding = P_sRGB;
+
+            /* The output will now be one or two 8-bit gray or gray+alpha
+             * channels.  The more complex case arises when the input has alpha.
+             */
+            if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+               png_ptr->num_trans > 0) &&
+               (output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+            {
+               /* Both input and output have an alpha channel, so no background
+                * processing is required; just map the GA bytes to the right
+                * color-map entry.
+                */
+               expand_tRNS = 1;
+
+               if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries)
+                  png_error(png_ptr, "rgb[ga] color-map: too few entries");
+
+               cmap_entries = make_ga_colormap(display);
+               background_index = PNG_CMAP_GA_BACKGROUND;
+               output_processing = PNG_CMAP_GA;
+            }
+
+            else
+            {
+               /* Either the input or the output has no alpha channel, so there
+                * will be no non-opaque pixels in the color-map; it will just be
+                * grayscale.
+                */
+               if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries)
+                  png_error(png_ptr, "rgb[gray] color-map: too few entries");
+
+               /* Ideally this code would use libpng to do the gamma correction,
+                * but if an input alpha channel is to be removed we will hit the
+                * libpng bug in gamma+compose+rgb-to-gray (the double gamma
+                * correction bug).  Fix this by dropping the gamma correction in
+                * this case and doing it in the palette; this will result in
+                * duplicate palette entries, but that's better than the
+                * alternative of double gamma correction.
+                */
+               if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+                  png_ptr->num_trans > 0) &&
+                  png_gamma_not_sRGB(png_ptr->colorspace.gamma) != 0)
+               {
+                  cmap_entries = make_gray_file_colormap(display);
+                  data_encoding = P_FILE;
+               }
+
+               else
+                  cmap_entries = make_gray_colormap(display);
+
+               /* But if the input has alpha or transparency it must be removed
+                */
+               if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+                  png_ptr->num_trans > 0)
+               {
+                  png_color_16 c;
+                  png_uint_32 gray = back_g;
+
+                  /* We need to ensure that the application background exists in
+                   * the colormap and that completely transparent pixels map to
+                   * it.  Achieve this simply by ensuring that the entry
+                   * selected for the background really is the background color.
+                   */
+                  if (data_encoding == P_FILE) /* from the fixup above */
+                  {
+                     /* The app supplied a gray which is in output_encoding, we
+                      * need to convert it to a value of the input (P_FILE)
+                      * encoding then set this palette entry to the required
+                      * output encoding.
+                      */
+                     if (output_encoding == P_sRGB)
+                        gray = png_sRGB_table[gray]; /* now P_LINEAR */
+
+                     gray = PNG_DIV257(png_gamma_16bit_correct(gray,
+                        png_ptr->colorspace.gamma)); /* now P_FILE */
+
+                     /* And make sure the corresponding palette entry contains
+                      * exactly the required sRGB value.
+                      */
+                     png_create_colormap_entry(display, gray, back_g, back_g,
+                        back_g, 0/*unused*/, output_encoding);
+                  }
+
+                  else if (output_encoding == P_LINEAR)
+                  {
+                     gray = PNG_sRGB_FROM_LINEAR(gray * 255);
+
+                     /* And make sure the corresponding palette entry matches.
+                      */
+                     png_create_colormap_entry(display, gray, back_g, back_g,
+                        back_g, 0/*unused*/, P_LINEAR);
+                  }
+
+                  /* The background passed to libpng, however, must be the
+                   * output (normally sRGB) value.
+                   */
+                  c.index = 0; /*unused*/
+                  c.gray = c.red = c.green = c.blue = (png_uint_16)gray;
+
+                  /* NOTE: the following is apparently a bug in libpng. Without
+                   * it the transparent color recognition in
+                   * png_set_background_fixed seems to go wrong.
+                   */
+                  expand_tRNS = 1;
+                  png_set_background_fixed(png_ptr, &c,
+                     PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                     0/*gamma: not used*/);
+               }
+
+               output_processing = PNG_CMAP_NONE;
+            }
+         }
+
+         else /* output is color */
+         {
+            /* We could use png_quantize here so long as there is no transparent
+             * color or alpha; png_quantize ignores alpha.  Easier overall just
+             * to do it once and using PNG_DIV51 on the 6x6x6 reduced RGB cube.
+             * Consequently we always want libpng to produce sRGB data.
+             */
+            data_encoding = P_sRGB;
+
+            /* Is there any transparency or alpha? */
+            if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+               png_ptr->num_trans > 0)
+            {
+               /* Is there alpha in the output too?  If so all four channels are
+                * processed into a special RGB cube with alpha support.
+                */
+               if ((output_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+               {
+                  png_uint_32 r;
+
+                  if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)
+                     png_error(png_ptr, "rgb+alpha color-map: too few entries");
+
+                  cmap_entries = make_rgb_colormap(display);
+
+                  /* Add a transparent entry. */
+                  png_create_colormap_entry(display, cmap_entries, 255, 255,
+                     255, 0, P_sRGB);
+
+                  /* This is stored as the background index for the processing
+                   * algorithm.
+                   */
+                  background_index = cmap_entries++;
+
+                  /* Add 27 r,g,b entries each with alpha 0.5. */
+                  for (r=0; r<256; r = (r << 1) | 0x7f)
+                  {
+                     png_uint_32 g;
+
+                     for (g=0; g<256; g = (g << 1) | 0x7f)
+                     {
+                        png_uint_32 b;
+
+                        /* This generates components with the values 0, 127 and
+                         * 255
+                         */
+                        for (b=0; b<256; b = (b << 1) | 0x7f)
+                           png_create_colormap_entry(display, cmap_entries++,
+                              r, g, b, 128, P_sRGB);
+                     }
+                  }
+
+                  expand_tRNS = 1;
+                  output_processing = PNG_CMAP_RGB_ALPHA;
+               }
+
+               else
+               {
+                  /* Alpha/transparency must be removed.  The background must
+                   * exist in the color map (achieved by setting adding it after
+                   * the 666 color-map).  If the standard processing code will
+                   * pick up this entry automatically that's all that is
+                   * required; libpng can be called to do the background
+                   * processing.
+                   */
+                  unsigned int sample_size =
+                     PNG_IMAGE_SAMPLE_SIZE(output_format);
+                  png_uint_32 r, g, b; /* sRGB background */
+
+                  if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries)
+                     png_error(png_ptr, "rgb-alpha color-map: too few entries");
+
+                  cmap_entries = make_rgb_colormap(display);
+
+                  png_create_colormap_entry(display, cmap_entries, back_r,
+                        back_g, back_b, 0/*unused*/, output_encoding);
+
+                  if (output_encoding == P_LINEAR)
+                  {
+                     r = PNG_sRGB_FROM_LINEAR(back_r * 255);
+                     g = PNG_sRGB_FROM_LINEAR(back_g * 255);
+                     b = PNG_sRGB_FROM_LINEAR(back_b * 255);
+                  }
+
+                  else
+                  {
+                     r = back_r;
+                     g = back_g;
+                     b = back_g;
+                  }
+
+                  /* Compare the newly-created color-map entry with the one the
+                   * PNG_CMAP_RGB algorithm will use.  If the two entries don't
+                   * match, add the new one and set this as the background
+                   * index.
+                   */
+                  if (memcmp((png_const_bytep)display->colormap +
+                        sample_size * cmap_entries,
+                     (png_const_bytep)display->colormap +
+                        sample_size * PNG_RGB_INDEX(r,g,b),
+                     sample_size) != 0)
+                  {
+                     /* The background color must be added. */
+                     background_index = cmap_entries++;
+
+                     /* Add 27 r,g,b entries each with created by composing with
+                      * the background at alpha 0.5.
+                      */
+                     for (r=0; r<256; r = (r << 1) | 0x7f)
+                     {
+                        for (g=0; g<256; g = (g << 1) | 0x7f)
+                        {
+                           /* This generates components with the values 0, 127
+                            * and 255
+                            */
+                           for (b=0; b<256; b = (b << 1) | 0x7f)
+                              png_create_colormap_entry(display, cmap_entries++,
+                                 png_colormap_compose(display, r, P_sRGB, 128,
+                                    back_r, output_encoding),
+                                 png_colormap_compose(display, g, P_sRGB, 128,
+                                    back_g, output_encoding),
+                                 png_colormap_compose(display, b, P_sRGB, 128,
+                                    back_b, output_encoding),
+                                 0/*unused*/, output_encoding);
+                        }
+                     }
+
+                     expand_tRNS = 1;
+                     output_processing = PNG_CMAP_RGB_ALPHA;
+                  }
+
+                  else /* background color is in the standard color-map */
+                  {
+                     png_color_16 c;
+
+                     c.index = 0; /*unused*/
+                     c.red = (png_uint_16)back_r;
+                     c.gray = c.green = (png_uint_16)back_g;
+                     c.blue = (png_uint_16)back_b;
+
+                     png_set_background_fixed(png_ptr, &c,
+                        PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                        0/*gamma: not used*/);
+
+                     output_processing = PNG_CMAP_RGB;
+                  }
+               }
+            }
+
+            else /* no alpha or transparency in the input */
+            {
+               /* Alpha in the output is irrelevant, simply map the opaque input
+                * pixels to the 6x6x6 color-map.
+                */
+               if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries)
+                  png_error(png_ptr, "rgb color-map: too few entries");
+
+               cmap_entries = make_rgb_colormap(display);
+               output_processing = PNG_CMAP_RGB;
+            }
+         }
+         break;
+
+      case PNG_COLOR_TYPE_PALETTE:
+         /* It's already got a color-map.  It may be necessary to eliminate the
+          * tRNS entries though.
+          */
+         {
+            unsigned int num_trans = png_ptr->num_trans;
+            png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL;
+            png_const_colorp colormap = png_ptr->palette;
+            const int do_background = trans != NULL &&
+               (output_format & PNG_FORMAT_FLAG_ALPHA) == 0;
+            unsigned int i;
+
+            /* Just in case: */
+            if (trans == NULL)
+               num_trans = 0;
+
+            output_processing = PNG_CMAP_NONE;
+            data_encoding = P_FILE; /* Don't change from color-map indices */
+            cmap_entries = png_ptr->num_palette;
+            if (cmap_entries > 256)
+               cmap_entries = 256;
+
+            if (cmap_entries > image->colormap_entries)
+               png_error(png_ptr, "palette color-map: too few entries");
+
+            for (i=0; i < cmap_entries; ++i)
+            {
+               if (do_background != 0 && i < num_trans && trans[i] < 255)
+               {
+                  if (trans[i] == 0)
+                     png_create_colormap_entry(display, i, back_r, back_g,
+                        back_b, 0, output_encoding);
+
+                  else
+                  {
+                     /* Must compose the PNG file color in the color-map entry
+                      * on the sRGB color in 'back'.
+                      */
+                     png_create_colormap_entry(display, i,
+                        png_colormap_compose(display, colormap[i].red, P_FILE,
+                           trans[i], back_r, output_encoding),
+                        png_colormap_compose(display, colormap[i].green, P_FILE,
+                           trans[i], back_g, output_encoding),
+                        png_colormap_compose(display, colormap[i].blue, P_FILE,
+                           trans[i], back_b, output_encoding),
+                        output_encoding == P_LINEAR ? trans[i] * 257U :
+                           trans[i],
+                        output_encoding);
+                  }
+               }
+
+               else
+                  png_create_colormap_entry(display, i, colormap[i].red,
+                     colormap[i].green, colormap[i].blue,
+                     i < num_trans ? trans[i] : 255U, P_FILE/*8-bit*/);
+            }
+
+            /* The PNG data may have indices packed in fewer than 8 bits, it
+             * must be expanded if so.
+             */
+            if (png_ptr->bit_depth < 8)
+               png_set_packing(png_ptr);
+         }
+         break;
+
+      default:
+         png_error(png_ptr, "invalid PNG color type");
+         /*NOT REACHED*/
+   }
+
+   /* Now deal with the output processing */
+   if (expand_tRNS != 0 && png_ptr->num_trans > 0 &&
+       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0)
+      png_set_tRNS_to_alpha(png_ptr);
+
+   switch (data_encoding)
+   {
+      case P_sRGB:
+         /* Change to 8-bit sRGB */
+         png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB);
+         /* FALL THROUGH */
+
+      case P_FILE:
+         if (png_ptr->bit_depth > 8)
+            png_set_scale_16(png_ptr);
+         break;
+
+#ifdef __GNUC__
+      default:
+         png_error(png_ptr, "bad data option (internal error)");
+#endif
+   }
+
+   if (cmap_entries > 256 || cmap_entries > image->colormap_entries)
+      png_error(png_ptr, "color map overflow (BAD internal error)");
+
+   image->colormap_entries = cmap_entries;
+
+   /* Double check using the recorded background index */
+   switch (output_processing)
+   {
+      case PNG_CMAP_NONE:
+         if (background_index != PNG_CMAP_NONE_BACKGROUND)
+            goto bad_background;
+         break;
+
+      case PNG_CMAP_GA:
+         if (background_index != PNG_CMAP_GA_BACKGROUND)
+            goto bad_background;
+         break;
+
+      case PNG_CMAP_TRANS:
+         if (background_index >= cmap_entries ||
+            background_index != PNG_CMAP_TRANS_BACKGROUND)
+            goto bad_background;
+         break;
+
+      case PNG_CMAP_RGB:
+         if (background_index != PNG_CMAP_RGB_BACKGROUND)
+            goto bad_background;
+         break;
+
+      case PNG_CMAP_RGB_ALPHA:
+         if (background_index != PNG_CMAP_RGB_ALPHA_BACKGROUND)
+            goto bad_background;
+         break;
+
+      default:
+         png_error(png_ptr, "bad processing option (internal error)");
+
+      bad_background:
+         png_error(png_ptr, "bad background index (internal error)");
+   }
+
+   display->colormap_processing = output_processing;
+
+   return 1/*ok*/;
+}
+
+/* The final part of the color-map read called from png_image_finish_read. */
+static int
+png_image_read_and_map(png_voidp argument)
+{
+   png_image_read_control *display = png_voidcast(png_image_read_control*,
+      argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+   int passes;
+
+   /* Called when the libpng data must be transformed into the color-mapped
+    * form.  There is a local row buffer in display->local and this routine must
+    * do the interlace handling.
+    */
+   switch (png_ptr->interlaced)
+   {
+      case PNG_INTERLACE_NONE:
+         passes = 1;
+         break;
+
+      case PNG_INTERLACE_ADAM7:
+         passes = PNG_INTERLACE_ADAM7_PASSES;
+         break;
+
+      default:
+         png_error(png_ptr, "unknown interlace type");
+   }
+
+   {
+      png_uint_32  height = image->height;
+      png_uint_32  width = image->width;
+      int          proc = display->colormap_processing;
+      png_bytep    first_row = png_voidcast(png_bytep, display->first_row);
+      ptrdiff_t    step_row = display->row_bytes;
+      int pass;
+
+      for (pass = 0; pass < passes; ++pass)
+      {
+         unsigned int     startx, stepx, stepy;
+         png_uint_32      y;
+
+         if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
+         {
+            /* The row may be empty for a short image: */
+            if (PNG_PASS_COLS(width, pass) == 0)
+               continue;
+
+            startx = PNG_PASS_START_COL(pass);
+            stepx = PNG_PASS_COL_OFFSET(pass);
+            y = PNG_PASS_START_ROW(pass);
+            stepy = PNG_PASS_ROW_OFFSET(pass);
+         }
+
+         else
+         {
+            y = 0;
+            startx = 0;
+            stepx = stepy = 1;
+         }
+
+         for (; y<height; y += stepy)
+         {
+            png_bytep inrow = png_voidcast(png_bytep, display->local_row);
+            png_bytep outrow = first_row + y * step_row;
+            png_const_bytep end_row = outrow + width;
+
+            /* Read read the libpng data into the temporary buffer. */
+            png_read_row(png_ptr, inrow, NULL);
+
+            /* Now process the row according to the processing option, note
+             * that the caller verifies that the format of the libpng output
+             * data is as required.
+             */
+            outrow += startx;
+            switch (proc)
+            {
+               case PNG_CMAP_GA:
+                  for (; outrow < end_row; outrow += stepx)
+                  {
+                     /* The data is always in the PNG order */
+                     unsigned int gray = *inrow++;
+                     unsigned int alpha = *inrow++;
+                     unsigned int entry;
+
+                     /* NOTE: this code is copied as a comment in
+                      * make_ga_colormap above.  Please update the
+                      * comment if you change this code!
+                      */
+                     if (alpha > 229) /* opaque */
+                     {
+                        entry = (231 * gray + 128) >> 8;
+                     }
+                     else if (alpha < 26) /* transparent */
+                     {
+                        entry = 231;
+                     }
+                     else /* partially opaque */
+                     {
+                        entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray);
+                     }
+
+                     *outrow = (png_byte)entry;
+                  }
+                  break;
+
+               case PNG_CMAP_TRANS:
+                  for (; outrow < end_row; outrow += stepx)
+                  {
+                     png_byte gray = *inrow++;
+                     png_byte alpha = *inrow++;
+
+                     if (alpha == 0)
+                        *outrow = PNG_CMAP_TRANS_BACKGROUND;
+
+                     else if (gray != PNG_CMAP_TRANS_BACKGROUND)
+                        *outrow = gray;
+
+                     else
+                        *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1);
+                  }
+                  break;
+
+               case PNG_CMAP_RGB:
+                  for (; outrow < end_row; outrow += stepx)
+                  {
+                     *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]);
+                     inrow += 3;
+                  }
+                  break;
+
+               case PNG_CMAP_RGB_ALPHA:
+                  for (; outrow < end_row; outrow += stepx)
+                  {
+                     unsigned int alpha = inrow[3];
+
+                     /* Because the alpha entries only hold alpha==0.5 values
+                      * split the processing at alpha==0.25 (64) and 0.75
+                      * (196).
+                      */
+
+                     if (alpha >= 196)
+                        *outrow = PNG_RGB_INDEX(inrow[0], inrow[1],
+                           inrow[2]);
+
+                     else if (alpha < 64)
+                        *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND;
+
+                     else
+                     {
+                        /* Likewise there are three entries for each of r, g
+                         * and b.  We could select the entry by popcount on
+                         * the top two bits on those architectures that
+                         * support it, this is what the code below does,
+                         * crudely.
+                         */
+                        unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND+1;
+
+                        /* Here are how the values map:
+                         *
+                         * 0x00 .. 0x3f -> 0
+                         * 0x40 .. 0xbf -> 1
+                         * 0xc0 .. 0xff -> 2
+                         *
+                         * So, as above with the explicit alpha checks, the
+                         * breakpoints are at 64 and 196.
+                         */
+                        if (inrow[0] & 0x80) back_i += 9; /* red */
+                        if (inrow[0] & 0x40) back_i += 9;
+                        if (inrow[0] & 0x80) back_i += 3; /* green */
+                        if (inrow[0] & 0x40) back_i += 3;
+                        if (inrow[0] & 0x80) back_i += 1; /* blue */
+                        if (inrow[0] & 0x40) back_i += 1;
+
+                        *outrow = (png_byte)back_i;
+                     }
+
+                     inrow += 4;
+                  }
+                  break;
+
+               default:
+                  break;
+            }
+         }
+      }
+   }
+
+   return 1;
+}
+
+static int
+png_image_read_colormapped(png_voidp argument)
+{
+   png_image_read_control *display = png_voidcast(png_image_read_control*,
+      argument);
+   png_imagep image = display->image;
+   png_controlp control = image->opaque;
+   png_structrp png_ptr = control->png_ptr;
+   png_inforp info_ptr = control->info_ptr;
+
+   int passes = 0; /* As a flag */
+
+   PNG_SKIP_CHUNKS(png_ptr);
+
+   /* Update the 'info' structure and make sure the result is as required; first
+    * make sure to turn on the interlace handling if it will be required
+    * (because it can't be turned on *after* the call to png_read_update_info!)
+    */
+   if (display->colormap_processing == PNG_CMAP_NONE)
+      passes = png_set_interlace_handling(png_ptr);
+
+   png_read_update_info(png_ptr, info_ptr);
+
+   /* The expected output can be deduced from the colormap_processing option. */
+   switch (display->colormap_processing)
+   {
+      case PNG_CMAP_NONE:
+         /* Output must be one channel and one byte per pixel, the output
+          * encoding can be anything.
+          */
+         if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+            info_ptr->color_type == PNG_COLOR_TYPE_GRAY) &&
+            info_ptr->bit_depth == 8)
+            break;
+
+         goto bad_output;
+
+      case PNG_CMAP_TRANS:
+      case PNG_CMAP_GA:
+         /* Output must be two channels and the 'G' one must be sRGB, the latter
+          * can be checked with an exact number because it should have been set
+          * to this number above!
+          */
+         if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+            info_ptr->bit_depth == 8 &&
+            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
+            image->colormap_entries == 256)
+            break;
+
+         goto bad_output;
+
+      case PNG_CMAP_RGB:
+         /* Output must be 8-bit sRGB encoded RGB */
+         if (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
+            info_ptr->bit_depth == 8 &&
+            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
+            image->colormap_entries == 216)
+            break;
+
+         goto bad_output;
+
+      case PNG_CMAP_RGB_ALPHA:
+         /* Output must be 8-bit sRGB encoded RGBA */
+         if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+            info_ptr->bit_depth == 8 &&
+            png_ptr->screen_gamma == PNG_GAMMA_sRGB &&
+            image->colormap_entries == 244 /* 216 + 1 + 27 */)
+            break;
+
+         /* goto bad_output; */
+         /* FALL THROUGH */
+
+      default:
+      bad_output:
+         png_error(png_ptr, "bad color-map processing (internal error)");
+   }
+
+   /* Now read the rows.  Do this here if it is possible to read directly into
+    * the output buffer, otherwise allocate a local row buffer of the maximum
+    * size libpng requires and call the relevant processing routine safely.
+    */
+   {
+      png_voidp first_row = display->buffer;
+      ptrdiff_t row_bytes = display->row_stride;
+
+      /* The following expression is designed to work correctly whether it gives
+       * a signed or an unsigned result.
+       */
+      if (row_bytes < 0)
+      {
+         char *ptr = png_voidcast(char*, first_row);
+         ptr += (image->height-1) * (-row_bytes);
+         first_row = png_voidcast(png_voidp, ptr);
+      }
+
+      display->first_row = first_row;
+      display->row_bytes = row_bytes;
+   }
+
+   if (passes == 0)
+   {
+      int result;
+      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
+
+      display->local_row = row;
+      result = png_safe_execute(image, png_image_read_and_map, display);
+      display->local_row = NULL;
+      png_free(png_ptr, row);
+
+      return result;
+   }
+
+   else
+   {
+      png_alloc_size_t row_bytes = display->row_bytes;
+
+      while (--passes >= 0)
+      {
+         png_uint_32      y = image->height;
+         png_bytep        row = png_voidcast(png_bytep, display->first_row);
+
+         while (y-- > 0)
+         {
+            png_read_row(png_ptr, row, NULL);
+            row += row_bytes;
+         }
+      }
+
+      return 1;
+   }
+}
+
+/* Just the row reading part of png_image_read. */
+static int
+png_image_read_composite(png_voidp argument)
+{
+   png_image_read_control *display = png_voidcast(png_image_read_control*,
+      argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+   int passes;
+
+   switch (png_ptr->interlaced)
+   {
+      case PNG_INTERLACE_NONE:
+         passes = 1;
+         break;
+
+      case PNG_INTERLACE_ADAM7:
+         passes = PNG_INTERLACE_ADAM7_PASSES;
+         break;
+
+      default:
+         png_error(png_ptr, "unknown interlace type");
+   }
+
+   {
+      png_uint_32  height = image->height;
+      png_uint_32  width = image->width;
+      ptrdiff_t    step_row = display->row_bytes;
+      unsigned int channels =
+          (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+      int pass;
+
+      for (pass = 0; pass < passes; ++pass)
+      {
+         unsigned int     startx, stepx, stepy;
+         png_uint_32      y;
+
+         if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
+         {
+            /* The row may be empty for a short image: */
+            if (PNG_PASS_COLS(width, pass) == 0)
+               continue;
+
+            startx = PNG_PASS_START_COL(pass) * channels;
+            stepx = PNG_PASS_COL_OFFSET(pass) * channels;
+            y = PNG_PASS_START_ROW(pass);
+            stepy = PNG_PASS_ROW_OFFSET(pass);
+         }
+
+         else
+         {
+            y = 0;
+            startx = 0;
+            stepx = channels;
+            stepy = 1;
+         }
+
+         for (; y<height; y += stepy)
+         {
+            png_bytep inrow = png_voidcast(png_bytep, display->local_row);
+            png_bytep outrow;
+            png_const_bytep end_row;
+
+            /* Read the row, which is packed: */
+            png_read_row(png_ptr, inrow, NULL);
+
+            outrow = png_voidcast(png_bytep, display->first_row);
+            outrow += y * step_row;
+            end_row = outrow + width * channels;
+
+            /* Now do the composition on each pixel in this row. */
+            outrow += startx;
+            for (; outrow < end_row; outrow += stepx)
+            {
+               png_byte alpha = inrow[channels];
+
+               if (alpha > 0) /* else no change to the output */
+               {
+                  unsigned int c;
+
+                  for (c=0; c<channels; ++c)
+                  {
+                     png_uint_32 component = inrow[c];
+
+                     if (alpha < 255) /* else just use component */
+                     {
+                        /* This is PNG_OPTIMIZED_ALPHA, the component value
+                         * is a linear 8-bit value.  Combine this with the
+                         * current outrow[c] value which is sRGB encoded.
+                         * Arithmetic here is 16-bits to preserve the output
+                         * values correctly.
+                         */
+                        component *= 257*255; /* =65535 */
+                        component += (255-alpha)*png_sRGB_table[outrow[c]];
+
+                        /* So 'component' is scaled by 255*65535 and is
+                         * therefore appropriate for the sRGB to linear
+                         * conversion table.
+                         */
+                        component = PNG_sRGB_FROM_LINEAR(component);
+                     }
+
+                     outrow[c] = (png_byte)component;
+                  }
+               }
+
+               inrow += channels+1; /* components and alpha channel */
+            }
+         }
+      }
+   }
+
+   return 1;
+}
+
+/* The do_local_background case; called when all the following transforms are to
+ * be done:
+ *
+ * PNG_RGB_TO_GRAY
+ * PNG_COMPOSITE
+ * PNG_GAMMA
+ *
+ * This is a work-around for the fact that both the PNG_RGB_TO_GRAY and
+ * PNG_COMPOSITE code performs gamma correction, so we get double gamma
+ * correction.  The fix-up is to prevent the PNG_COMPOSITE operation from
+ * happening inside libpng, so this routine sees an 8 or 16-bit gray+alpha
+ * row and handles the removal or pre-multiplication of the alpha channel.
+ */
+static int
+png_image_read_background(png_voidp argument)
+{
+   png_image_read_control *display = png_voidcast(png_image_read_control*,
+      argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+   png_inforp info_ptr = image->opaque->info_ptr;
+   png_uint_32 height = image->height;
+   png_uint_32 width = image->width;
+   int pass, passes;
+
+   /* Double check the convoluted logic below.  We expect to get here with
+    * libpng doing rgb to gray and gamma correction but background processing
+    * left to the png_image_read_background function.  The rows libpng produce
+    * might be 8 or 16-bit but should always have two channels; gray plus alpha.
+    */
+   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
+      png_error(png_ptr, "lost rgb to gray");
+
+   if ((png_ptr->transformations & PNG_COMPOSE) != 0)
+      png_error(png_ptr, "unexpected compose");
+
+   if (png_get_channels(png_ptr, info_ptr) != 2)
+      png_error(png_ptr, "lost/gained channels");
+
+   /* Expect the 8-bit case to always remove the alpha channel */
+   if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 &&
+      (image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+      png_error(png_ptr, "unexpected 8-bit transformation");
+
+   switch (png_ptr->interlaced)
+   {
+      case PNG_INTERLACE_NONE:
+         passes = 1;
+         break;
+
+      case PNG_INTERLACE_ADAM7:
+         passes = PNG_INTERLACE_ADAM7_PASSES;
+         break;
+
+      default:
+         png_error(png_ptr, "unknown interlace type");
+   }
+
+   /* Use direct access to info_ptr here because otherwise the simplified API
+    * would require PNG_EASY_ACCESS_SUPPORTED (just for this.)  Note this is
+    * checking the value after libpng expansions, not the original value in the
+    * PNG.
+    */
+   switch (info_ptr->bit_depth)
+   {
+      case 8:
+         /* 8-bit sRGB gray values with an alpha channel; the alpha channel is
+          * to be removed by composing on a background: either the row if
+          * display->background is NULL or display->background->green if not.
+          * Unlike the code above ALPHA_OPTIMIZED has *not* been done.
+          */
+         {
+            png_bytep first_row = png_voidcast(png_bytep, display->first_row);
+            ptrdiff_t step_row = display->row_bytes;
+
+            for (pass = 0; pass < passes; ++pass)
+            {
+               png_bytep        row = png_voidcast(png_bytep,
+                                                   display->first_row);
+               unsigned int     startx, stepx, stepy;
+               png_uint_32      y;
+
+               if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
+               {
+                  /* The row may be empty for a short image: */
+                  if (PNG_PASS_COLS(width, pass) == 0)
+                     continue;
+
+                  startx = PNG_PASS_START_COL(pass);
+                  stepx = PNG_PASS_COL_OFFSET(pass);
+                  y = PNG_PASS_START_ROW(pass);
+                  stepy = PNG_PASS_ROW_OFFSET(pass);
+               }
+
+               else
+               {
+                  y = 0;
+                  startx = 0;
+                  stepx = stepy = 1;
+               }
+
+               if (display->background == NULL)
+               {
+                  for (; y<height; y += stepy)
+                  {
+                     png_bytep inrow = png_voidcast(png_bytep,
+                        display->local_row);
+                     png_bytep outrow = first_row + y * step_row;
+                     png_const_bytep end_row = outrow + width;
+
+                     /* Read the row, which is packed: */
+                     png_read_row(png_ptr, inrow, NULL);
+
+                     /* Now do the composition on each pixel in this row. */
+                     outrow += startx;
+                     for (; outrow < end_row; outrow += stepx)
+                     {
+                        png_byte alpha = inrow[1];
+
+                        if (alpha > 0) /* else no change to the output */
+                        {
+                           png_uint_32 component = inrow[0];
+
+                           if (alpha < 255) /* else just use component */
+                           {
+                              /* Since PNG_OPTIMIZED_ALPHA was not set it is
+                               * necessary to invert the sRGB transfer
+                               * function and multiply the alpha out.
+                               */
+                              component = png_sRGB_table[component] * alpha;
+                              component += png_sRGB_table[outrow[0]] *
+                                 (255-alpha);
+                              component = PNG_sRGB_FROM_LINEAR(component);
+                           }
+
+                           outrow[0] = (png_byte)component;
+                        }
+
+                        inrow += 2; /* gray and alpha channel */
+                     }
+                  }
+               }
+
+               else /* constant background value */
+               {
+                  png_byte background8 = display->background->green;
+                  png_uint_16 background = png_sRGB_table[background8];
+
+                  for (; y<height; y += stepy)
+                  {
+                     png_bytep inrow = png_voidcast(png_bytep,
+                        display->local_row);
+                     png_bytep outrow = first_row + y * step_row;
+                     png_const_bytep end_row = outrow + width;
+
+                     /* Read the row, which is packed: */
+                     png_read_row(png_ptr, inrow, NULL);
+
+                     /* Now do the composition on each pixel in this row. */
+                     outrow += startx;
+                     for (; outrow < end_row; outrow += stepx)
+                     {
+                        png_byte alpha = inrow[1];
+
+                        if (alpha > 0) /* else use background */
+                        {
+                           png_uint_32 component = inrow[0];
+
+                           if (alpha < 255) /* else just use component */
+                           {
+                              component = png_sRGB_table[component] * alpha;
+                              component += background * (255-alpha);
+                              component = PNG_sRGB_FROM_LINEAR(component);
+                           }
+
+                           outrow[0] = (png_byte)component;
+                        }
+
+                        else
+                           outrow[0] = background8;
+
+                        inrow += 2; /* gray and alpha channel */
+                     }
+
+                     row += display->row_bytes;
+                  }
+               }
+            }
+         }
+         break;
+
+      case 16:
+         /* 16-bit linear with pre-multiplied alpha; the pre-multiplication must
+          * still be done and, maybe, the alpha channel removed.  This code also
+          * handles the alpha-first option.
+          */
+         {
+            png_uint_16p first_row = png_voidcast(png_uint_16p,
+               display->first_row);
+            /* The division by two is safe because the caller passed in a
+             * stride which was multiplied by 2 (below) to get row_bytes.
+             */
+            ptrdiff_t    step_row = display->row_bytes / 2;
+            int preserve_alpha = (image->format & PNG_FORMAT_FLAG_ALPHA) != 0;
+            unsigned int outchannels = 1+preserve_alpha;
+            int swap_alpha = 0;
+
+#           ifdef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
+               if (preserve_alpha != 0 &&
+                   (image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
+                  swap_alpha = 1;
+#           endif
+
+            for (pass = 0; pass < passes; ++pass)
+            {
+               unsigned int     startx, stepx, stepy;
+               png_uint_32      y;
+
+               /* The 'x' start and step are adjusted to output components here.
+                */
+               if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
+               {
+                  /* The row may be empty for a short image: */
+                  if (PNG_PASS_COLS(width, pass) == 0)
+                     continue;
+
+                  startx = PNG_PASS_START_COL(pass) * outchannels;
+                  stepx = PNG_PASS_COL_OFFSET(pass) * outchannels;
+                  y = PNG_PASS_START_ROW(pass);
+                  stepy = PNG_PASS_ROW_OFFSET(pass);
+               }
+
+               else
+               {
+                  y = 0;
+                  startx = 0;
+                  stepx = outchannels;
+                  stepy = 1;
+               }
+
+               for (; y<height; y += stepy)
+               {
+                  png_const_uint_16p inrow;
+                  png_uint_16p outrow = first_row + y*step_row;
+                  png_uint_16p end_row = outrow + width * outchannels;
+
+                  /* Read the row, which is packed: */
+                  png_read_row(png_ptr, png_voidcast(png_bytep,
+                     display->local_row), NULL);
+                  inrow = png_voidcast(png_const_uint_16p, display->local_row);
+
+                  /* Now do the pre-multiplication on each pixel in this row.
+                   */
+                  outrow += startx;
+                  for (; outrow < end_row; outrow += stepx)
+                  {
+                     png_uint_32 component = inrow[0];
+                     png_uint_16 alpha = inrow[1];
+
+                     if (alpha > 0) /* else 0 */
+                     {
+                        if (alpha < 65535) /* else just use component */
+                        {
+                           component *= alpha;
+                           component += 32767;
+                           component /= 65535;
+                        }
+                     }
+
+                     else
+                        component = 0;
+
+                     outrow[swap_alpha] = (png_uint_16)component;
+                     if (preserve_alpha != 0)
+                        outrow[1 ^ swap_alpha] = alpha;
+
+                     inrow += 2; /* components and alpha channel */
+                  }
+               }
+            }
+         }
+         break;
+
+#ifdef __GNUC__
+      default:
+         png_error(png_ptr, "unexpected bit depth");
+#endif
+   }
+
+   return 1;
+}
+
+/* The guts of png_image_finish_read as a png_safe_execute callback. */
+static int
+png_image_read_direct(png_voidp argument)
+{
+   png_image_read_control *display = png_voidcast(png_image_read_control*,
+      argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+   png_inforp info_ptr = image->opaque->info_ptr;
+
+   png_uint_32 format = image->format;
+   int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0;
+   int do_local_compose = 0;
+   int do_local_background = 0; /* to avoid double gamma correction bug */
+   int passes = 0;
+
+   /* Add transforms to ensure the correct output format is produced then check
+    * that the required implementation support is there.  Always expand; always
+    * need 8 bits minimum, no palette and expanded tRNS.
+    */
+   png_set_expand(png_ptr);
+
+   /* Now check the format to see if it was modified. */
+   {
+      png_uint_32 base_format = png_image_format(png_ptr) &
+         ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */;
+      png_uint_32 change = format ^ base_format;
+      png_fixed_point output_gamma;
+      int mode; /* alpha mode */
+
+      /* Do this first so that we have a record if rgb to gray is happening. */
+      if ((change & PNG_FORMAT_FLAG_COLOR) != 0)
+      {
+         /* gray<->color transformation required. */
+         if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
+            png_set_gray_to_rgb(png_ptr);
+
+         else
+         {
+            /* libpng can't do both rgb to gray and
+             * background/pre-multiplication if there is also significant gamma
+             * correction, because both operations require linear colors and
+             * the code only supports one transform doing the gamma correction.
+             * Handle this by doing the pre-multiplication or background
+             * operation in this code, if necessary.
+             *
+             * TODO: fix this by rewriting pngrtran.c (!)
+             *
+             * For the moment (given that fixing this in pngrtran.c is an
+             * enormous change) 'do_local_background' is used to indicate that
+             * the problem exists.
+             */
+            if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+               do_local_background = 1/*maybe*/;
+
+            png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE,
+               PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
+         }
+
+         change &= ~PNG_FORMAT_FLAG_COLOR;
+      }
+
+      /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise.
+       */
+      {
+         png_fixed_point input_gamma_default;
+
+         if ((base_format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
+             (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0)
+            input_gamma_default = PNG_GAMMA_LINEAR;
+         else
+            input_gamma_default = PNG_DEFAULT_sRGB;
+
+         /* Call png_set_alpha_mode to set the default for the input gamma; the
+          * output gamma is set by a second call below.
+          */
+         png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, input_gamma_default);
+      }
+
+      if (linear != 0)
+      {
+         /* If there *is* an alpha channel in the input it must be multiplied
+          * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG.
+          */
+         if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+            mode = PNG_ALPHA_STANDARD; /* associated alpha */
+
+         else
+            mode = PNG_ALPHA_PNG;
+
+         output_gamma = PNG_GAMMA_LINEAR;
+      }
+
+      else
+      {
+         mode = PNG_ALPHA_PNG;
+         output_gamma = PNG_DEFAULT_sRGB;
+      }
+
+      /* If 'do_local_background' is set check for the presence of gamma
+       * correction; this is part of the work-round for the libpng bug
+       * described above.
+       *
+       * TODO: fix libpng and remove this.
+       */
+      if (do_local_background != 0)
+      {
+         png_fixed_point gtest;
+
+         /* This is 'png_gamma_threshold' from pngrtran.c; the test used for
+          * gamma correction, the screen gamma hasn't been set on png_struct
+          * yet; it's set below.  png_struct::gamma, however, is set to the
+          * final value.
+          */
+         if (png_muldiv(&gtest, output_gamma, png_ptr->colorspace.gamma,
+               PNG_FP_1) != 0 && png_gamma_significant(gtest) == 0)
+            do_local_background = 0;
+
+         else if (mode == PNG_ALPHA_STANDARD)
+         {
+            do_local_background = 2/*required*/;
+            mode = PNG_ALPHA_PNG; /* prevent libpng doing it */
+         }
+
+         /* else leave as 1 for the checks below */
+      }
+
+      /* If the bit-depth changes then handle that here. */
+      if ((change & PNG_FORMAT_FLAG_LINEAR) != 0)
+      {
+         if (linear != 0 /*16-bit output*/)
+            png_set_expand_16(png_ptr);
+
+         else /* 8-bit output */
+            png_set_scale_16(png_ptr);
+
+         change &= ~PNG_FORMAT_FLAG_LINEAR;
+      }
+
+      /* Now the background/alpha channel changes. */
+      if ((change & PNG_FORMAT_FLAG_ALPHA) != 0)
+      {
+         /* Removing an alpha channel requires composition for the 8-bit
+          * formats; for the 16-bit it is already done, above, by the
+          * pre-multiplication and the channel just needs to be stripped.
+          */
+         if ((base_format & PNG_FORMAT_FLAG_ALPHA) != 0)
+         {
+            /* If RGB->gray is happening the alpha channel must be left and the
+             * operation completed locally.
+             *
+             * TODO: fix libpng and remove this.
+             */
+            if (do_local_background != 0)
+               do_local_background = 2/*required*/;
+
+            /* 16-bit output: just remove the channel */
+            else if (linear != 0) /* compose on black (well, pre-multiply) */
+               png_set_strip_alpha(png_ptr);
+
+            /* 8-bit output: do an appropriate compose */
+            else if (display->background != NULL)
+            {
+               png_color_16 c;
+
+               c.index = 0; /*unused*/
+               c.red = display->background->red;
+               c.green = display->background->green;
+               c.blue = display->background->blue;
+               c.gray = display->background->green;
+
+               /* This is always an 8-bit sRGB value, using the 'green' channel
+                * for gray is much better than calculating the luminance here;
+                * we can get off-by-one errors in that calculation relative to
+                * the app expectations and that will show up in transparent
+                * pixels.
+                */
+               png_set_background_fixed(png_ptr, &c,
+                  PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/,
+                  0/*gamma: not used*/);
+            }
+
+            else /* compose on row: implemented below. */
+            {
+               do_local_compose = 1;
+               /* This leaves the alpha channel in the output, so it has to be
+                * removed by the code below.  Set the encoding to the 'OPTIMIZE'
+                * one so the code only has to hack on the pixels that require
+                * composition.
+                */
+               mode = PNG_ALPHA_OPTIMIZED;
+            }
+         }
+
+         else /* output needs an alpha channel */
+         {
+            /* This is tricky because it happens before the swap operation has
+             * been accomplished; however, the swap does *not* swap the added
+             * alpha channel (weird API), so it must be added in the correct
+             * place.
+             */
+            png_uint_32 filler; /* opaque filler */
+            int where;
+
+            if (linear != 0)
+               filler = 65535;
+
+            else
+               filler = 255;
+
+#           ifdef PNG_FORMAT_AFIRST_SUPPORTED
+               if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
+               {
+                  where = PNG_FILLER_BEFORE;
+                  change &= ~PNG_FORMAT_FLAG_AFIRST;
+               }
+
+               else
+#           endif
+               where = PNG_FILLER_AFTER;
+
+            png_set_add_alpha(png_ptr, filler, where);
+         }
+
+         /* This stops the (irrelevant) call to swap_alpha below. */
+         change &= ~PNG_FORMAT_FLAG_ALPHA;
+      }
+
+      /* Now set the alpha mode correctly; this is always done, even if there is
+       * no alpha channel in either the input or the output because it correctly
+       * sets the output gamma.
+       */
+      png_set_alpha_mode_fixed(png_ptr, mode, output_gamma);
+
+#     ifdef PNG_FORMAT_BGR_SUPPORTED
+         if ((change & PNG_FORMAT_FLAG_BGR) != 0)
+         {
+            /* Check only the output format; PNG is never BGR; don't do this if
+             * the output is gray, but fix up the 'format' value in that case.
+             */
+            if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
+               png_set_bgr(png_ptr);
+
+            else
+               format &= ~PNG_FORMAT_FLAG_BGR;
+
+            change &= ~PNG_FORMAT_FLAG_BGR;
+         }
+#     endif
+
+#     ifdef PNG_FORMAT_AFIRST_SUPPORTED
+         if ((change & PNG_FORMAT_FLAG_AFIRST) != 0)
+         {
+            /* Only relevant if there is an alpha channel - it's particularly
+             * important to handle this correctly because do_local_compose may
+             * be set above and then libpng will keep the alpha channel for this
+             * code to remove.
+             */
+            if ((format & PNG_FORMAT_FLAG_ALPHA) != 0)
+            {
+               /* Disable this if doing a local background,
+                * TODO: remove this when local background is no longer required.
+                */
+               if (do_local_background != 2)
+                  png_set_swap_alpha(png_ptr);
+            }
+
+            else
+               format &= ~PNG_FORMAT_FLAG_AFIRST;
+
+            change &= ~PNG_FORMAT_FLAG_AFIRST;
+         }
+#     endif
+
+      /* If the *output* is 16-bit then we need to check for a byte-swap on this
+       * architecture.
+       */
+      if (linear != 0)
+      {
+         PNG_CONST png_uint_16 le = 0x0001;
+
+         if ((*(png_const_bytep) & le) != 0)
+            png_set_swap(png_ptr);
+      }
+
+      /* If change is not now 0 some transformation is missing - error out. */
+      if (change != 0)
+         png_error(png_ptr, "png_read_image: unsupported transformation");
+   }
+
+   PNG_SKIP_CHUNKS(png_ptr);
+
+   /* Update the 'info' structure and make sure the result is as required; first
+    * make sure to turn on the interlace handling if it will be required
+    * (because it can't be turned on *after* the call to png_read_update_info!)
+    *
+    * TODO: remove the do_local_background fixup below.
+    */
+   if (do_local_compose == 0 && do_local_background != 2)
+      passes = png_set_interlace_handling(png_ptr);
+
+   png_read_update_info(png_ptr, info_ptr);
+
+   {
+      png_uint_32 info_format = 0;
+
+      if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+         info_format |= PNG_FORMAT_FLAG_COLOR;
+
+      if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      {
+         /* do_local_compose removes this channel below. */
+         if (do_local_compose == 0)
+         {
+            /* do_local_background does the same if required. */
+            if (do_local_background != 2 ||
+               (format & PNG_FORMAT_FLAG_ALPHA) != 0)
+               info_format |= PNG_FORMAT_FLAG_ALPHA;
+         }
+      }
+
+      else if (do_local_compose != 0) /* internal error */
+         png_error(png_ptr, "png_image_read: alpha channel lost");
+
+      if (info_ptr->bit_depth == 16)
+         info_format |= PNG_FORMAT_FLAG_LINEAR;
+
+#     ifdef PNG_FORMAT_BGR_SUPPORTED
+         if ((png_ptr->transformations & PNG_BGR) != 0)
+            info_format |= PNG_FORMAT_FLAG_BGR;
+#     endif
+
+#     ifdef PNG_FORMAT_AFIRST_SUPPORTED
+         if (do_local_background == 2)
+         {
+            if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
+               info_format |= PNG_FORMAT_FLAG_AFIRST;
+         }
+
+         if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 ||
+            ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 &&
+            (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0))
+         {
+            if (do_local_background == 2)
+               png_error(png_ptr, "unexpected alpha swap transformation");
+
+            info_format |= PNG_FORMAT_FLAG_AFIRST;
+         }
+#     endif
+
+      /* This is actually an internal error. */
+      if (info_format != format)
+         png_error(png_ptr, "png_read_image: invalid transformations");
+   }
+
+   /* Now read the rows.  If do_local_compose is set then it is necessary to use
+    * a local row buffer.  The output will be GA, RGBA or BGRA and must be
+    * converted to G, RGB or BGR as appropriate.  The 'local_row' member of the
+    * display acts as a flag.
+    */
+   {
+      png_voidp first_row = display->buffer;
+      ptrdiff_t row_bytes = display->row_stride;
+
+      if (linear != 0)
+         row_bytes *= 2;
+
+      /* The following expression is designed to work correctly whether it gives
+       * a signed or an unsigned result.
+       */
+      if (row_bytes < 0)
+      {
+         char *ptr = png_voidcast(char*, first_row);
+         ptr += (image->height-1) * (-row_bytes);
+         first_row = png_voidcast(png_voidp, ptr);
+      }
+
+      display->first_row = first_row;
+      display->row_bytes = row_bytes;
+   }
+
+   if (do_local_compose != 0)
+   {
+      int result;
+      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
+
+      display->local_row = row;
+      result = png_safe_execute(image, png_image_read_composite, display);
+      display->local_row = NULL;
+      png_free(png_ptr, row);
+
+      return result;
+   }
+
+   else if (do_local_background == 2)
+   {
+      int result;
+      png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
+
+      display->local_row = row;
+      result = png_safe_execute(image, png_image_read_background, display);
+      display->local_row = NULL;
+      png_free(png_ptr, row);
+
+      return result;
+   }
+
+   else
+   {
+      png_alloc_size_t row_bytes = display->row_bytes;
+
+      while (--passes >= 0)
+      {
+         png_uint_32      y = image->height;
+         png_bytep        row = png_voidcast(png_bytep, display->first_row);
+
+         while (y-- > 0)
+         {
+            png_read_row(png_ptr, row, NULL);
+            row += row_bytes;
+         }
+      }
+
+      return 1;
+   }
+}
+
+int PNGAPI
+png_image_finish_read(png_imagep image, png_const_colorp background,
+   void *buffer, png_int_32 row_stride, void *colormap)
+{
+   if (image != NULL && image->version == PNG_IMAGE_VERSION)
+   {
+      png_uint_32 check;
+
+      if (row_stride == 0)
+         row_stride = PNG_IMAGE_ROW_STRIDE(*image);
+
+      if (row_stride < 0)
+         check = -row_stride;
+
+      else
+         check = row_stride;
+
+      if (image->opaque != NULL && buffer != NULL &&
+         check >= PNG_IMAGE_ROW_STRIDE(*image))
+      {
+         if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 ||
+            (image->colormap_entries > 0 && colormap != NULL))
+         {
+            int result;
+            png_image_read_control display;
+
+            memset(&display, 0, (sizeof display));
+            display.image = image;
+            display.buffer = buffer;
+            display.row_stride = row_stride;
+            display.colormap = colormap;
+            display.background = background;
+            display.local_row = NULL;
+
+            /* Choose the correct 'end' routine; for the color-map case all the
+             * setup has already been done.
+             */
+            if ((image->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+               result =
+                  png_safe_execute(image, png_image_read_colormap, &display) &&
+                  png_safe_execute(image, png_image_read_colormapped, &display);
+
+            else
+               result =
+                  png_safe_execute(image, png_image_read_direct, &display);
+
+            png_image_free(image);
+            return result;
+         }
+
+         else
+            return png_image_error(image,
+               "png_image_finish_read[color-map]: no color-map");
+      }
+
+      else
+         return png_image_error(image,
+            "png_image_finish_read: invalid argument");
+   }
+
+   else if (image != NULL)
+      return png_image_error(image,
+         "png_image_finish_read: damaged PNG_IMAGE_VERSION");
+
+   return 0;
+}
+
+#endif /* SIMPLIFIED_READ */
+#endif /* READ */
diff --git a/third_party/lpng_v163/pngrio.c b/third_party/libpng/pngrio.c
similarity index 94%
rename from third_party/lpng_v163/pngrio.c
rename to third_party/libpng/pngrio.c
index d27a26d..38f7fd4 100644
--- a/third_party/lpng_v163/pngrio.c
+++ b/third_party/libpng/pngrio.c
@@ -1,117 +1,120 @@
-/* pngrio.c - functions for data input

- *

- * Last changed in libpng 1.6.0 [February 14, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file provides a location for all input.  Users who need

- * special handling are expected to write a function that has the same

- * arguments as this and performs a similar function, but that possibly

- * has a different input method.  Note that you shouldn't change this

- * function, but rather write a replacement function and then make

- * libpng use it at run time with png_set_read_fn(...).

- */

-

-#include "pngpriv.h"

-

-#ifdef PNG_READ_SUPPORTED

-

-/* Read the data from whatever input you are using.  The default routine

- * reads from a file pointer.  Note that this routine sometimes gets called

- * with very small lengths, so you should implement some kind of simple

- * buffering if you are using unbuffered reads.  This should never be asked

- * to read more then 64K on a 16 bit machine.

- */

-void /* PRIVATE */

-png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length)

-{

-   png_debug1(4, "reading %d bytes", (int)length);

-

-   if (png_ptr->read_data_fn != NULL)

-      (*(png_ptr->read_data_fn))(png_ptr, data, length);

-

-   else

-      png_error(png_ptr, "Call to NULL read function");

-}

-

-#ifdef PNG_STDIO_SUPPORTED

-/* This is the function that does the actual reading of data.  If you are

- * not reading from a standard C stream, you should create a replacement

- * read_data function and use it at run time with png_set_read_fn(), rather

- * than changing the library.

- */

-void PNGCBAPI

-png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)

-{

-   png_size_t check;

-

-   if (png_ptr == NULL)

-      return;

-

-   /* fread() returns 0 on error, so it is OK to store this in a png_size_t

-    * instead of an int, which is what fread() actually returns.

-    */

-   check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));

-

-   if (check != length)

-      png_error(png_ptr, "Read Error");

-}

-#endif

-

-/* This function allows the application to supply a new input function

- * for libpng if standard C streams aren't being used.

- *

- * This function takes as its arguments:

- *

- * png_ptr      - pointer to a png input data structure

- *

- * io_ptr       - pointer to user supplied structure containing info about

- *                the input functions.  May be NULL.

- *

- * read_data_fn - pointer to a new input function that takes as its

- *                arguments a pointer to a png_struct, a pointer to

- *                a location where input data can be stored, and a 32-bit

- *                unsigned int that is the number of bytes to be read.

- *                To exit and output any fatal error messages the new write

- *                function should call png_error(png_ptr, "Error msg").

- *                May be NULL, in which case libpng's default function will

- *                be used.

- */

-void PNGAPI

-png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,

-   png_rw_ptr read_data_fn)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->io_ptr = io_ptr;

-

-#ifdef PNG_STDIO_SUPPORTED

-   if (read_data_fn != NULL)

-      png_ptr->read_data_fn = read_data_fn;

-

-   else

-      png_ptr->read_data_fn = png_default_read_data;

-#else

-   png_ptr->read_data_fn = read_data_fn;

-#endif

-

-   /* It is an error to write to a read device */

-   if (png_ptr->write_data_fn != NULL)

-   {

-      png_ptr->write_data_fn = NULL;

-      png_warning(png_ptr,

-          "Can't set both read_data_fn and write_data_fn in the"

-          " same structure");

-   }

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-   png_ptr->output_flush_fn = NULL;

-#endif

-}

-#endif /* PNG_READ_SUPPORTED */

+
+/* pngrio.c - functions for data input
+ *
+ * Last changed in libpng 1.6.17 [March 26, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all input.  Users who need
+ * special handling are expected to write a function that has the same
+ * arguments as this and performs a similar function, but that possibly
+ * has a different input method.  Note that you shouldn't change this
+ * function, but rather write a replacement function and then make
+ * libpng use it at run time with png_set_read_fn(...).
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+/* Read the data from whatever input you are using.  The default routine
+ * reads from a file pointer.  Note that this routine sometimes gets called
+ * with very small lengths, so you should implement some kind of simple
+ * buffering if you are using unbuffered reads.  This should never be asked
+ * to read more than 64K on a 16-bit machine.
+ */
+void /* PRIVATE */
+png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length)
+{
+   png_debug1(4, "reading %d bytes", (int)length);
+
+   if (png_ptr->read_data_fn != NULL)
+      (*(png_ptr->read_data_fn))(png_ptr, data, length);
+
+   else
+      png_error(png_ptr, "Call to NULL read function");
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+/* This is the function that does the actual reading of data.  If you are
+ * not reading from a standard C stream, you should create a replacement
+ * read_data function and use it at run time with png_set_read_fn(), rather
+ * than changing the library.
+ */
+void PNGCBAPI
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_size_t check;
+
+   if (png_ptr == NULL)
+      return;
+
+   /* fread() returns 0 on error, so it is OK to store this in a png_size_t
+    * instead of an int, which is what fread() actually returns.
+    */
+   check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));
+
+   if (check != length)
+      png_error(png_ptr, "Read Error");
+}
+#endif
+
+/* This function allows the application to supply a new input function
+ * for libpng if standard C streams aren't being used.
+ *
+ * This function takes as its arguments:
+ *
+ * png_ptr      - pointer to a png input data structure
+ *
+ * io_ptr       - pointer to user supplied structure containing info about
+ *                the input functions.  May be NULL.
+ *
+ * read_data_fn - pointer to a new input function that takes as its
+ *                arguments a pointer to a png_struct, a pointer to
+ *                a location where input data can be stored, and a 32-bit
+ *                unsigned int that is the number of bytes to be read.
+ *                To exit and output any fatal error messages the new write
+ *                function should call png_error(png_ptr, "Error msg").
+ *                May be NULL, in which case libpng's default function will
+ *                be used.
+ */
+void PNGAPI
+png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,
+   png_rw_ptr read_data_fn)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->io_ptr = io_ptr;
+
+#ifdef PNG_STDIO_SUPPORTED
+   if (read_data_fn != NULL)
+      png_ptr->read_data_fn = read_data_fn;
+
+   else
+      png_ptr->read_data_fn = png_default_read_data;
+#else
+   png_ptr->read_data_fn = read_data_fn;
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+   /* It is an error to write to a read device */
+   if (png_ptr->write_data_fn != NULL)
+   {
+      png_ptr->write_data_fn = NULL;
+      png_warning(png_ptr,
+          "Can't set both read_data_fn and write_data_fn in the"
+          " same structure");
+   }
+#endif
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+   png_ptr->output_flush_fn = NULL;
+#endif
+}
+#endif /* READ */
diff --git a/third_party/lpng_v163/pngrtran.c b/third_party/libpng/pngrtran.c
similarity index 90%
rename from third_party/lpng_v163/pngrtran.c
rename to third_party/libpng/pngrtran.c
index a81034c..f129ef1 100644
--- a/third_party/lpng_v163/pngrtran.c
+++ b/third_party/libpng/pngrtran.c
@@ -1,5102 +1,4997 @@
-/* pngrtran.c - transforms the data in a row for PNG readers

- *

- * 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file contains functions optionally called by an application

- * in order to tell libpng how to handle data when reading a PNG.

- * Transformations that are used in both reading and writing are

- * in pngtrans.c.

- */

-

-#include "pngpriv.h"

-

-#ifdef PNG_READ_SUPPORTED

-

-/* Set the action on getting a CRC error for an ancillary or critical chunk. */

-void PNGAPI

-png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action)

-{

-   png_debug(1, "in png_set_crc_action");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* Tell libpng how we react to CRC errors in critical chunks */

-   switch (crit_action)

-   {

-      case PNG_CRC_NO_CHANGE:                        /* Leave setting as is */

-         break;

-

-      case PNG_CRC_WARN_USE:                               /* Warn/use data */

-         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;

-         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;

-         break;

-

-      case PNG_CRC_QUIET_USE:                             /* Quiet/use data */

-         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;

-         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |

-                           PNG_FLAG_CRC_CRITICAL_IGNORE;

-         break;

-

-      case PNG_CRC_WARN_DISCARD:    /* Not a valid action for critical data */

-         png_warning(png_ptr,

-            "Can't discard critical data on CRC error");

-      case PNG_CRC_ERROR_QUIT:                                /* Error/quit */

-

-      case PNG_CRC_DEFAULT:

-      default:

-         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;

-         break;

-   }

-

-   /* Tell libpng how we react to CRC errors in ancillary chunks */

-   switch (ancil_action)

-   {

-      case PNG_CRC_NO_CHANGE:                       /* Leave setting as is */

-         break;

-

-      case PNG_CRC_WARN_USE:                              /* Warn/use data */

-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;

-         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;

-         break;

-

-      case PNG_CRC_QUIET_USE:                            /* Quiet/use data */

-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;

-         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |

-                           PNG_FLAG_CRC_ANCILLARY_NOWARN;

-         break;

-

-      case PNG_CRC_ERROR_QUIT:                               /* Error/quit */

-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;

-         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;

-         break;

-

-      case PNG_CRC_WARN_DISCARD:                      /* Warn/discard data */

-

-      case PNG_CRC_DEFAULT:

-      default:

-         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;

-         break;

-   }

-}

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-/* Is it OK to set a transformation now?  Only if png_start_read_image or

- * png_read_update_info have not been called.  It is not necessary for the IHDR

- * to have been read in all cases, the parameter allows for this check too.

- */

-static int

-png_rtran_ok(png_structrp png_ptr, int need_IHDR)

-{

-   if (png_ptr != NULL)

-   {

-      if (png_ptr->flags & PNG_FLAG_ROW_INIT)

-         png_app_error(png_ptr,

-            "invalid after png_start_read_image or png_read_update_info");

-

-      else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0)

-         png_app_error(png_ptr, "invalid before the PNG header has been read");

-

-      else

-      {

-         /* Turn on failure to initialize correctly for all transforms. */

-         png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;

-

-         return 1; /* Ok */

-      }

-   }

-

-   return 0; /* no png_error possible! */

-}

-#endif

-

-#ifdef PNG_READ_BACKGROUND_SUPPORTED

-/* Handle alpha and tRNS via a background color */

-void PNGFAPI

-png_set_background_fixed(png_structrp png_ptr,

-    png_const_color_16p background_color, int background_gamma_code,

-    int need_expand, png_fixed_point background_gamma)

-{

-   png_debug(1, "in png_set_background_fixed");

-

-   if (!png_rtran_ok(png_ptr, 0) || background_color == NULL)

-      return;

-

-   if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)

-   {

-      png_warning(png_ptr, "Application must supply a known background gamma");

-      return;

-   }

-

-   png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA;

-   png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-   png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-

-   png_ptr->background = *background_color;

-   png_ptr->background_gamma = background_gamma;

-   png_ptr->background_gamma_type = (png_byte)(background_gamma_code);

-   if (need_expand)

-      png_ptr->transformations |= PNG_BACKGROUND_EXPAND;

-   else

-      png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_background(png_structrp png_ptr,

-    png_const_color_16p background_color, int background_gamma_code,

-    int need_expand, double background_gamma)

-{

-   png_set_background_fixed(png_ptr, background_color, background_gamma_code,

-      need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));

-}

-#  endif  /* FLOATING_POINT */

-#endif /* READ_BACKGROUND */

-

-/* Scale 16-bit depth files to 8-bit depth.  If both of these are set then the

- * one that pngrtran does first (scale) happens.  This is necessary to allow the

- * TRANSFORM and API behavior to be somewhat consistent, and it's simpler.

- */

-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-void PNGAPI

-png_set_scale_16(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_scale_16");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= PNG_SCALE_16_TO_8;

-}

-#endif

-

-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-/* Chop 16-bit depth files to 8-bit depth */

-void PNGAPI

-png_set_strip_16(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_strip_16");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= PNG_16_TO_8;

-}

-#endif

-

-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED

-void PNGAPI

-png_set_strip_alpha(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_strip_alpha");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= PNG_STRIP_ALPHA;

-}

-#endif

-

-#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)

-static png_fixed_point

-translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,

-   int is_screen)

-{

-   /* Check for flag values.  The main reason for having the old Mac value as a

-    * flag is that it is pretty near impossible to work out what the correct

-    * value is from Apple documentation - a working Mac system is needed to

-    * discover the value!

-    */

-   if (output_gamma == PNG_DEFAULT_sRGB ||

-      output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)

-   {

-      /* If there is no sRGB support this just sets the gamma to the standard

-       * sRGB value.  (This is a side effect of using this function!)

-       */

-#     ifdef PNG_READ_sRGB_SUPPORTED

-         png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;

-#     else

-         PNG_UNUSED(png_ptr)

-#     endif

-      if (is_screen)

-         output_gamma = PNG_GAMMA_sRGB;

-      else

-         output_gamma = PNG_GAMMA_sRGB_INVERSE;

-   }

-

-   else if (output_gamma == PNG_GAMMA_MAC_18 ||

-      output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18)

-   {

-      if (is_screen)

-         output_gamma = PNG_GAMMA_MAC_OLD;

-      else

-         output_gamma = PNG_GAMMA_MAC_INVERSE;

-   }

-

-   return output_gamma;

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-static png_fixed_point

-convert_gamma_value(png_structrp png_ptr, double output_gamma)

-{

-   /* The following silently ignores cases where fixed point (times 100,000)

-    * gamma values are passed to the floating point API.  This is safe and it

-    * means the fixed point constants work just fine with the floating point

-    * API.  The alternative would just lead to undetected errors and spurious

-    * bug reports.  Negative values fail inside the _fixed API unless they

-    * correspond to the flag values.

-    */

-   if (output_gamma > 0 && output_gamma < 128)

-      output_gamma *= PNG_FP_1;

-

-   /* This preserves -1 and -2 exactly: */

-   output_gamma = floor(output_gamma + .5);

-

-   if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN)

-      png_fixed_error(png_ptr, "gamma value");

-

-   return (png_fixed_point)output_gamma;

-}

-#  endif

-#endif /* READ_ALPHA_MODE || READ_GAMMA */

-

-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED

-void PNGFAPI

-png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,

-   png_fixed_point output_gamma)

-{

-   int compose = 0;

-   png_fixed_point file_gamma;

-

-   png_debug(1, "in png_set_alpha_mode");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);

-

-   /* Validate the value to ensure it is in a reasonable range. The value

-    * is expected to be 1 or greater, but this range test allows for some

-    * viewing correction values.  The intent is to weed out users of this API

-    * who use the inverse of the gamma value accidentally!  Since some of these

-    * values are reasonable this may have to be changed.

-    */

-   if (output_gamma < 70000 || output_gamma > 300000)

-      png_error(png_ptr, "output gamma out of expected range");

-

-   /* The default file gamma is the inverse of the output gamma; the output

-    * gamma may be changed below so get the file value first:

-    */

-   file_gamma = png_reciprocal(output_gamma);

-

-   /* There are really 8 possibilities here, composed of any combination

-    * of:

-    *

-    *    premultiply the color channels

-    *    do not encode non-opaque pixels

-    *    encode the alpha as well as the color channels

-    *

-    * The differences disappear if the input/output ('screen') gamma is 1.0,

-    * because then the encoding is a no-op and there is only the choice of

-    * premultiplying the color channels or not.

-    *

-    * png_set_alpha_mode and png_set_background interact because both use

-    * png_compose to do the work.  Calling both is only useful when

-    * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along

-    * with a default gamma value.  Otherwise PNG_COMPOSE must not be set.

-    */

-   switch (mode)

-   {

-      case PNG_ALPHA_PNG:        /* default: png standard */

-         /* No compose, but it may be set by png_set_background! */

-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-         break;

-

-      case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */

-         compose = 1;

-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-         /* The output is linear: */

-         output_gamma = PNG_FP_1;

-         break;

-

-      case PNG_ALPHA_OPTIMIZED:  /* associated, non-opaque pixels linear */

-         compose = 1;

-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-         png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA;

-         /* output_gamma records the encoding of opaque pixels! */

-         break;

-

-      case PNG_ALPHA_BROKEN:     /* associated, non-linear, alpha encoded */

-         compose = 1;

-         png_ptr->transformations |= PNG_ENCODE_ALPHA;

-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-         break;

-

-      default:

-         png_error(png_ptr, "invalid alpha mode");

-   }

-

-   /* Only set the default gamma if the file gamma has not been set (this has

-    * the side effect that the gamma in a second call to png_set_alpha_mode will

-    * be ignored.)

-    */

-   if (png_ptr->colorspace.gamma == 0)

-   {

-      png_ptr->colorspace.gamma = file_gamma;

-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;

-   }

-

-   /* But always set the output gamma: */

-   png_ptr->screen_gamma = output_gamma;

-

-   /* Finally, if pre-multiplying, set the background fields to achieve the

-    * desired result.

-    */

-   if (compose)

-   {

-      /* And obtain alpha pre-multiplication by composing on black: */

-      memset(&png_ptr->background, 0, (sizeof png_ptr->background));

-      png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */

-      png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;

-      png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;

-

-      if (png_ptr->transformations & PNG_COMPOSE)

-         png_error(png_ptr,

-            "conflicting calls to set alpha mode and background");

-

-      png_ptr->transformations |= PNG_COMPOSE;

-   }

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)

-{

-   png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,

-      output_gamma));

-}

-#  endif

-#endif

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-/* Dither file to 8-bit.  Supply a palette, the current number

- * of elements in the palette, the maximum number of elements

- * allowed, and a histogram if possible.  If the current number

- * of colors is greater then the maximum number, the palette will be

- * modified to fit in the maximum number.  "full_quantize" indicates

- * whether we need a quantizing cube set up for RGB images, or if we

- * simply are reducing the number of colors in a paletted image.

- */

-

-typedef struct png_dsort_struct

-{

-   struct png_dsort_struct * next;

-   png_byte left;

-   png_byte right;

-} png_dsort;

-typedef png_dsort *   png_dsortp;

-typedef png_dsort * * png_dsortpp;

-

-void PNGAPI

-png_set_quantize(png_structrp png_ptr, png_colorp palette,

-    int num_palette, int maximum_colors, png_const_uint_16p histogram,

-    int full_quantize)

-{

-   png_debug(1, "in png_set_quantize");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= PNG_QUANTIZE;

-

-   if (!full_quantize)

-   {

-      int i;

-

-      png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,

-          (png_uint_32)(num_palette * (sizeof (png_byte))));

-      for (i = 0; i < num_palette; i++)

-         png_ptr->quantize_index[i] = (png_byte)i;

-   }

-

-   if (num_palette > maximum_colors)

-   {

-      if (histogram != NULL)

-      {

-         /* This is easy enough, just throw out the least used colors.

-          * Perhaps not the best solution, but good enough.

-          */

-

-         int i;

-

-         /* Initialize an array to sort colors */

-         png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,

-             (png_uint_32)(num_palette * (sizeof (png_byte))));

-

-         /* Initialize the quantize_sort array */

-         for (i = 0; i < num_palette; i++)

-            png_ptr->quantize_sort[i] = (png_byte)i;

-

-         /* Find the least used palette entries by starting a

-          * bubble sort, and running it until we have sorted

-          * out enough colors.  Note that we don't care about

-          * sorting all the colors, just finding which are

-          * least used.

-          */

-

-         for (i = num_palette - 1; i >= maximum_colors; i--)

-         {

-            int done; /* To stop early if the list is pre-sorted */

-            int j;

-

-            done = 1;

-            for (j = 0; j < i; j++)

-            {

-               if (histogram[png_ptr->quantize_sort[j]]

-                   < histogram[png_ptr->quantize_sort[j + 1]])

-               {

-                  png_byte t;

-

-                  t = png_ptr->quantize_sort[j];

-                  png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1];

-                  png_ptr->quantize_sort[j + 1] = t;

-                  done = 0;

-               }

-            }

-

-            if (done)

-               break;

-         }

-

-         /* Swap the palette around, and set up a table, if necessary */

-         if (full_quantize)

-         {

-            int j = num_palette;

-

-            /* Put all the useful colors within the max, but don't

-             * move the others.

-             */

-            for (i = 0; i < maximum_colors; i++)

-            {

-               if ((int)png_ptr->quantize_sort[i] >= maximum_colors)

-               {

-                  do

-                     j--;

-                  while ((int)png_ptr->quantize_sort[j] >= maximum_colors);

-

-                  palette[i] = palette[j];

-               }

-            }

-         }

-         else

-         {

-            int j = num_palette;

-

-            /* Move all the used colors inside the max limit, and

-             * develop a translation table.

-             */

-            for (i = 0; i < maximum_colors; i++)

-            {

-               /* Only move the colors we need to */

-               if ((int)png_ptr->quantize_sort[i] >= maximum_colors)

-               {

-                  png_color tmp_color;

-

-                  do

-                     j--;

-                  while ((int)png_ptr->quantize_sort[j] >= maximum_colors);

-

-                  tmp_color = palette[j];

-                  palette[j] = palette[i];

-                  palette[i] = tmp_color;

-                  /* Indicate where the color went */

-                  png_ptr->quantize_index[j] = (png_byte)i;

-                  png_ptr->quantize_index[i] = (png_byte)j;

-               }

-            }

-

-            /* Find closest color for those colors we are not using */

-            for (i = 0; i < num_palette; i++)

-            {

-               if ((int)png_ptr->quantize_index[i] >= maximum_colors)

-               {

-                  int min_d, k, min_k, d_index;

-

-                  /* Find the closest color to one we threw out */

-                  d_index = png_ptr->quantize_index[i];

-                  min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);

-                  for (k = 1, min_k = 0; k < maximum_colors; k++)

-                  {

-                     int d;

-

-                     d = PNG_COLOR_DIST(palette[d_index], palette[k]);

-

-                     if (d < min_d)

-                     {

-                        min_d = d;

-                        min_k = k;

-                     }

-                  }

-                  /* Point to closest color */

-                  png_ptr->quantize_index[i] = (png_byte)min_k;

-               }

-            }

-         }

-         png_free(png_ptr, png_ptr->quantize_sort);

-         png_ptr->quantize_sort = NULL;

-      }

-      else

-      {

-         /* This is much harder to do simply (and quickly).  Perhaps

-          * we need to go through a median cut routine, but those

-          * don't always behave themselves with only a few colors

-          * as input.  So we will just find the closest two colors,

-          * and throw out one of them (chosen somewhat randomly).

-          * [We don't understand this at all, so if someone wants to

-          *  work on improving it, be our guest - AED, GRP]

-          */

-         int i;

-         int max_d;

-         int num_new_palette;

-         png_dsortp t;

-         png_dsortpp hash;

-

-         t = NULL;

-

-         /* Initialize palette index arrays */

-         png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,

-             (png_uint_32)(num_palette * (sizeof (png_byte))));

-         png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,

-             (png_uint_32)(num_palette * (sizeof (png_byte))));

-

-         /* Initialize the sort array */

-         for (i = 0; i < num_palette; i++)

-         {

-            png_ptr->index_to_palette[i] = (png_byte)i;

-            png_ptr->palette_to_index[i] = (png_byte)i;

-         }

-

-         hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 *

-             (sizeof (png_dsortp))));

-

-         num_new_palette = num_palette;

-

-         /* Initial wild guess at how far apart the farthest pixel

-          * pair we will be eliminating will be.  Larger

-          * numbers mean more areas will be allocated, Smaller

-          * numbers run the risk of not saving enough data, and

-          * having to do this all over again.

-          *

-          * I have not done extensive checking on this number.

-          */

-         max_d = 96;

-

-         while (num_new_palette > maximum_colors)

-         {

-            for (i = 0; i < num_new_palette - 1; i++)

-            {

-               int j;

-

-               for (j = i + 1; j < num_new_palette; j++)

-               {

-                  int d;

-

-                  d = PNG_COLOR_DIST(palette[i], palette[j]);

-

-                  if (d <= max_d)

-                  {

-

-                     t = (png_dsortp)png_malloc_warn(png_ptr,

-                         (png_uint_32)(sizeof (png_dsort)));

-

-                     if (t == NULL)

-                         break;

-

-                     t->next = hash[d];

-                     t->left = (png_byte)i;

-                     t->right = (png_byte)j;

-                     hash[d] = t;

-                  }

-               }

-               if (t == NULL)

-                  break;

-            }

-

-            if (t != NULL)

-            for (i = 0; i <= max_d; i++)

-            {

-               if (hash[i] != NULL)

-               {

-                  png_dsortp p;

-

-                  for (p = hash[i]; p; p = p->next)

-                  {

-                     if ((int)png_ptr->index_to_palette[p->left]

-                         < num_new_palette &&

-                         (int)png_ptr->index_to_palette[p->right]

-                         < num_new_palette)

-                     {

-                        int j, next_j;

-

-                        if (num_new_palette & 0x01)

-                        {

-                           j = p->left;

-                           next_j = p->right;

-                        }

-                        else

-                        {

-                           j = p->right;

-                           next_j = p->left;

-                        }

-

-                        num_new_palette--;

-                        palette[png_ptr->index_to_palette[j]]

-                            = palette[num_new_palette];

-                        if (!full_quantize)

-                        {

-                           int k;

-

-                           for (k = 0; k < num_palette; k++)

-                           {

-                              if (png_ptr->quantize_index[k] ==

-                                  png_ptr->index_to_palette[j])

-                                 png_ptr->quantize_index[k] =

-                                     png_ptr->index_to_palette[next_j];

-

-                              if ((int)png_ptr->quantize_index[k] ==

-                                  num_new_palette)

-                                 png_ptr->quantize_index[k] =

-                                     png_ptr->index_to_palette[j];

-                           }

-                        }

-

-                        png_ptr->index_to_palette[png_ptr->palette_to_index

-                            [num_new_palette]] = png_ptr->index_to_palette[j];

-

-                        png_ptr->palette_to_index[png_ptr->index_to_palette[j]]

-                            = png_ptr->palette_to_index[num_new_palette];

-

-                        png_ptr->index_to_palette[j] =

-                            (png_byte)num_new_palette;

-

-                        png_ptr->palette_to_index[num_new_palette] =

-                            (png_byte)j;

-                     }

-                     if (num_new_palette <= maximum_colors)

-                        break;

-                  }

-                  if (num_new_palette <= maximum_colors)

-                     break;

-               }

-            }

-

-            for (i = 0; i < 769; i++)

-            {

-               if (hash[i] != NULL)

-               {

-                  png_dsortp p = hash[i];

-                  while (p)

-                  {

-                     t = p->next;

-                     png_free(png_ptr, p);

-                     p = t;

-                  }

-               }

-               hash[i] = 0;

-            }

-            max_d += 96;

-         }

-         png_free(png_ptr, hash);

-         png_free(png_ptr, png_ptr->palette_to_index);

-         png_free(png_ptr, png_ptr->index_to_palette);

-         png_ptr->palette_to_index = NULL;

-         png_ptr->index_to_palette = NULL;

-      }

-      num_palette = maximum_colors;

-   }

-   if (png_ptr->palette == NULL)

-   {

-      png_ptr->palette = palette;

-   }

-   png_ptr->num_palette = (png_uint_16)num_palette;

-

-   if (full_quantize)

-   {

-      int i;

-      png_bytep distance;

-      int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS +

-          PNG_QUANTIZE_BLUE_BITS;

-      int num_red = (1 << PNG_QUANTIZE_RED_BITS);

-      int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);

-      int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);

-      png_size_t num_entries = ((png_size_t)1 << total_bits);

-

-      png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,

-          (png_uint_32)(num_entries * (sizeof (png_byte))));

-

-      distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *

-          (sizeof (png_byte))));

-

-      memset(distance, 0xff, num_entries * (sizeof (png_byte)));

-

-      for (i = 0; i < num_palette; i++)

-      {

-         int ir, ig, ib;

-         int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));

-         int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));

-         int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));

-

-         for (ir = 0; ir < num_red; ir++)

-         {

-            /* int dr = abs(ir - r); */

-            int dr = ((ir > r) ? ir - r : r - ir);

-            int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +

-                PNG_QUANTIZE_GREEN_BITS));

-

-            for (ig = 0; ig < num_green; ig++)

-            {

-               /* int dg = abs(ig - g); */

-               int dg = ((ig > g) ? ig - g : g - ig);

-               int dt = dr + dg;

-               int dm = ((dr > dg) ? dr : dg);

-               int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);

-

-               for (ib = 0; ib < num_blue; ib++)

-               {

-                  int d_index = index_g | ib;

-                  /* int db = abs(ib - b); */

-                  int db = ((ib > b) ? ib - b : b - ib);

-                  int dmax = ((dm > db) ? dm : db);

-                  int d = dmax + dt + db;

-

-                  if (d < (int)distance[d_index])

-                  {

-                     distance[d_index] = (png_byte)d;

-                     png_ptr->palette_lookup[d_index] = (png_byte)i;

-                  }

-               }

-            }

-         }

-      }

-

-      png_free(png_ptr, distance);

-   }

-}

-#endif /* PNG_READ_QUANTIZE_SUPPORTED */

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-void PNGFAPI

-png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,

-   png_fixed_point file_gamma)

-{

-   png_debug(1, "in png_set_gamma_fixed");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   /* New in libpng-1.5.4 - reserve particular negative values as flags. */

-   scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);

-   file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);

-

-   /* Checking the gamma values for being >0 was added in 1.5.4 along with the

-    * premultiplied alpha support; this actually hides an undocumented feature

-    * of the previous implementation which allowed gamma processing to be

-    * disabled in background handling.  There is no evidence (so far) that this

-    * was being used; however, png_set_background itself accepted and must still

-    * accept '0' for the gamma value it takes, because it isn't always used.

-    *

-    * Since this is an API change (albeit a very minor one that removes an

-    * undocumented API feature) the following checks were only enabled in

-    * libpng-1.6.0.

-    */

-   if (file_gamma <= 0)

-      png_error(png_ptr, "invalid file gamma in png_set_gamma");

-

-   if (scrn_gamma <= 0)

-      png_error(png_ptr, "invalid screen gamma in png_set_gamma");

-

-   /* Set the gamma values unconditionally - this overrides the value in the PNG

-    * file if a gAMA chunk was present.  png_set_alpha_mode provides a

-    * different, easier, way to default the file gamma.

-    */

-   png_ptr->colorspace.gamma = file_gamma;

-   png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;

-   png_ptr->screen_gamma = scrn_gamma;

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma)

-{

-   png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),

-      convert_gamma_value(png_ptr, file_gamma));

-}

-#  endif /* FLOATING_POINT_SUPPORTED */

-#endif /* READ_GAMMA */

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-/* Expand paletted images to RGB, expand grayscale images of

- * less than 8-bit depth to 8-bit depth, and expand tRNS chunks

- * to alpha channels.

- */

-void PNGAPI

-png_set_expand(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_expand");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);

-}

-

-/* GRR 19990627:  the following three functions currently are identical

- *  to png_set_expand().  However, it is entirely reasonable that someone

- *  might wish to expand an indexed image to RGB but *not* expand a single,

- *  fully transparent palette entry to a full alpha channel--perhaps instead

- *  convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace

- *  the transparent color with a particular RGB value, or drop tRNS entirely.

- *  IOW, a future version of the library may make the transformations flag

- *  a bit more fine-grained, with separate bits for each of these three

- *  functions.

- *

- *  More to the point, these functions make it obvious what libpng will be

- *  doing, whereas "expand" can (and does) mean any number of things.

- *

- *  GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified

- *  to expand only the sample depth but not to expand the tRNS to alpha

- *  and its name was changed to png_set_expand_gray_1_2_4_to_8().

- */

-

-/* Expand paletted images to RGB. */

-void PNGAPI

-png_set_palette_to_rgb(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_palette_to_rgb");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);

-}

-

-/* Expand grayscale images of less than 8-bit depth to 8 bits. */

-void PNGAPI

-png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_expand_gray_1_2_4_to_8");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= PNG_EXPAND;

-}

-

-/* Expand tRNS chunks to alpha channels. */

-void PNGAPI

-png_set_tRNS_to_alpha(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_tRNS_to_alpha");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);

-}

-#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */

-

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise

- * it may not work correctly.)

- */

-void PNGAPI

-png_set_expand_16(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_expand_16");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);

-}

-#endif

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-void PNGAPI

-png_set_gray_to_rgb(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_gray_to_rgb");

-

-   if (!png_rtran_ok(png_ptr, 0))

-      return;

-

-   /* Because rgb must be 8 bits or more: */

-   png_set_expand_gray_1_2_4_to_8(png_ptr);

-   png_ptr->transformations |= PNG_GRAY_TO_RGB;

-}

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-void PNGFAPI

-png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,

-    png_fixed_point red, png_fixed_point green)

-{

-   png_debug(1, "in png_set_rgb_to_gray");

-

-   /* Need the IHDR here because of the check on color_type below. */

-   /* TODO: fix this */

-   if (!png_rtran_ok(png_ptr, 1))

-      return;

-

-   switch(error_action)

-   {

-      case PNG_ERROR_ACTION_NONE:

-         png_ptr->transformations |= PNG_RGB_TO_GRAY;

-         break;

-

-      case PNG_ERROR_ACTION_WARN:

-         png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;

-         break;

-

-      case PNG_ERROR_ACTION_ERROR:

-         png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;

-         break;

-

-      default:

-         png_error(png_ptr, "invalid error action to rgb_to_gray");

-         break;

-   }

-

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-#ifdef PNG_READ_EXPAND_SUPPORTED

-      png_ptr->transformations |= PNG_EXPAND;

-#else

-   {

-      /* Make this an error in 1.6 because otherwise the application may assume

-       * that it just worked and get a memory overwrite.

-       */

-      png_error(png_ptr,

-        "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");

-

-      /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */

-   }

-#endif

-   {

-      if (red >= 0 && green >= 0 && red + green <= PNG_FP_1)

-      {

-         png_uint_16 red_int, green_int;

-

-         /* NOTE: this calculation does not round, but this behavior is retained

-          * for consistency, the inaccuracy is very small.  The code here always

-          * overwrites the coefficients, regardless of whether they have been

-          * defaulted or set already.

-          */

-         red_int = (png_uint_16)(((png_uint_32)red*32768)/100000);

-         green_int = (png_uint_16)(((png_uint_32)green*32768)/100000);

-

-         png_ptr->rgb_to_gray_red_coeff   = red_int;

-         png_ptr->rgb_to_gray_green_coeff = green_int;

-         png_ptr->rgb_to_gray_coefficients_set = 1;

-      }

-

-      else

-      {

-         if (red >= 0 && green >= 0)

-            png_app_warning(png_ptr,

-               "ignoring out of range rgb_to_gray coefficients");

-

-         /* Use the defaults, from the cHRM chunk if set, else the historical

-          * values which are close to the sRGB/HDTV/ITU-Rec 709 values.  See

-          * png_do_rgb_to_gray for more discussion of the values.  In this case

-          * the coefficients are not marked as 'set' and are not overwritten if

-          * something has already provided a default.

-          */

-         if (png_ptr->rgb_to_gray_red_coeff == 0 &&

-            png_ptr->rgb_to_gray_green_coeff == 0)

-         {

-            png_ptr->rgb_to_gray_red_coeff   = 6968;

-            png_ptr->rgb_to_gray_green_coeff = 23434;

-            /* png_ptr->rgb_to_gray_blue_coeff  = 2366; */

-         }

-      }

-   }

-}

-

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-/* Convert a RGB image to a grayscale of the same width.  This allows us,

- * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.

- */

-

-void PNGAPI

-png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,

-   double green)

-{

-   png_set_rgb_to_gray_fixed(png_ptr, error_action,

-      png_fixed(png_ptr, red, "rgb to gray red coefficient"),

-      png_fixed(png_ptr, green, "rgb to gray green coefficient"));

-}

-#endif /* FLOATING POINT */

-

-#endif /* RGB_TO_GRAY */

-

-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \

-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)

-void PNGAPI

-png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr

-    read_user_transform_fn)

-{

-   png_debug(1, "in png_set_read_user_transform_fn");

-

-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED

-   png_ptr->transformations |= PNG_USER_TRANSFORM;

-   png_ptr->read_user_transform_fn = read_user_transform_fn;

-#endif

-}

-#endif

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-#ifdef PNG_READ_GAMMA_SUPPORTED

-/* In the case of gamma transformations only do transformations on images where

- * the [file] gamma and screen_gamma are not close reciprocals, otherwise it

- * slows things down slightly, and also needlessly introduces small errors.

- */

-static int /* PRIVATE */

-png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma)

-{

-   /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma

-    * correction as a difference of the overall transform from 1.0

-    *

-    * We want to compare the threshold with s*f - 1, if we get

-    * overflow here it is because of wacky gamma values so we

-    * turn on processing anyway.

-    */

-   png_fixed_point gtest;

-   return !png_muldiv(&gtest, screen_gamma, file_gamma, PNG_FP_1) ||

-       png_gamma_significant(gtest);

-}

-#endif

-

-/* Initialize everything needed for the read.  This includes modifying

- * the palette.

- */

-

-/*For the moment 'png_init_palette_transformations' and

- * 'png_init_rgb_transformations' only do some flag canceling optimizations.

- * The intent is that these two routines should have palette or rgb operations

- * extracted from 'png_init_read_transformations'.

- */

-static void /* PRIVATE */

-png_init_palette_transformations(png_structrp png_ptr)

-{

-   /* Called to handle the (input) palette case.  In png_do_read_transformations

-    * the first step is to expand the palette if requested, so this code must

-    * take care to only make changes that are invariant with respect to the

-    * palette expansion, or only do them if there is no expansion.

-    *

-    * STRIP_ALPHA has already been handled in the caller (by setting num_trans

-    * to 0.)

-    */

-   int input_has_alpha = 0;

-   int input_has_transparency = 0;

-

-   if (png_ptr->num_trans > 0)

-   {

-      int i;

-

-      /* Ignore if all the entries are opaque (unlikely!) */

-      for (i=0; i<png_ptr->num_trans; ++i)

-         if (png_ptr->trans_alpha[i] == 255)

-            continue;

-         else if (png_ptr->trans_alpha[i] == 0)

-            input_has_transparency = 1;

-         else

-            input_has_alpha = 1;

-   }

-

-   /* If no alpha we can optimize. */

-   if (!input_has_alpha)

-   {

-      /* Any alpha means background and associative alpha processing is

-       * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA

-       * and ENCODE_ALPHA are irrelevant.

-       */

-      png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-

-      if (!input_has_transparency)

-         png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);

-   }

-

-#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)

-   /* png_set_background handling - deals with the complexity of whether the

-    * background color is in the file format or the screen format in the case

-    * where an 'expand' will happen.

-    */

-

-   /* The following code cannot be entered in the alpha pre-multiplication case

-    * because PNG_BACKGROUND_EXPAND is cancelled below.

-    */

-   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&

-       (png_ptr->transformations & PNG_EXPAND))

-   {

-      {

-         png_ptr->background.red   =

-             png_ptr->palette[png_ptr->background.index].red;

-         png_ptr->background.green =

-             png_ptr->palette[png_ptr->background.index].green;

-         png_ptr->background.blue  =

-             png_ptr->palette[png_ptr->background.index].blue;

-

-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED

-        if (png_ptr->transformations & PNG_INVERT_ALPHA)

-        {

-           if (!(png_ptr->transformations & PNG_EXPAND_tRNS))

-           {

-              /* Invert the alpha channel (in tRNS) unless the pixels are

-               * going to be expanded, in which case leave it for later

-               */

-              int i, istop = png_ptr->num_trans;

-

-              for (i=0; i<istop; i++)

-                 png_ptr->trans_alpha[i] = (png_byte)(255 -

-                    png_ptr->trans_alpha[i]);

-           }

-        }

-#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */

-      }

-   } /* background expand and (therefore) no alpha association. */

-#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */

-}

-

-static void /* PRIVATE */

-png_init_rgb_transformations(png_structrp png_ptr)

-{

-   /* Added to libpng-1.5.4: check the color type to determine whether there

-    * is any alpha or transparency in the image and simply cancel the

-    * background and alpha mode stuff if there isn't.

-    */

-   int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0;

-   int input_has_transparency = png_ptr->num_trans > 0;

-

-   /* If no alpha we can optimize. */

-   if (!input_has_alpha)

-   {

-      /* Any alpha means background and associative alpha processing is

-       * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA

-       * and ENCODE_ALPHA are irrelevant.

-       */

-#     ifdef PNG_READ_ALPHA_MODE_SUPPORTED

-         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-#     endif

-

-      if (!input_has_transparency)

-         png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);

-   }

-

-#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)

-   /* png_set_background handling - deals with the complexity of whether the

-    * background color is in the file format or the screen format in the case

-    * where an 'expand' will happen.

-    */

-

-   /* The following code cannot be entered in the alpha pre-multiplication case

-    * because PNG_BACKGROUND_EXPAND is cancelled below.

-    */

-   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&

-       (png_ptr->transformations & PNG_EXPAND) &&

-       !(png_ptr->color_type & PNG_COLOR_MASK_COLOR))

-       /* i.e., GRAY or GRAY_ALPHA */

-   {

-      {

-         /* Expand background and tRNS chunks */

-         int gray = png_ptr->background.gray;

-         int trans_gray = png_ptr->trans_color.gray;

-

-         switch (png_ptr->bit_depth)

-         {

-            case 1:

-               gray *= 0xff;

-               trans_gray *= 0xff;

-               break;

-

-            case 2:

-               gray *= 0x55;

-               trans_gray *= 0x55;

-               break;

-

-            case 4:

-               gray *= 0x11;

-               trans_gray *= 0x11;

-               break;

-

-            default:

-

-            case 8:

-               /* FALL THROUGH (Already 8 bits) */

-

-            case 16:

-               /* Already a full 16 bits */

-               break;

-         }

-

-         png_ptr->background.red = png_ptr->background.green =

-            png_ptr->background.blue = (png_uint_16)gray;

-

-         if (!(png_ptr->transformations & PNG_EXPAND_tRNS))

-         {

-            png_ptr->trans_color.red = png_ptr->trans_color.green =

-               png_ptr->trans_color.blue = (png_uint_16)trans_gray;

-         }

-      }

-   } /* background expand and (therefore) no alpha association. */

-#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */

-}

-

-void /* PRIVATE */

-png_init_read_transformations(png_structrp png_ptr)

-{

-   png_debug(1, "in png_init_read_transformations");

-

-   /* This internal function is called from png_read_start_row in pngrutil.c

-    * and it is called before the 'rowbytes' calculation is done, so the code

-    * in here can change or update the transformations flags.

-    *

-    * First do updates that do not depend on the details of the PNG image data

-    * being processed.

-    */

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds

-    * png_set_alpha_mode and this is another source for a default file gamma so

-    * the test needs to be performed later - here.  In addition prior to 1.5.4

-    * the tests were repeated for the PALETTE color type here - this is no

-    * longer necessary (and doesn't seem to have been necessary before.)

-    */

-   {

-      /* The following temporary indicates if overall gamma correction is

-       * required.

-       */

-      int gamma_correction = 0;

-

-      if (png_ptr->colorspace.gamma != 0) /* has been set */

-      {

-         if (png_ptr->screen_gamma != 0) /* screen set too */

-            gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,

-               png_ptr->screen_gamma);

-

-         else

-            /* Assume the output matches the input; a long time default behavior

-             * of libpng, although the standard has nothing to say about this.

-             */

-            png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma);

-      }

-

-      else if (png_ptr->screen_gamma != 0)

-         /* The converse - assume the file matches the screen, note that this

-          * perhaps undesireable default can (from 1.5.4) be changed by calling

-          * png_set_alpha_mode (even if the alpha handling mode isn't required

-          * or isn't changed from the default.)

-          */

-         png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma);

-

-      else /* neither are set */

-         /* Just in case the following prevents any processing - file and screen

-          * are both assumed to be linear and there is no way to introduce a

-          * third gamma value other than png_set_background with 'UNIQUE', and,

-          * prior to 1.5.4

-          */

-         png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1;

-

-      /* We have a gamma value now. */

-      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;

-

-      /* Now turn the gamma transformation on or off as appropriate.  Notice

-       * that PNG_GAMMA just refers to the file->screen correction.  Alpha

-       * composition may independently cause gamma correction because it needs

-       * linear data (e.g. if the file has a gAMA chunk but the screen gamma

-       * hasn't been specified.)  In any case this flag may get turned off in

-       * the code immediately below if the transform can be handled outside the

-       * row loop.

-       */

-      if (gamma_correction)

-         png_ptr->transformations |= PNG_GAMMA;

-

-      else

-         png_ptr->transformations &= ~PNG_GAMMA;

-   }

-#endif

-

-   /* Certain transformations have the effect of preventing other

-    * transformations that happen afterward in png_do_read_transformations,

-    * resolve the interdependencies here.  From the code of

-    * png_do_read_transformations the order is:

-    *

-    *  1) PNG_EXPAND (including PNG_EXPAND_tRNS)

-    *  2) PNG_STRIP_ALPHA (if no compose)

-    *  3) PNG_RGB_TO_GRAY

-    *  4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY

-    *  5) PNG_COMPOSE

-    *  6) PNG_GAMMA

-    *  7) PNG_STRIP_ALPHA (if compose)

-    *  8) PNG_ENCODE_ALPHA

-    *  9) PNG_SCALE_16_TO_8

-    * 10) PNG_16_TO_8

-    * 11) PNG_QUANTIZE (converts to palette)

-    * 12) PNG_EXPAND_16

-    * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY

-    * 14) PNG_INVERT_MONO

-    * 15) PNG_SHIFT

-    * 16) PNG_PACK

-    * 17) PNG_BGR

-    * 18) PNG_PACKSWAP

-    * 19) PNG_FILLER (includes PNG_ADD_ALPHA)

-    * 20) PNG_INVERT_ALPHA

-    * 21) PNG_SWAP_ALPHA

-    * 22) PNG_SWAP_BYTES

-    * 23) PNG_USER_TRANSFORM [must be last]

-    */

-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED

-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&

-      !(png_ptr->transformations & PNG_COMPOSE))

-   {

-      /* Stripping the alpha channel happens immediately after the 'expand'

-       * transformations, before all other transformation, so it cancels out

-       * the alpha handling.  It has the side effect negating the effect of

-       * PNG_EXPAND_tRNS too:

-       */

-      png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA |

-         PNG_EXPAND_tRNS);

-      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-

-      /* Kill the tRNS chunk itself too.  Prior to 1.5.4 this did not happen

-       * so transparency information would remain just so long as it wasn't

-       * expanded.  This produces unexpected API changes if the set of things

-       * that do PNG_EXPAND_tRNS changes (perfectly possible given the

-       * documentation - which says ask for what you want, accept what you

-       * get.)  This makes the behavior consistent from 1.5.4:

-       */

-      png_ptr->num_trans = 0;

-   }

-#endif /* STRIP_ALPHA supported, no COMPOSE */

-

-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED

-   /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA

-    * settings will have no effect.

-    */

-   if (!png_gamma_significant(png_ptr->screen_gamma))

-   {

-      png_ptr->transformations &= ~PNG_ENCODE_ALPHA;

-      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;

-   }

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-   /* Make sure the coefficients for the rgb to gray conversion are set

-    * appropriately.

-    */

-   if (png_ptr->transformations & PNG_RGB_TO_GRAY)

-      png_colorspace_set_rgb_coefficients(png_ptr);

-#endif

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)

-   /* Detect gray background and attempt to enable optimization for

-    * gray --> RGB case.

-    *

-    * Note:  if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or

-    * RGB_ALPHA (in which case need_expand is superfluous anyway), the

-    * background color might actually be gray yet not be flagged as such.

-    * This is not a problem for the current code, which uses

-    * PNG_BACKGROUND_IS_GRAY only to decide when to do the

-    * png_do_gray_to_rgb() transformation.

-    *

-    * TODO: this code needs to be revised to avoid the complexity and

-    * interdependencies.  The color type of the background should be recorded in

-    * png_set_background, along with the bit depth, then the code has a record

-    * of exactly what color space the background is currently in.

-    */

-   if (png_ptr->transformations & PNG_BACKGROUND_EXPAND)

-   {

-      /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if

-       * the file was grayscale the background value is gray.

-       */

-      if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))

-         png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;

-   }

-

-   else if (png_ptr->transformations & PNG_COMPOSE)

-   {

-      /* PNG_COMPOSE: png_set_background was called with need_expand false,

-       * so the color is in the color space of the output or png_set_alpha_mode

-       * was called and the color is black.  Ignore RGB_TO_GRAY because that

-       * happens before GRAY_TO_RGB.

-       */

-      if (png_ptr->transformations & PNG_GRAY_TO_RGB)

-      {

-         if (png_ptr->background.red == png_ptr->background.green &&

-             png_ptr->background.red == png_ptr->background.blue)

-         {

-            png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;

-            png_ptr->background.gray = png_ptr->background.red;

-         }

-      }

-   }

-#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */

-#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */

-

-   /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations

-    * can be performed directly on the palette, and some (such as rgb to gray)

-    * can be optimized inside the palette.  This is particularly true of the

-    * composite (background and alpha) stuff, which can be pretty much all done

-    * in the palette even if the result is expanded to RGB or gray afterward.

-    *

-    * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and

-    * earlier and the palette stuff is actually handled on the first row.  This

-    * leads to the reported bug that the palette returned by png_get_PLTE is not

-    * updated.

-    */

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      png_init_palette_transformations(png_ptr);

-

-   else

-      png_init_rgb_transformations(png_ptr);

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \

-   defined(PNG_READ_EXPAND_16_SUPPORTED)

-   if ((png_ptr->transformations & PNG_EXPAND_16) &&

-      (png_ptr->transformations & PNG_COMPOSE) &&

-      !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&

-      png_ptr->bit_depth != 16)

-   {

-      /* TODO: fix this.  Because the expand_16 operation is after the compose

-       * handling the background color must be 8, not 16, bits deep, but the

-       * application will supply a 16-bit value so reduce it here.

-       *

-       * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at

-       * present, so that case is ok (until do_expand_16 is moved.)

-       *

-       * NOTE: this discards the low 16 bits of the user supplied background

-       * color, but until expand_16 works properly there is no choice!

-       */

-#     define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x))

-      CHOP(png_ptr->background.red);

-      CHOP(png_ptr->background.green);

-      CHOP(png_ptr->background.blue);

-      CHOP(png_ptr->background.gray);

-#     undef CHOP

-   }

-#endif /* PNG_READ_BACKGROUND_SUPPORTED && PNG_READ_EXPAND_16_SUPPORTED */

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \

-   (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \

-   defined(PNG_READ_STRIP_16_TO_8_SUPPORTED))

-   if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) &&

-      (png_ptr->transformations & PNG_COMPOSE) &&

-      !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&

-      png_ptr->bit_depth == 16)

-   {

-      /* On the other hand, if a 16-bit file is to be reduced to 8-bits per

-       * component this will also happen after PNG_COMPOSE and so the background

-       * color must be pre-expanded here.

-       *

-       * TODO: fix this too.

-       */

-      png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257);

-      png_ptr->background.green =

-         (png_uint_16)(png_ptr->background.green * 257);

-      png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257);

-      png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257);

-   }

-#endif

-

-   /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the

-    * background support (see the comments in scripts/pnglibconf.dfa), this

-    * allows pre-multiplication of the alpha channel to be implemented as

-    * compositing on black.  This is probably sub-optimal and has been done in

-    * 1.5.4 betas simply to enable external critique and testing (i.e. to

-    * implement the new API quickly, without lots of internal changes.)

-    */

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-#  ifdef PNG_READ_BACKGROUND_SUPPORTED

-      /* Includes ALPHA_MODE */

-      png_ptr->background_1 = png_ptr->background;

-#  endif

-

-   /* This needs to change - in the palette image case a whole set of tables are

-    * built when it would be quicker to just calculate the correct value for

-    * each palette entry directly.  Also, the test is too tricky - why check

-    * PNG_RGB_TO_GRAY if PNG_GAMMA is not set?  The answer seems to be that

-    * PNG_GAMMA is cancelled even if the gamma is known?  The test excludes the

-    * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction

-    * the gamma tables will not be built even if composition is required on a

-    * gamma encoded value.

-    *

-    * In 1.5.4 this is addressed below by an additional check on the individual

-    * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the

-    * tables.

-    */

-   if ((png_ptr->transformations & PNG_GAMMA)

-      || ((png_ptr->transformations & PNG_RGB_TO_GRAY)

-         && (png_gamma_significant(png_ptr->colorspace.gamma) ||

-            png_gamma_significant(png_ptr->screen_gamma)))

-      || ((png_ptr->transformations & PNG_COMPOSE)

-         && (png_gamma_significant(png_ptr->colorspace.gamma)

-            || png_gamma_significant(png_ptr->screen_gamma)

-#  ifdef PNG_READ_BACKGROUND_SUPPORTED

-            || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE

-               && png_gamma_significant(png_ptr->background_gamma))

-#  endif

-      )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA)

-         && png_gamma_significant(png_ptr->screen_gamma))

-      )

-   {

-      png_build_gamma_table(png_ptr, png_ptr->bit_depth);

-

-#ifdef PNG_READ_BACKGROUND_SUPPORTED

-      if (png_ptr->transformations & PNG_COMPOSE)

-      {

-         /* Issue a warning about this combination: because RGB_TO_GRAY is

-          * optimized to do the gamma transform if present yet do_background has

-          * to do the same thing if both options are set a

-          * double-gamma-correction happens.  This is true in all versions of

-          * libpng to date.

-          */

-         if (png_ptr->transformations & PNG_RGB_TO_GRAY)

-            png_warning(png_ptr,

-               "libpng does not support gamma+background+rgb_to_gray");

-

-         if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-         {

-            /* We don't get to here unless there is a tRNS chunk with non-opaque

-             * entries - see the checking code at the start of this function.

-             */

-            png_color back, back_1;

-            png_colorp palette = png_ptr->palette;

-            int num_palette = png_ptr->num_palette;

-            int i;

-            if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)

-            {

-

-               back.red = png_ptr->gamma_table[png_ptr->background.red];

-               back.green = png_ptr->gamma_table[png_ptr->background.green];

-               back.blue = png_ptr->gamma_table[png_ptr->background.blue];

-

-               back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];

-               back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];

-               back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];

-            }

-            else

-            {

-               png_fixed_point g, gs;

-

-               switch (png_ptr->background_gamma_type)

-               {

-                  case PNG_BACKGROUND_GAMMA_SCREEN:

-                     g = (png_ptr->screen_gamma);

-                     gs = PNG_FP_1;

-                     break;

-

-                  case PNG_BACKGROUND_GAMMA_FILE:

-                     g = png_reciprocal(png_ptr->colorspace.gamma);

-                     gs = png_reciprocal2(png_ptr->colorspace.gamma,

-                        png_ptr->screen_gamma);

-                     break;

-

-                  case PNG_BACKGROUND_GAMMA_UNIQUE:

-                     g = png_reciprocal(png_ptr->background_gamma);

-                     gs = png_reciprocal2(png_ptr->background_gamma,

-                        png_ptr->screen_gamma);

-                     break;

-                  default:

-                     g = PNG_FP_1;    /* back_1 */

-                     gs = PNG_FP_1;   /* back */

-                     break;

-               }

-

-               if (png_gamma_significant(gs))

-               {

-                  back.red = png_gamma_8bit_correct(png_ptr->background.red,

-                      gs);

-                  back.green = png_gamma_8bit_correct(png_ptr->background.green,

-                      gs);

-                  back.blue = png_gamma_8bit_correct(png_ptr->background.blue,

-                      gs);

-               }

-

-               else

-               {

-                  back.red   = (png_byte)png_ptr->background.red;

-                  back.green = (png_byte)png_ptr->background.green;

-                  back.blue  = (png_byte)png_ptr->background.blue;

-               }

-

-               if (png_gamma_significant(g))

-               {

-                  back_1.red = png_gamma_8bit_correct(png_ptr->background.red,

-                     g);

-                  back_1.green = png_gamma_8bit_correct(

-                     png_ptr->background.green, g);

-                  back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,

-                     g);

-               }

-

-               else

-               {

-                  back_1.red   = (png_byte)png_ptr->background.red;

-                  back_1.green = (png_byte)png_ptr->background.green;

-                  back_1.blue  = (png_byte)png_ptr->background.blue;

-               }

-            }

-

-            for (i = 0; i < num_palette; i++)

-            {

-               if (i < (int)png_ptr->num_trans &&

-                   png_ptr->trans_alpha[i] != 0xff)

-               {

-                  if (png_ptr->trans_alpha[i] == 0)

-                  {

-                     palette[i] = back;

-                  }

-                  else /* if (png_ptr->trans_alpha[i] != 0xff) */

-                  {

-                     png_byte v, w;

-

-                     v = png_ptr->gamma_to_1[palette[i].red];

-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.red);

-                     palette[i].red = png_ptr->gamma_from_1[w];

-

-                     v = png_ptr->gamma_to_1[palette[i].green];

-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.green);

-                     palette[i].green = png_ptr->gamma_from_1[w];

-

-                     v = png_ptr->gamma_to_1[palette[i].blue];

-                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue);

-                     palette[i].blue = png_ptr->gamma_from_1[w];

-                  }

-               }

-               else

-               {

-                  palette[i].red = png_ptr->gamma_table[palette[i].red];

-                  palette[i].green = png_ptr->gamma_table[palette[i].green];

-                  palette[i].blue = png_ptr->gamma_table[palette[i].blue];

-               }

-            }

-

-            /* Prevent the transformations being done again.

-             *

-             * NOTE: this is highly dubious; it removes the transformations in

-             * place.  This seems inconsistent with the general treatment of the

-             * transformations elsewhere.

-             */

-            png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);

-         } /* color_type == PNG_COLOR_TYPE_PALETTE */

-

-         /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */

-         else /* color_type != PNG_COLOR_TYPE_PALETTE */

-         {

-            int gs_sig, g_sig;

-            png_fixed_point g = PNG_FP_1;  /* Correction to linear */

-            png_fixed_point gs = PNG_FP_1; /* Correction to screen */

-

-            switch (png_ptr->background_gamma_type)

-            {

-               case PNG_BACKGROUND_GAMMA_SCREEN:

-                  g = png_ptr->screen_gamma;

-                  /* gs = PNG_FP_1; */

-                  break;

-

-               case PNG_BACKGROUND_GAMMA_FILE:

-                  g = png_reciprocal(png_ptr->colorspace.gamma);

-                  gs = png_reciprocal2(png_ptr->colorspace.gamma,

-                     png_ptr->screen_gamma);

-                  break;

-

-               case PNG_BACKGROUND_GAMMA_UNIQUE:

-                  g = png_reciprocal(png_ptr->background_gamma);

-                  gs = png_reciprocal2(png_ptr->background_gamma,

-                      png_ptr->screen_gamma);

-                  break;

-

-               default:

-                  png_error(png_ptr, "invalid background gamma type");

-            }

-

-            g_sig = png_gamma_significant(g);

-            gs_sig = png_gamma_significant(gs);

-

-            if (g_sig)

-               png_ptr->background_1.gray = png_gamma_correct(png_ptr,

-                   png_ptr->background.gray, g);

-

-            if (gs_sig)

-               png_ptr->background.gray = png_gamma_correct(png_ptr,

-                   png_ptr->background.gray, gs);

-

-            if ((png_ptr->background.red != png_ptr->background.green) ||

-                (png_ptr->background.red != png_ptr->background.blue) ||

-                (png_ptr->background.red != png_ptr->background.gray))

-            {

-               /* RGB or RGBA with color background */

-               if (g_sig)

-               {

-                  png_ptr->background_1.red = png_gamma_correct(png_ptr,

-                      png_ptr->background.red, g);

-

-                  png_ptr->background_1.green = png_gamma_correct(png_ptr,

-                      png_ptr->background.green, g);

-

-                  png_ptr->background_1.blue = png_gamma_correct(png_ptr,

-                      png_ptr->background.blue, g);

-               }

-

-               if (gs_sig)

-               {

-                  png_ptr->background.red = png_gamma_correct(png_ptr,

-                      png_ptr->background.red, gs);

-

-                  png_ptr->background.green = png_gamma_correct(png_ptr,

-                      png_ptr->background.green, gs);

-

-                  png_ptr->background.blue = png_gamma_correct(png_ptr,

-                      png_ptr->background.blue, gs);

-               }

-            }

-

-            else

-            {

-               /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */

-               png_ptr->background_1.red = png_ptr->background_1.green

-                   = png_ptr->background_1.blue = png_ptr->background_1.gray;

-

-               png_ptr->background.red = png_ptr->background.green

-                   = png_ptr->background.blue = png_ptr->background.gray;

-            }

-

-            /* The background is now in screen gamma: */

-            png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN;

-         } /* color_type != PNG_COLOR_TYPE_PALETTE */

-      }/* png_ptr->transformations & PNG_BACKGROUND */

-

-      else

-      /* Transformation does not include PNG_BACKGROUND */

-#endif /* PNG_READ_BACKGROUND_SUPPORTED */

-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-         /* RGB_TO_GRAY needs to have non-gamma-corrected values! */

-         && ((png_ptr->transformations & PNG_EXPAND) == 0 ||

-         (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)

-#endif

-         )

-      {

-         png_colorp palette = png_ptr->palette;

-         int num_palette = png_ptr->num_palette;

-         int i;

-

-         /* NOTE: there are other transformations that should probably be in

-          * here too.

-          */

-         for (i = 0; i < num_palette; i++)

-         {

-            palette[i].red = png_ptr->gamma_table[palette[i].red];

-            palette[i].green = png_ptr->gamma_table[palette[i].green];

-            palette[i].blue = png_ptr->gamma_table[palette[i].blue];

-         }

-

-         /* Done the gamma correction. */

-         png_ptr->transformations &= ~PNG_GAMMA;

-      } /* color_type == PALETTE && !PNG_BACKGROUND transformation */

-   }

-#ifdef PNG_READ_BACKGROUND_SUPPORTED

-   else

-#endif

-#endif /* PNG_READ_GAMMA_SUPPORTED */

-

-#ifdef PNG_READ_BACKGROUND_SUPPORTED

-   /* No GAMMA transformation (see the hanging else 4 lines above) */

-   if ((png_ptr->transformations & PNG_COMPOSE) &&

-       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))

-   {

-      int i;

-      int istop = (int)png_ptr->num_trans;

-      png_color back;

-      png_colorp palette = png_ptr->palette;

-

-      back.red   = (png_byte)png_ptr->background.red;

-      back.green = (png_byte)png_ptr->background.green;

-      back.blue  = (png_byte)png_ptr->background.blue;

-

-      for (i = 0; i < istop; i++)

-      {

-         if (png_ptr->trans_alpha[i] == 0)

-         {

-            palette[i] = back;

-         }

-

-         else if (png_ptr->trans_alpha[i] != 0xff)

-         {

-            /* The png_composite() macro is defined in png.h */

-            png_composite(palette[i].red, palette[i].red,

-                png_ptr->trans_alpha[i], back.red);

-

-            png_composite(palette[i].green, palette[i].green,

-                png_ptr->trans_alpha[i], back.green);

-

-            png_composite(palette[i].blue, palette[i].blue,

-                png_ptr->trans_alpha[i], back.blue);

-         }

-      }

-

-      png_ptr->transformations &= ~PNG_COMPOSE;

-	  

-  	  //png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; /* Add by Sunliang.Liu */

-   }

-#endif /* PNG_READ_BACKGROUND_SUPPORTED */

-

-#ifdef PNG_READ_SHIFT_SUPPORTED

-   if ((png_ptr->transformations & PNG_SHIFT) &&

-      !(png_ptr->transformations & PNG_EXPAND) &&

-       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))

-   {

-      int i;

-      int istop = png_ptr->num_palette;

-      int shift = 8 - png_ptr->sig_bit.red;

-

-      png_ptr->transformations &= ~PNG_SHIFT;

-

-      /* significant bits can be in the range 1 to 7 for a meaninful result, if

-       * the number of significant bits is 0 then no shift is done (this is an

-       * error condition which is silently ignored.)

-       */

-      if (shift > 0 && shift < 8) for (i=0; i<istop; ++i)

-      {

-         int component = png_ptr->palette[i].red;

-

-         component >>= shift;

-         png_ptr->palette[i].red = (png_byte)component;

-      }

-

-      shift = 8 - png_ptr->sig_bit.green;

-      if (shift > 0 && shift < 8) for (i=0; i<istop; ++i)

-      {

-         int component = png_ptr->palette[i].green;

-

-         component >>= shift;

-         png_ptr->palette[i].green = (png_byte)component;

-      }

-

-      shift = 8 - png_ptr->sig_bit.blue;

-      if (shift > 0 && shift < 8) for (i=0; i<istop; ++i)

-      {

-         int component = png_ptr->palette[i].blue;

-

-         component >>= shift;

-         png_ptr->palette[i].blue = (png_byte)component;

-      }

-   }

-#endif  /* PNG_READ_SHIFT_SUPPORTED */

-}

-

-/* Modify the info structure to reflect the transformations.  The

- * info should be updated so a PNG file could be written with it,

- * assuming the transformations result in valid PNG data.

- */

-void /* PRIVATE */

-png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)

-{

-   png_debug(1, "in png_read_transform_info");

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-   if (png_ptr->transformations & PNG_EXPAND)

-   {

-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         /* This check must match what actually happens in

-          * png_do_expand_palette; if it ever checks the tRNS chunk to see if

-          * it is all opaque we must do the same (at present it does not.)

-          */

-         if (png_ptr->num_trans > 0)

-            info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;

-

-         else

-            info_ptr->color_type = PNG_COLOR_TYPE_RGB;

-

-         info_ptr->bit_depth = 8;

-         info_ptr->num_trans = 0;

-      }

-      else

-      {

-         if (png_ptr->num_trans)

-         {

-            if (png_ptr->transformations & PNG_EXPAND_tRNS)

-               info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;

-         }

-         if (info_ptr->bit_depth < 8)

-            info_ptr->bit_depth = 8;

-

-         info_ptr->num_trans = 0;

-      }

-   }

-#endif

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)

-   /* The following is almost certainly wrong unless the background value is in

-    * the screen space!

-    */

-   if (png_ptr->transformations & PNG_COMPOSE)

-      info_ptr->background = png_ptr->background;

-#endif

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4),

-    * however it seems that the code in png_init_read_transformations, which has

-    * been called before this from png_read_update_info->png_read_start_row

-    * sometimes does the gamma transform and cancels the flag.

-    *

-    * TODO: this looks wrong; the info_ptr should end up with a gamma equal to

-    * the screen_gamma value.  The following probably results in weirdness if

-    * the info_ptr is used by the app after the rows have been read.

-    */

-   info_ptr->colorspace.gamma = png_ptr->colorspace.gamma;

-#endif

-

-   if (info_ptr->bit_depth == 16)

-   {

-#  ifdef PNG_READ_16BIT_SUPPORTED

-#     ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-         if (png_ptr->transformations & PNG_SCALE_16_TO_8)

-            info_ptr->bit_depth = 8;

-#     endif

-

-#     ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-         if (png_ptr->transformations & PNG_16_TO_8)

-            info_ptr->bit_depth = 8;

-#     endif

-

-#  else

-      /* No 16 bit support: force chopping 16-bit input down to 8, in this case

-       * the app program can chose if both APIs are available by setting the

-       * correct scaling to use.

-       */

-#     ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-         /* For compatibility with previous versions use the strip method by

-          * default.  This code works because if PNG_SCALE_16_TO_8 is already

-          * set the code below will do that in preference to the chop.

-          */

-         png_ptr->transformations |= PNG_16_TO_8;

-         info_ptr->bit_depth = 8;

-#     else

-

-#        ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-            png_ptr->transformations |= PNG_SCALE_16_TO_8;

-            info_ptr->bit_depth = 8;

-#        else

-

-            CONFIGURATION ERROR: you must enable at least one 16 to 8 method

-#        endif

-#    endif

-#endif /* !READ_16BIT_SUPPORTED */

-   }

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-   if (png_ptr->transformations & PNG_GRAY_TO_RGB)

-      info_ptr->color_type = (png_byte)(info_ptr->color_type |

-         PNG_COLOR_MASK_COLOR);

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-   if (png_ptr->transformations & PNG_RGB_TO_GRAY)

-      info_ptr->color_type = (png_byte)(info_ptr->color_type &

-         ~PNG_COLOR_MASK_COLOR);

-#endif

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-   if (png_ptr->transformations & PNG_QUANTIZE)

-   {

-      if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||

-          (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&

-          png_ptr->palette_lookup && info_ptr->bit_depth == 8)

-      {

-         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;

-      }

-   }

-#endif

-

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-   if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 &&

-      info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)

-   {

-      info_ptr->bit_depth = 16;

-   }

-#endif

-

-#ifdef PNG_READ_PACK_SUPPORTED

-   if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))

-      info_ptr->bit_depth = 8;

-#endif

-

-   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      info_ptr->channels = 1;

-

-   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)

-      info_ptr->channels = 3;

-

-   else

-      info_ptr->channels = 1;

-

-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED

-   if (png_ptr->transformations & PNG_STRIP_ALPHA)

-   {

-      info_ptr->color_type = (png_byte)(info_ptr->color_type &

-         ~PNG_COLOR_MASK_ALPHA);

-      info_ptr->num_trans = 0;

-   }

-#endif

-

-   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)

-      info_ptr->channels++;

-

-#ifdef PNG_READ_FILLER_SUPPORTED

-   /* STRIP_ALPHA and FILLER allowed:  MASK_ALPHA bit stripped above */

-   if ((png_ptr->transformations & PNG_FILLER) &&

-       ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||

-       (info_ptr->color_type == PNG_COLOR_TYPE_GRAY)))

-   {

-      info_ptr->channels++;

-      /* If adding a true alpha channel not just filler */

-      if (png_ptr->transformations & PNG_ADD_ALPHA)

-         info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;

-   }

-#endif

-

-#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \

-defined(PNG_READ_USER_TRANSFORM_SUPPORTED)

-   if (png_ptr->transformations & PNG_USER_TRANSFORM)

-   {

-      if (info_ptr->bit_depth < png_ptr->user_transform_depth)

-         info_ptr->bit_depth = png_ptr->user_transform_depth;

-

-      if (info_ptr->channels < png_ptr->user_transform_channels)

-         info_ptr->channels = png_ptr->user_transform_channels;

-   }

-#endif

-

-   info_ptr->pixel_depth = (png_byte)(info_ptr->channels *

-       info_ptr->bit_depth);

-

-   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width);

-

-   /* Adding in 1.5.4: cache the above value in png_struct so that we can later

-    * check in png_rowbytes that the user buffer won't get overwritten.  Note

-    * that the field is not always set - if png_read_update_info isn't called

-    * the application has to either not do any transforms or get the calculation

-    * right itself.

-    */

-   png_ptr->info_rowbytes = info_ptr->rowbytes;

-

-#ifndef PNG_READ_EXPAND_SUPPORTED

-   if (png_ptr)

-      return;

-#endif

-}

-

-/* Transform the row.  The order of transformations is significant,

- * and is very touchy.  If you add a transformation, take care to

- * decide how it fits in with the other transformations here.

- */

-void /* PRIVATE */

-png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info)

-{

-   png_debug(1, "in png_do_read_transformations");

-

-   if (png_ptr->row_buf == NULL)

-   {

-      /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this

-       * error is incredibly rare and incredibly easy to debug without this

-       * information.

-       */

-      png_error(png_ptr, "NULL row buffer");

-   }

-

-   /* The following is debugging; prior to 1.5.4 the code was never compiled in;

-    * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro

-    * PNG_WARN_UNINITIALIZED_ROW removed.  In 1.6 the new flag is set only for

-    * all transformations, however in practice the ROW_INIT always gets done on

-    * demand, if necessary.

-    */

-   if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 &&

-      !(png_ptr->flags & PNG_FLAG_ROW_INIT))

-   {

-      /* Application has failed to call either png_read_start_image() or

-       * png_read_update_info() after setting transforms that expand pixels.

-       * This check added to libpng-1.2.19 (but not enabled until 1.5.4).

-       */

-      png_error(png_ptr, "Uninitialized row");

-   }

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-   if (png_ptr->transformations & PNG_EXPAND)

-   {

-      if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         png_do_expand_palette(row_info, png_ptr->row_buf + 1,

-             png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans);

-      }

-

-      else

-      {

-         if (png_ptr->num_trans &&

-             (png_ptr->transformations & PNG_EXPAND_tRNS))

-            png_do_expand(row_info, png_ptr->row_buf + 1,

-                &(png_ptr->trans_color));

-

-         else

-            png_do_expand(row_info, png_ptr->row_buf + 1,

-                NULL);

-      }

-   }

-#endif

-

-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED

-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&

-      !(png_ptr->transformations & PNG_COMPOSE) &&

-      (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||

-      row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))

-      png_do_strip_channel(row_info, png_ptr->row_buf + 1,

-         0 /* at_start == false, because SWAP_ALPHA happens later */);

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-   if (png_ptr->transformations & PNG_RGB_TO_GRAY)

-   {

-      int rgb_error =

-          png_do_rgb_to_gray(png_ptr, row_info,

-              png_ptr->row_buf + 1);

-

-      if (rgb_error)

-      {

-         png_ptr->rgb_to_gray_status=1;

-         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==

-             PNG_RGB_TO_GRAY_WARN)

-            png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");

-

-         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==

-             PNG_RGB_TO_GRAY_ERR)

-            png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");

-      }

-   }

-#endif

-

-/* From Andreas Dilger e-mail to png-implement, 26 March 1998:

- *

- *   In most cases, the "simple transparency" should be done prior to doing

- *   gray-to-RGB, or you will have to test 3x as many bytes to check if a

- *   pixel is transparent.  You would also need to make sure that the

- *   transparency information is upgraded to RGB.

- *

- *   To summarize, the current flow is:

- *   - Gray + simple transparency -> compare 1 or 2 gray bytes and composite

- *                                   with background "in place" if transparent,

- *                                   convert to RGB if necessary

- *   - Gray + alpha -> composite with gray background and remove alpha bytes,

- *                                   convert to RGB if necessary

- *

- *   To support RGB backgrounds for gray images we need:

- *   - Gray + simple transparency -> convert to RGB + simple transparency,

- *                                   compare 3 or 6 bytes and composite with

- *                                   background "in place" if transparent

- *                                   (3x compare/pixel compared to doing

- *                                   composite with gray bkgrnd)

- *   - Gray + alpha -> convert to RGB + alpha, composite with background and

- *                                   remove alpha bytes (3x float

- *                                   operations/pixel compared with composite

- *                                   on gray background)

- *

- *  Greg's change will do this.  The reason it wasn't done before is for

- *  performance, as this increases the per-pixel operations.  If we would check

- *  in advance if the background was gray or RGB, and position the gray-to-RGB

- *  transform appropriately, then it would save a lot of work/time.

- */

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-   /* If gray -> RGB, do so now only if background is non-gray; else do later

-    * for performance reasons

-    */

-   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&

-       !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY))

-      png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);

-#endif

-

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)

-   if (png_ptr->transformations & PNG_COMPOSE)

-      png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr);

-#endif

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   if ((png_ptr->transformations & PNG_GAMMA) &&

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-      /* Because RGB_TO_GRAY does the gamma transform. */

-      !(png_ptr->transformations & PNG_RGB_TO_GRAY) &&

-#endif

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)

-      /* Because PNG_COMPOSE does the gamma transform if there is something to

-       * do (if there is an alpha channel or transparency.)

-       */

-       !((png_ptr->transformations & PNG_COMPOSE) &&

-       ((png_ptr->num_trans != 0) ||

-       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&

-#endif

-      /* Because png_init_read_transformations transforms the palette, unless

-       * RGB_TO_GRAY will do the transform.

-       */

-       (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))

-      png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr);

-#endif

-

-#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED

-   if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&

-      (png_ptr->transformations & PNG_COMPOSE) &&

-      (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||

-      row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))

-      png_do_strip_channel(row_info, png_ptr->row_buf + 1,

-         0 /* at_start == false, because SWAP_ALPHA happens later */);

-#endif

-

-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED

-   if ((png_ptr->transformations & PNG_ENCODE_ALPHA) &&

-      (row_info->color_type & PNG_COLOR_MASK_ALPHA))

-      png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr);

-#endif

-

-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-   if (png_ptr->transformations & PNG_SCALE_16_TO_8)

-      png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-   /* There is no harm in doing both of these because only one has any effect,

-    * by putting the 'scale' option first if the app asks for scale (either by

-    * calling the API or in a TRANSFORM flag) this is what happens.

-    */

-   if (png_ptr->transformations & PNG_16_TO_8)

-      png_do_chop(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-   if (png_ptr->transformations & PNG_QUANTIZE)

-   {

-      png_do_quantize(row_info, png_ptr->row_buf + 1,

-          png_ptr->palette_lookup, png_ptr->quantize_index);

-

-      if (row_info->rowbytes == 0)

-         png_error(png_ptr, "png_do_quantize returned rowbytes=0");

-   }

-#endif /* PNG_READ_QUANTIZE_SUPPORTED */

-

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-   /* Do the expansion now, after all the arithmetic has been done.  Notice

-    * that previous transformations can handle the PNG_EXPAND_16 flag if this

-    * is efficient (particularly true in the case of gamma correction, where

-    * better accuracy results faster!)

-    */

-   if (png_ptr->transformations & PNG_EXPAND_16)

-      png_do_expand_16(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-   /* NOTE: moved here in 1.5.4 (from much later in this list.) */

-   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&

-       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))

-      png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_INVERT_SUPPORTED

-   if (png_ptr->transformations & PNG_INVERT_MONO)

-      png_do_invert(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_SHIFT_SUPPORTED

-   if (png_ptr->transformations & PNG_SHIFT)

-      png_do_unshift(row_info, png_ptr->row_buf + 1,

-          &(png_ptr->shift));

-#endif

-

-#ifdef PNG_READ_PACK_SUPPORTED

-   if (png_ptr->transformations & PNG_PACK)

-      png_do_unpack(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED

-   /* Added at libpng-1.5.10 */

-   if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&

-       png_ptr->num_palette_max >= 0)

-      png_do_check_palette_indexes(png_ptr, row_info);

-#endif

-

-#ifdef PNG_READ_BGR_SUPPORTED

-   if (png_ptr->transformations & PNG_BGR)

-      png_do_bgr(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_PACKSWAP_SUPPORTED

-   if (png_ptr->transformations & PNG_PACKSWAP)

-      png_do_packswap(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_FILLER_SUPPORTED

-   if (png_ptr->transformations & PNG_FILLER)

-      png_do_read_filler(row_info, png_ptr->row_buf + 1,

-          (png_uint_32)png_ptr->filler, png_ptr->flags);

-#endif

-

-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED

-   if (png_ptr->transformations & PNG_INVERT_ALPHA)

-      png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED

-   if (png_ptr->transformations & PNG_SWAP_ALPHA)

-      png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-#ifdef PNG_READ_SWAP_SUPPORTED

-   if (png_ptr->transformations & PNG_SWAP_BYTES)

-      png_do_swap(row_info, png_ptr->row_buf + 1);

-#endif

-#endif

-

-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED

-   if (png_ptr->transformations & PNG_USER_TRANSFORM)

-    {

-      if (png_ptr->read_user_transform_fn != NULL)

-         (*(png_ptr->read_user_transform_fn)) /* User read transform function */

-             (png_ptr,     /* png_ptr */

-             row_info,     /* row_info: */

-                /*  png_uint_32 width;       width of row */

-                /*  png_size_t rowbytes;     number of bytes in row */

-                /*  png_byte color_type;     color type of pixels */

-                /*  png_byte bit_depth;      bit depth of samples */

-                /*  png_byte channels;       number of channels (1-4) */

-                /*  png_byte pixel_depth;    bits per pixel (depth*channels) */

-             png_ptr->row_buf + 1);    /* start of pixel data for row */

-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED

-      if (png_ptr->user_transform_depth)

-         row_info->bit_depth = png_ptr->user_transform_depth;

-

-      if (png_ptr->user_transform_channels)

-         row_info->channels = png_ptr->user_transform_channels;

-#endif

-      row_info->pixel_depth = (png_byte)(row_info->bit_depth *

-          row_info->channels);

-

-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width);

-   }

-#endif

-}

-

-#ifdef PNG_READ_PACK_SUPPORTED

-/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,

- * without changing the actual values.  Thus, if you had a row with

- * a bit depth of 1, you would end up with bytes that only contained

- * the numbers 0 or 1.  If you would rather they contain 0 and 255, use

- * png_do_shift() after this.

- */

-void /* PRIVATE */

-png_do_unpack(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_unpack");

-

-   if (row_info->bit_depth < 8)

-   {

-      png_uint_32 i;

-      png_uint_32 row_width=row_info->width;

-

-      switch (row_info->bit_depth)

-      {

-         case 1:

-         {

-            png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);

-            png_bytep dp = row + (png_size_t)row_width - 1;

-            png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);

-            for (i = 0; i < row_width; i++)

-            {

-               *dp = (png_byte)((*sp >> shift) & 0x01);

-

-               if (shift == 7)

-               {

-                  shift = 0;

-                  sp--;

-               }

-

-               else

-                  shift++;

-

-               dp--;

-            }

-            break;

-         }

-

-         case 2:

-         {

-

-            png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);

-            png_bytep dp = row + (png_size_t)row_width - 1;

-            png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);

-            for (i = 0; i < row_width; i++)

-            {

-               *dp = (png_byte)((*sp >> shift) & 0x03);

-

-               if (shift == 6)

-               {

-                  shift = 0;

-                  sp--;

-               }

-

-               else

-                  shift += 2;

-

-               dp--;

-            }

-            break;

-         }

-

-         case 4:

-         {

-            png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);

-            png_bytep dp = row + (png_size_t)row_width - 1;

-            png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);

-            for (i = 0; i < row_width; i++)

-            {

-               *dp = (png_byte)((*sp >> shift) & 0x0f);

-

-               if (shift == 4)

-               {

-                  shift = 0;

-                  sp--;

-               }

-

-               else

-                  shift = 4;

-

-               dp--;

-            }

-            break;

-         }

-

-         default:

-            break;

-      }

-      row_info->bit_depth = 8;

-      row_info->pixel_depth = (png_byte)(8 * row_info->channels);

-      row_info->rowbytes = row_width * row_info->channels;

-   }

-}

-#endif

-

-#ifdef PNG_READ_SHIFT_SUPPORTED

-/* Reverse the effects of png_do_shift.  This routine merely shifts the

- * pixels back to their significant bits values.  Thus, if you have

- * a row of bit depth 8, but only 5 are significant, this will shift

- * the values back to 0 through 31.

- */

-void /* PRIVATE */

-png_do_unshift(png_row_infop row_info, png_bytep row,

-    png_const_color_8p sig_bits)

-{

-   int color_type;

-

-   png_debug(1, "in png_do_unshift");

-

-   /* The palette case has already been handled in the _init routine. */

-   color_type = row_info->color_type;

-

-   if (color_type != PNG_COLOR_TYPE_PALETTE)

-   {

-      int shift[4];

-      int channels = 0;

-      int bit_depth = row_info->bit_depth;

-

-      if (color_type & PNG_COLOR_MASK_COLOR)

-      {

-         shift[channels++] = bit_depth - sig_bits->red;

-         shift[channels++] = bit_depth - sig_bits->green;

-         shift[channels++] = bit_depth - sig_bits->blue;

-      }

-

-      else

-      {

-         shift[channels++] = bit_depth - sig_bits->gray;

-      }

-

-      if (color_type & PNG_COLOR_MASK_ALPHA)

-      {

-         shift[channels++] = bit_depth - sig_bits->alpha;

-      }

-

-      {

-         int c, have_shift;

-

-         for (c = have_shift = 0; c < channels; ++c)

-         {

-            /* A shift of more than the bit depth is an error condition but it

-             * gets ignored here.

-             */

-            if (shift[c] <= 0 || shift[c] >= bit_depth)

-               shift[c] = 0;

-

-            else

-               have_shift = 1;

-         }

-

-         if (!have_shift)

-            return;

-      }

-

-      switch (bit_depth)

-      {

-         default:

-         /* Must be 1bpp gray: should not be here! */

-            /* NOTREACHED */

-            break;

-

-         case 2:

-         /* Must be 2bpp gray */

-         /* assert(channels == 1 && shift[0] == 1) */

-         {

-            png_bytep bp = row;

-            png_bytep bp_end = bp + row_info->rowbytes;

-

-            while (bp < bp_end)

-            {

-               int b = (*bp >> 1) & 0x55;

-               *bp++ = (png_byte)b;

-            }

-            break;

-         }

-

-         case 4:

-         /* Must be 4bpp gray */

-         /* assert(channels == 1) */

-         {

-            png_bytep bp = row;

-            png_bytep bp_end = bp + row_info->rowbytes;

-            int gray_shift = shift[0];

-            int mask =  0xf >> gray_shift;

-

-            mask |= mask << 4;

-

-            while (bp < bp_end)

-            {

-               int b = (*bp >> gray_shift) & mask;

-               *bp++ = (png_byte)b;

-            }

-            break;

-         }

-

-         case 8:

-         /* Single byte components, G, GA, RGB, RGBA */

-         {

-            png_bytep bp = row;

-            png_bytep bp_end = bp + row_info->rowbytes;

-            int channel = 0;

-

-            while (bp < bp_end)

-            {

-               int b = *bp >> shift[channel];

-               if (++channel >= channels)

-                  channel = 0;

-               *bp++ = (png_byte)b;

-            }

-            break;

-         }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-         case 16:

-         /* Double byte components, G, GA, RGB, RGBA */

-         {

-            png_bytep bp = row;

-            png_bytep bp_end = bp + row_info->rowbytes;

-            int channel = 0;

-

-            while (bp < bp_end)

-            {

-               int value = (bp[0] << 8) + bp[1];

-

-               value >>= shift[channel];

-               if (++channel >= channels)

-                  channel = 0;

-               *bp++ = (png_byte)(value >> 8);

-               *bp++ = (png_byte)(value & 0xff);

-            }

-            break;

-         }

-#endif

-      }

-   }

-}

-#endif

-

-#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED

-/* Scale rows of bit depth 16 down to 8 accurately */

-void /* PRIVATE */

-png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_scale_16_to_8");

-

-   if (row_info->bit_depth == 16)

-   {

-      png_bytep sp = row; /* source */

-      png_bytep dp = row; /* destination */

-      png_bytep ep = sp + row_info->rowbytes; /* end+1 */

-

-      while (sp < ep)

-      {

-         /* The input is an array of 16 bit components, these must be scaled to

-          * 8 bits each.  For a 16 bit value V the required value (from the PNG

-          * specification) is:

-          *

-          *    (V * 255) / 65535

-          *

-          * This reduces to round(V / 257), or floor((V + 128.5)/257)

-          *

-          * Represent V as the two byte value vhi.vlo.  Make a guess that the

-          * result is the top byte of V, vhi, then the correction to this value

-          * is:

-          *

-          *    error = floor(((V-vhi.vhi) + 128.5) / 257)

-          *          = floor(((vlo-vhi) + 128.5) / 257)

-          *

-          * This can be approximated using integer arithmetic (and a signed

-          * shift):

-          *

-          *    error = (vlo-vhi+128) >> 8;

-          *

-          * The approximate differs from the exact answer only when (vlo-vhi) is

-          * 128; it then gives a correction of +1 when the exact correction is

-          * 0.  This gives 128 errors.  The exact answer (correct for all 16 bit

-          * input values) is:

-          *

-          *    error = (vlo-vhi+128)*65535 >> 24;

-          *

-          * An alternative arithmetic calculation which also gives no errors is:

-          *

-          *    (V * 255 + 32895) >> 16

-          */

-

-         png_int_32 tmp = *sp++; /* must be signed! */

-         tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24;

-         *dp++ = (png_byte)tmp;

-      }

-

-      row_info->bit_depth = 8;

-      row_info->pixel_depth = (png_byte)(8 * row_info->channels);

-      row_info->rowbytes = row_info->width * row_info->channels;

-   }

-}

-#endif

-

-#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED

-void /* PRIVATE */

-/* Simply discard the low byte.  This was the default behavior prior

- * to libpng-1.5.4.

- */

-png_do_chop(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_chop");

-

-   if (row_info->bit_depth == 16)

-   {

-      png_bytep sp = row; /* source */

-      png_bytep dp = row; /* destination */

-      png_bytep ep = sp + row_info->rowbytes; /* end+1 */

-

-      while (sp < ep)

-      {

-         *dp++ = *sp;

-         sp += 2; /* skip low byte */

-      }

-

-      row_info->bit_depth = 8;

-      row_info->pixel_depth = (png_byte)(8 * row_info->channels);

-      row_info->rowbytes = row_info->width * row_info->channels;

-   }

-}

-#endif

-

-#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED

-void /* PRIVATE */

-png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_read_swap_alpha");

-

-   {

-      png_uint_32 row_width = row_info->width;

-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-      {

-         /* This converts from RGBA to ARGB */

-         if (row_info->bit_depth == 8)

-         {

-            png_bytep sp = row + row_info->rowbytes;

-            png_bytep dp = sp;

-            png_byte save;

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               save = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = save;

-            }

-         }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-         /* This converts from RRGGBBAA to AARRGGBB */

-         else

-         {

-            png_bytep sp = row + row_info->rowbytes;

-            png_bytep dp = sp;

-            png_byte save[2];

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               save[0] = *(--sp);

-               save[1] = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = save[0];

-               *(--dp) = save[1];

-            }

-         }

-#endif

-      }

-

-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-      {

-         /* This converts from GA to AG */

-         if (row_info->bit_depth == 8)

-         {

-            png_bytep sp = row + row_info->rowbytes;

-            png_bytep dp = sp;

-            png_byte save;

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               save = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = save;

-            }

-         }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-         /* This converts from GGAA to AAGG */

-         else

-         {

-            png_bytep sp = row + row_info->rowbytes;

-            png_bytep dp = sp;

-            png_byte save[2];

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               save[0] = *(--sp);

-               save[1] = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = save[0];

-               *(--dp) = save[1];

-            }

-         }

-#endif

-      }

-   }

-}

-#endif

-

-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED

-void /* PRIVATE */

-png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)

-{

-   png_uint_32 row_width;

-   png_debug(1, "in png_do_read_invert_alpha");

-

-   row_width = row_info->width;

-   if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         /* This inverts the alpha channel in RGBA */

-         png_bytep sp = row + row_info->rowbytes;

-         png_bytep dp = sp;

-         png_uint_32 i;

-

-         for (i = 0; i < row_width; i++)

-         {

-            *(--dp) = (png_byte)(255 - *(--sp));

-

-/*          This does nothing:

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            We can replace it with:

-*/

-            sp-=3;

-            dp=sp;

-         }

-      }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-      /* This inverts the alpha channel in RRGGBBAA */

-      else

-      {

-         png_bytep sp = row + row_info->rowbytes;

-         png_bytep dp = sp;

-         png_uint_32 i;

-

-         for (i = 0; i < row_width; i++)

-         {

-            *(--dp) = (png_byte)(255 - *(--sp));

-            *(--dp) = (png_byte)(255 - *(--sp));

-

-/*          This does nothing:

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-            We can replace it with:

-*/

-            sp-=6;

-            dp=sp;

-         }

-      }

-#endif

-   }

-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         /* This inverts the alpha channel in GA */

-         png_bytep sp = row + row_info->rowbytes;

-         png_bytep dp = sp;

-         png_uint_32 i;

-

-         for (i = 0; i < row_width; i++)

-         {

-            *(--dp) = (png_byte)(255 - *(--sp));

-            *(--dp) = *(--sp);

-         }

-      }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-      else

-      {

-         /* This inverts the alpha channel in GGAA */

-         png_bytep sp  = row + row_info->rowbytes;

-         png_bytep dp = sp;

-         png_uint_32 i;

-

-         for (i = 0; i < row_width; i++)

-         {

-            *(--dp) = (png_byte)(255 - *(--sp));

-            *(--dp) = (png_byte)(255 - *(--sp));

-/*

-            *(--dp) = *(--sp);

-            *(--dp) = *(--sp);

-*/

-            sp-=2;

-            dp=sp;

-         }

-      }

-#endif

-   }

-}

-#endif

-

-#ifdef PNG_READ_FILLER_SUPPORTED

-/* Add filler channel if we have RGB color */

-void /* PRIVATE */

-png_do_read_filler(png_row_infop row_info, png_bytep row,

-    png_uint_32 filler, png_uint_32 flags)

-{

-   png_uint_32 i;

-   png_uint_32 row_width = row_info->width;

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-   png_byte hi_filler = (png_byte)((filler>>8) & 0xff);

-#endif

-   png_byte lo_filler = (png_byte)(filler & 0xff);

-

-   png_debug(1, "in png_do_read_filler");

-

-   if (

-       row_info->color_type == PNG_COLOR_TYPE_GRAY)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         if (flags & PNG_FLAG_FILLER_AFTER)

-         {

-            /* This changes the data from G to GX */

-            png_bytep sp = row + (png_size_t)row_width;

-            png_bytep dp =  sp + (png_size_t)row_width;

-            for (i = 1; i < row_width; i++)

-            {

-               *(--dp) = lo_filler;

-               *(--dp) = *(--sp);

-            }

-            *(--dp) = lo_filler;

-            row_info->channels = 2;

-            row_info->pixel_depth = 16;

-            row_info->rowbytes = row_width * 2;

-         }

-

-         else

-         {

-            /* This changes the data from G to XG */

-            png_bytep sp = row + (png_size_t)row_width;

-            png_bytep dp = sp  + (png_size_t)row_width;

-            for (i = 0; i < row_width; i++)

-            {

-               *(--dp) = *(--sp);

-               *(--dp) = lo_filler;

-            }

-            row_info->channels = 2;

-            row_info->pixel_depth = 16;

-            row_info->rowbytes = row_width * 2;

-         }

-      }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-      else if (row_info->bit_depth == 16)

-      {

-         if (flags & PNG_FLAG_FILLER_AFTER)

-         {

-            /* This changes the data from GG to GGXX */

-            png_bytep sp = row + (png_size_t)row_width * 2;

-            png_bytep dp = sp  + (png_size_t)row_width * 2;

-            for (i = 1; i < row_width; i++)

-            {

-               *(--dp) = hi_filler;

-               *(--dp) = lo_filler;

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-            }

-            *(--dp) = hi_filler;

-            *(--dp) = lo_filler;

-            row_info->channels = 2;

-            row_info->pixel_depth = 32;

-            row_info->rowbytes = row_width * 4;

-         }

-

-         else

-         {

-            /* This changes the data from GG to XXGG */

-            png_bytep sp = row + (png_size_t)row_width * 2;

-            png_bytep dp = sp  + (png_size_t)row_width * 2;

-            for (i = 0; i < row_width; i++)

-            {

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = hi_filler;

-               *(--dp) = lo_filler;

-            }

-            row_info->channels = 2;

-            row_info->pixel_depth = 32;

-            row_info->rowbytes = row_width * 4;

-         }

-      }

-#endif

-   } /* COLOR_TYPE == GRAY */

-   else if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         if (flags & PNG_FLAG_FILLER_AFTER)

-         {

-            /* This changes the data from RGB to RGBX */

-            png_bytep sp = row + (png_size_t)row_width * 3;

-            png_bytep dp = sp  + (png_size_t)row_width;

-            for (i = 1; i < row_width; i++)

-            {

-               *(--dp) = lo_filler;

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-            }

-            *(--dp) = lo_filler;

-            row_info->channels = 4;

-            row_info->pixel_depth = 32;

-            row_info->rowbytes = row_width * 4;

-         }

-

-         else

-         {

-            /* This changes the data from RGB to XRGB */

-            png_bytep sp = row + (png_size_t)row_width * 3;

-            png_bytep dp = sp + (png_size_t)row_width;

-            for (i = 0; i < row_width; i++)

-            {

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = lo_filler;

-            }

-            row_info->channels = 4;

-            row_info->pixel_depth = 32;

-            row_info->rowbytes = row_width * 4;

-         }

-      }

-

-#ifdef PNG_READ_16BIT_SUPPORTED

-      else if (row_info->bit_depth == 16)

-      {

-         if (flags & PNG_FLAG_FILLER_AFTER)

-         {

-            /* This changes the data from RRGGBB to RRGGBBXX */

-            png_bytep sp = row + (png_size_t)row_width * 6;

-            png_bytep dp = sp  + (png_size_t)row_width * 2;

-            for (i = 1; i < row_width; i++)

-            {

-               *(--dp) = hi_filler;

-               *(--dp) = lo_filler;

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-            }

-            *(--dp) = hi_filler;

-            *(--dp) = lo_filler;

-            row_info->channels = 4;

-            row_info->pixel_depth = 64;

-            row_info->rowbytes = row_width * 8;

-         }

-

-         else

-         {

-            /* This changes the data from RRGGBB to XXRRGGBB */

-            png_bytep sp = row + (png_size_t)row_width * 6;

-            png_bytep dp = sp  + (png_size_t)row_width * 2;

-            for (i = 0; i < row_width; i++)

-            {

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = *(--sp);

-               *(--dp) = hi_filler;

-               *(--dp) = lo_filler;

-            }

-

-            row_info->channels = 4;

-            row_info->pixel_depth = 64;

-            row_info->rowbytes = row_width * 8;

-         }

-      }

-#endif

-   } /* COLOR_TYPE == RGB */

-}

-#endif

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-/* Expand grayscale files to RGB, with or without alpha */

-void /* PRIVATE */

-png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)

-{

-   png_uint_32 i;

-   png_uint_32 row_width = row_info->width;

-

-   png_debug(1, "in png_do_gray_to_rgb");

-

-   if (row_info->bit_depth >= 8 &&

-       !(row_info->color_type & PNG_COLOR_MASK_COLOR))

-   {

-      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            /* This changes G to RGB */

-            png_bytep sp = row + (png_size_t)row_width - 1;

-            png_bytep dp = sp  + (png_size_t)row_width * 2;

-            for (i = 0; i < row_width; i++)

-            {

-               *(dp--) = *sp;

-               *(dp--) = *sp;

-               *(dp--) = *(sp--);

-            }

-         }

-

-         else

-         {

-            /* This changes GG to RRGGBB */

-            png_bytep sp = row + (png_size_t)row_width * 2 - 1;

-            png_bytep dp = sp  + (png_size_t)row_width * 4;

-            for (i = 0; i < row_width; i++)

-            {

-               *(dp--) = *sp;

-               *(dp--) = *(sp - 1);

-               *(dp--) = *sp;

-               *(dp--) = *(sp - 1);

-               *(dp--) = *(sp--);

-               *(dp--) = *(sp--);

-            }

-         }

-      }

-

-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            /* This changes GA to RGBA */

-            png_bytep sp = row + (png_size_t)row_width * 2 - 1;

-            png_bytep dp = sp  + (png_size_t)row_width * 2;

-            for (i = 0; i < row_width; i++)

-            {

-               *(dp--) = *(sp--);

-               *(dp--) = *sp;

-               *(dp--) = *sp;

-               *(dp--) = *(sp--);

-            }

-         }

-

-         else

-         {

-            /* This changes GGAA to RRGGBBAA */

-            png_bytep sp = row + (png_size_t)row_width * 4 - 1;

-            png_bytep dp = sp  + (png_size_t)row_width * 4;

-            for (i = 0; i < row_width; i++)

-            {

-               *(dp--) = *(sp--);

-               *(dp--) = *(sp--);

-               *(dp--) = *sp;

-               *(dp--) = *(sp - 1);

-               *(dp--) = *sp;

-               *(dp--) = *(sp - 1);

-               *(dp--) = *(sp--);

-               *(dp--) = *(sp--);

-            }

-         }

-      }

-      row_info->channels = (png_byte)(row_info->channels + 2);

-      row_info->color_type |= PNG_COLOR_MASK_COLOR;

-      row_info->pixel_depth = (png_byte)(row_info->channels *

-          row_info->bit_depth);

-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);

-   }

-}

-#endif

-

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-/* Reduce RGB files to grayscale, with or without alpha

- * using the equation given in Poynton's ColorFAQ of 1998-01-04 at

- * <http://www.inforamp.net/~poynton/>  (THIS LINK IS DEAD June 2008 but

- * versions dated 1998 through November 2002 have been archived at

- * http://web.archive.org/web/20000816232553/http://www.inforamp.net/

- * ~poynton/notes/colour_and_gamma/ColorFAQ.txt )

- * Charles Poynton poynton at poynton.com

- *

- *     Y = 0.212671 * R + 0.715160 * G + 0.072169 * B

- *

- *  which can be expressed with integers as

- *

- *     Y = (6969 * R + 23434 * G + 2365 * B)/32768

- *

- * Poynton's current link (as of January 2003 through July 2011):

- * <http://www.poynton.com/notes/colour_and_gamma/>

- * has changed the numbers slightly:

- *

- *     Y = 0.2126*R + 0.7152*G + 0.0722*B

- *

- *  which can be expressed with integers as

- *

- *     Y = (6966 * R + 23436 * G + 2366 * B)/32768

- *

- *  Historically, however, libpng uses numbers derived from the ITU-R Rec 709

- *  end point chromaticities and the D65 white point.  Depending on the

- *  precision used for the D65 white point this produces a variety of different

- *  numbers, however if the four decimal place value used in ITU-R Rec 709 is

- *  used (0.3127,0.3290) the Y calculation would be:

- *

- *     Y = (6968 * R + 23435 * G + 2366 * B)/32768

- *

- *  While this is correct the rounding results in an overflow for white, because

- *  the sum of the rounded coefficients is 32769, not 32768.  Consequently

- *  libpng uses, instead, the closest non-overflowing approximation:

- *

- *     Y = (6968 * R + 23434 * G + 2366 * B)/32768

- *

- *  Starting with libpng-1.5.5, if the image being converted has a cHRM chunk

- *  (including an sRGB chunk) then the chromaticities are used to calculate the

- *  coefficients.  See the chunk handling in pngrutil.c for more information.

- *

- *  In all cases the calculation is to be done in a linear colorspace.  If no

- *  gamma information is available to correct the encoding of the original RGB

- *  values this results in an implicit assumption that the original PNG RGB

- *  values were linear.

- *

- *  Other integer coefficents can be used via png_set_rgb_to_gray().  Because

- *  the API takes just red and green coefficients the blue coefficient is

- *  calculated to make the sum 32768.  This will result in different rounding

- *  to that used above.

- */

-int /* PRIVATE */

-png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row)

-

-{

-   int rgb_error = 0;

-

-   png_debug(1, "in png_do_rgb_to_gray");

-

-   if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) &&

-       (row_info->color_type & PNG_COLOR_MASK_COLOR))

-   {

-      PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;

-      PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;

-      PNG_CONST png_uint_32 bc = 32768 - rc - gc;

-      PNG_CONST png_uint_32 row_width = row_info->width;

-      PNG_CONST int have_alpha =

-         (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;

-

-      if (row_info->bit_depth == 8)

-      {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-         /* Notice that gamma to/from 1 are not necessarily inverses (if

-          * there is an overall gamma correction).  Prior to 1.5.5 this code

-          * checked the linearized values for equality; this doesn't match

-          * the documentation, the original values must be checked.

-          */

-         if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)

-         {

-            png_bytep sp = row;

-            png_bytep dp = row;

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               png_byte red   = *(sp++);

-               png_byte green = *(sp++);

-               png_byte blue  = *(sp++);

-

-               if (red != green || red != blue)

-               {

-                  red = png_ptr->gamma_to_1[red];

-                  green = png_ptr->gamma_to_1[green];

-                  blue = png_ptr->gamma_to_1[blue];

-

-                  rgb_error |= 1;

-                  *(dp++) = png_ptr->gamma_from_1[

-                      (rc*red + gc*green + bc*blue + 16384)>>15];

-               }

-

-               else

-               {

-                  /* If there is no overall correction the table will not be

-                   * set.

-                   */

-                  if (png_ptr->gamma_table != NULL)

-                     red = png_ptr->gamma_table[red];

-

-                  *(dp++) = red;

-               }

-

-               if (have_alpha)

-                  *(dp++) = *(sp++);

-            }

-         }

-         else

-#endif

-         {

-            png_bytep sp = row;

-            png_bytep dp = row;

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               png_byte red   = *(sp++);

-               png_byte green = *(sp++);

-               png_byte blue  = *(sp++);

-

-               if (red != green || red != blue)

-               {

-                  rgb_error |= 1;

-                  /* NOTE: this is the historical approach which simply

-                   * truncates the results.

-                   */

-                  *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);

-               }

-

-               else

-                  *(dp++) = red;

-

-               if (have_alpha)

-                  *(dp++) = *(sp++);

-            }

-         }

-      }

-

-      else /* RGB bit_depth == 16 */

-      {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-         if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL)

-         {

-            png_bytep sp = row;

-            png_bytep dp = row;

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               png_uint_16 red, green, blue, w;

-

-               red   = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;

-               green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;

-               blue  = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;

-

-               if (red == green && red == blue)

-               {

-                  if (png_ptr->gamma_16_table != NULL)

-                     w = png_ptr->gamma_16_table[(red&0xff)

-                         >> png_ptr->gamma_shift][red>>8];

-

-                  else

-                     w = red;

-               }

-

-               else

-               {

-                  png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff)

-                      >> png_ptr->gamma_shift][red>>8];

-                  png_uint_16 green_1 =

-                      png_ptr->gamma_16_to_1[(green&0xff) >>

-                      png_ptr->gamma_shift][green>>8];

-                  png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff)

-                      >> png_ptr->gamma_shift][blue>>8];

-                  png_uint_16 gray16  = (png_uint_16)((rc*red_1 + gc*green_1

-                      + bc*blue_1 + 16384)>>15);

-                  w = png_ptr->gamma_16_from_1[(gray16&0xff) >>

-                      png_ptr->gamma_shift][gray16 >> 8];

-                  rgb_error |= 1;

-               }

-

-               *(dp++) = (png_byte)((w>>8) & 0xff);

-               *(dp++) = (png_byte)(w & 0xff);

-

-               if (have_alpha)

-               {

-                  *(dp++) = *(sp++);

-                  *(dp++) = *(sp++);

-               }

-            }

-         }

-         else

-#endif

-         {

-            png_bytep sp = row;

-            png_bytep dp = row;

-            png_uint_32 i;

-

-            for (i = 0; i < row_width; i++)

-            {

-               png_uint_16 red, green, blue, gray16;

-

-               red   = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;

-               green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;

-               blue  = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;

-

-               if (red != green || red != blue)

-                  rgb_error |= 1;

-

-               /* From 1.5.5 in the 16 bit case do the accurate conversion even

-                * in the 'fast' case - this is because this is where the code

-                * ends up when handling linear 16 bit data.

-                */

-               gray16  = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >>

-                  15);

-               *(dp++) = (png_byte)((gray16>>8) & 0xff);

-               *(dp++) = (png_byte)(gray16 & 0xff);

-

-               if (have_alpha)

-               {

-                  *(dp++) = *(sp++);

-                  *(dp++) = *(sp++);

-               }

-            }

-         }

-      }

-

-      row_info->channels = (png_byte)(row_info->channels - 2);

-      row_info->color_type = (png_byte)(row_info->color_type &

-          ~PNG_COLOR_MASK_COLOR);

-      row_info->pixel_depth = (png_byte)(row_info->channels *

-          row_info->bit_depth);

-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);

-   }

-   return rgb_error;

-}

-#endif

-#endif /* PNG_READ_TRANSFORMS_SUPPORTED */

-

-#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED

-/* Build a grayscale palette.  Palette is assumed to be 1 << bit_depth

- * large of png_color.  This lets grayscale images be treated as

- * paletted.  Most useful for gamma correction and simplification

- * of code.  This API is not used internally.

- */

-void PNGAPI

-png_build_grayscale_palette(int bit_depth, png_colorp palette)

-{

-   int num_palette;

-   int color_inc;

-   int i;

-   int v;

-

-   png_debug(1, "in png_do_build_grayscale_palette");

-

-   if (palette == NULL)

-      return;

-

-   switch (bit_depth)

-   {

-      case 1:

-         num_palette = 2;

-         color_inc = 0xff;

-         break;

-

-      case 2:

-         num_palette = 4;

-         color_inc = 0x55;

-         break;

-

-      case 4:

-         num_palette = 16;

-         color_inc = 0x11;

-         break;

-

-      case 8:

-         num_palette = 256;

-         color_inc = 1;

-         break;

-

-      default:

-         num_palette = 0;

-         color_inc = 0;

-         break;

-   }

-

-   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)

-   {

-      palette[i].red = (png_byte)v;

-      palette[i].green = (png_byte)v;

-      palette[i].blue = (png_byte)v;

-   }

-}

-#endif

-

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)

-/* Replace any alpha or transparency with the supplied background color.

- * "background" is already in the screen gamma, while "background_1" is

- * at a gamma of 1.0.  Paletted files have already been taken care of.

- */

-void /* PRIVATE */

-png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr)

-{

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   png_const_bytep gamma_table = png_ptr->gamma_table;

-   png_const_bytep gamma_from_1 = png_ptr->gamma_from_1;

-   png_const_bytep gamma_to_1 = png_ptr->gamma_to_1;

-   png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table;

-   png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1;

-   png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1;

-   int gamma_shift = png_ptr->gamma_shift;

-   int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;

-#endif

-

-   png_bytep sp;

-   png_uint_32 i;

-   png_uint_32 row_width = row_info->width;

-   int shift;

-

-   png_debug(1, "in png_do_compose");

-

-   {

-      switch (row_info->color_type)

-      {

-         case PNG_COLOR_TYPE_GRAY:

-         {

-            switch (row_info->bit_depth)

-            {

-               case 1:

-               {

-                  sp = row;

-                  shift = 7;

-                  for (i = 0; i < row_width; i++)

-                  {

-                     if ((png_uint_16)((*sp >> shift) & 0x01)

-                        == png_ptr->trans_color.gray)

-                     {

-                        unsigned int tmp = *sp & (0x7f7f >> (7 - shift));

-                        tmp |= png_ptr->background.gray << shift;

-                        *sp = (png_byte)(tmp & 0xff);

-                     }

-

-                     if (!shift)

-                     {

-                        shift = 7;

-                        sp++;

-                     }

-

-                     else

-                        shift--;

-                  }

-                  break;

-               }

-

-               case 2:

-               {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-                  if (gamma_table != NULL)

-                  {

-                     sp = row;

-                     shift = 6;

-                     for (i = 0; i < row_width; i++)

-                     {

-                        if ((png_uint_16)((*sp >> shift) & 0x03)

-                            == png_ptr->trans_color.gray)

-                        {

-                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));

-                           tmp |= png_ptr->background.gray << shift;

-                           *sp = (png_byte)(tmp & 0xff);

-                        }

-

-                        else

-                        {

-                           unsigned int p = (*sp >> shift) & 0x03;

-                           unsigned int g = (gamma_table [p | (p << 2) |

-                               (p << 4) | (p << 6)] >> 6) & 0x03;

-                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));

-                           tmp |= g << shift;

-                           *sp = (png_byte)(tmp & 0xff);

-                        }

-

-                        if (!shift)

-                        {

-                           shift = 6;

-                           sp++;

-                        }

-

-                        else

-                           shift -= 2;

-                     }

-                  }

-

-                  else

-#endif

-                  {

-                     sp = row;

-                     shift = 6;

-                     for (i = 0; i < row_width; i++)

-                     {

-                        if ((png_uint_16)((*sp >> shift) & 0x03)

-                            == png_ptr->trans_color.gray)

-                        {

-                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));

-                           tmp |= png_ptr->background.gray << shift;

-                           *sp = (png_byte)(tmp & 0xff);

-                        }

-

-                        if (!shift)

-                        {

-                           shift = 6;

-                           sp++;

-                        }

-

-                        else

-                           shift -= 2;

-                     }

-                  }

-                  break;

-               }

-

-               case 4:

-               {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-                  if (gamma_table != NULL)

-                  {

-                     sp = row;

-                     shift = 4;

-                     for (i = 0; i < row_width; i++)

-                     {

-                        if ((png_uint_16)((*sp >> shift) & 0x0f)

-                            == png_ptr->trans_color.gray)

-                        {

-                           unsigned int tmp = *sp & (0xf0f >> (4 - shift));

-                           tmp |= png_ptr->background.gray << shift;

-                           *sp = (png_byte)(tmp & 0xff);

-                        }

-

-                        else

-                        {

-                           unsigned int p = (*sp >> shift) & 0x0f;

-                           unsigned int g = (gamma_table[p | (p << 4)] >> 4) &

-                              0x0f;

-                           unsigned int tmp = *sp & (0xf0f >> (4 - shift));

-                           tmp |= g << shift;

-                           *sp = (png_byte)(tmp & 0xff);

-                        }

-

-                        if (!shift)

-                        {

-                           shift = 4;

-                           sp++;

-                        }

-

-                        else

-                           shift -= 4;

-                     }

-                  }

-

-                  else

-#endif

-                  {

-                     sp = row;

-                     shift = 4;

-                     for (i = 0; i < row_width; i++)

-                     {

-                        if ((png_uint_16)((*sp >> shift) & 0x0f)

-                            == png_ptr->trans_color.gray)

-                        {

-                           unsigned int tmp = *sp & (0xf0f >> (4 - shift));

-                           tmp |= png_ptr->background.gray << shift;

-                           *sp = (png_byte)(tmp & 0xff);

-                        }

-

-                        if (!shift)

-                        {

-                           shift = 4;

-                           sp++;

-                        }

-

-                        else

-                           shift -= 4;

-                     }

-                  }

-                  break;

-               }

-

-               case 8:

-               {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-                  if (gamma_table != NULL)

-                  {

-                     sp = row;

-                     for (i = 0; i < row_width; i++, sp++)

-                     {

-                        if (*sp == png_ptr->trans_color.gray)

-                           *sp = (png_byte)png_ptr->background.gray;

-

-                        else

-                           *sp = gamma_table[*sp];

-                     }

-                  }

-                  else

-#endif

-                  {

-                     sp = row;

-                     for (i = 0; i < row_width; i++, sp++)

-                     {

-                        if (*sp == png_ptr->trans_color.gray)

-                           *sp = (png_byte)png_ptr->background.gray;

-                     }

-                  }

-                  break;

-               }

-

-               case 16:

-               {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-                  if (gamma_16 != NULL)

-                  {

-                     sp = row;

-                     for (i = 0; i < row_width; i++, sp += 2)

-                     {

-                        png_uint_16 v;

-

-                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));

-

-                        if (v == png_ptr->trans_color.gray)

-                        {

-                           /* Background is already in screen gamma */

-                           *sp = (png_byte)((png_ptr->background.gray >> 8)

-                                & 0xff);

-                           *(sp + 1) = (png_byte)(png_ptr->background.gray

-                                & 0xff);

-                        }

-

-                        else

-                        {

-                           v = gamma_16[*(sp + 1) >> gamma_shift][*sp];

-                           *sp = (png_byte)((v >> 8) & 0xff);

-                           *(sp + 1) = (png_byte)(v & 0xff);

-                        }

-                     }

-                  }

-                  else

-#endif

-                  {

-                     sp = row;

-                     for (i = 0; i < row_width; i++, sp += 2)

-                     {

-                        png_uint_16 v;

-

-                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));

-

-                        if (v == png_ptr->trans_color.gray)

-                        {

-                           *sp = (png_byte)((png_ptr->background.gray >> 8)

-                                & 0xff);

-                           *(sp + 1) = (png_byte)(png_ptr->background.gray

-                                & 0xff);

-                        }

-                     }

-                  }

-                  break;

-               }

-

-               default:

-                  break;

-            }

-            break;

-         }

-

-         case PNG_COLOR_TYPE_RGB:

-         {

-            if (row_info->bit_depth == 8)

-            {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-               if (gamma_table != NULL)

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 3)

-                  {

-                     if (*sp == png_ptr->trans_color.red &&

-                         *(sp + 1) == png_ptr->trans_color.green &&

-                         *(sp + 2) == png_ptr->trans_color.blue)

-                     {

-                        *sp = (png_byte)png_ptr->background.red;

-                        *(sp + 1) = (png_byte)png_ptr->background.green;

-                        *(sp + 2) = (png_byte)png_ptr->background.blue;

-                     }

-

-                     else

-                     {

-                        *sp = gamma_table[*sp];

-                        *(sp + 1) = gamma_table[*(sp + 1)];

-                        *(sp + 2) = gamma_table[*(sp + 2)];

-                     }

-                  }

-               }

-               else

-#endif

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 3)

-                  {

-                     if (*sp == png_ptr->trans_color.red &&

-                         *(sp + 1) == png_ptr->trans_color.green &&

-                         *(sp + 2) == png_ptr->trans_color.blue)

-                     {

-                        *sp = (png_byte)png_ptr->background.red;

-                        *(sp + 1) = (png_byte)png_ptr->background.green;

-                        *(sp + 2) = (png_byte)png_ptr->background.blue;

-                     }

-                  }

-               }

-            }

-            else /* if (row_info->bit_depth == 16) */

-            {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-               if (gamma_16 != NULL)

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 6)

-                  {

-                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));

-

-                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)

-                         + *(sp + 3));

-

-                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)

-                         + *(sp + 5));

-

-                     if (r == png_ptr->trans_color.red &&

-                         g == png_ptr->trans_color.green &&

-                         b == png_ptr->trans_color.blue)

-                     {

-                        /* Background is already in screen gamma */

-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);

-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)

-                                & 0xff);

-                        *(sp + 3) = (png_byte)(png_ptr->background.green

-                                & 0xff);

-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)

-                                & 0xff);

-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);

-                     }

-

-                     else

-                     {

-                        png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];

-                        *sp = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(v & 0xff);

-

-                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];

-                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 3) = (png_byte)(v & 0xff);

-

-                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];

-                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 5) = (png_byte)(v & 0xff);

-                     }

-                  }

-               }

-

-               else

-#endif

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 6)

-                  {

-                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));

-

-                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)

-                         + *(sp + 3));

-

-                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)

-                         + *(sp + 5));

-

-                     if (r == png_ptr->trans_color.red &&

-                         g == png_ptr->trans_color.green &&

-                         b == png_ptr->trans_color.blue)

-                     {

-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);

-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)

-                                & 0xff);

-                        *(sp + 3) = (png_byte)(png_ptr->background.green

-                                & 0xff);

-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)

-                                & 0xff);

-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);

-                     }

-                  }

-               }

-            }

-            break;

-         }

-

-         case PNG_COLOR_TYPE_GRAY_ALPHA:

-         {

-            if (row_info->bit_depth == 8)

-            {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&

-                   gamma_table != NULL)

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 2)

-                  {

-                     png_uint_16 a = *(sp + 1);

-

-                     if (a == 0xff)

-                        *sp = gamma_table[*sp];

-

-                     else if (a == 0)

-                     {

-                        /* Background is already in screen gamma */

-                        *sp = (png_byte)png_ptr->background.gray;

-                     }

-

-                     else

-                     {

-                        png_byte v, w;

-

-                        v = gamma_to_1[*sp];

-                        png_composite(w, v, a, png_ptr->background_1.gray);

-                        if (!optimize)

-                           w = gamma_from_1[w];

-                        *sp = w;

-                     }

-                  }

-               }

-               else

-#endif

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 2)

-                  {

-                     png_byte a = *(sp + 1);

-

-                     if (a == 0)

-                        *sp = (png_byte)png_ptr->background.gray;

-

-                     else if (a < 0xff)

-                        png_composite(*sp, *sp, a, png_ptr->background.gray);

-                  }

-               }

-            }

-            else /* if (png_ptr->bit_depth == 16) */

-            {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&

-                   gamma_16_to_1 != NULL)

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 4)

-                  {

-                     png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)

-                         + *(sp + 3));

-

-                     if (a == (png_uint_16)0xffff)

-                     {

-                        png_uint_16 v;

-

-                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];

-                        *sp = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(v & 0xff);

-                     }

-

-                     else if (a == 0)

-                     {

-                        /* Background is already in screen gamma */

-                        *sp = (png_byte)((png_ptr->background.gray >> 8)

-                                & 0xff);

-                        *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);

-                     }

-

-                     else

-                     {

-                        png_uint_16 g, v, w;

-

-                        g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];

-                        png_composite_16(v, g, a, png_ptr->background_1.gray);

-                        if (optimize)

-                           w = v;

-                        else

-                           w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];

-                        *sp = (png_byte)((w >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(w & 0xff);

-                     }

-                  }

-               }

-               else

-#endif

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 4)

-                  {

-                     png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)

-                         + *(sp + 3));

-

-                     if (a == 0)

-                     {

-                        *sp = (png_byte)((png_ptr->background.gray >> 8)

-                                & 0xff);

-                        *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);

-                     }

-

-                     else if (a < 0xffff)

-                     {

-                        png_uint_16 g, v;

-

-                        g = (png_uint_16)(((*sp) << 8) + *(sp + 1));

-                        png_composite_16(v, g, a, png_ptr->background.gray);

-                        *sp = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(v & 0xff);

-                     }

-                  }

-               }

-            }

-            break;

-         }

-

-         case PNG_COLOR_TYPE_RGB_ALPHA:

-         {

-            if (row_info->bit_depth == 8)

-            {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&

-                   gamma_table != NULL)

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 4)

-                  {

-                     png_byte a = *(sp + 3);

-

-                     if (a == 0xff)

-                     {

-                        *sp = gamma_table[*sp];

-                        *(sp + 1) = gamma_table[*(sp + 1)];

-                        *(sp + 2) = gamma_table[*(sp + 2)];

-                     }

-

-                     else if (a == 0)

-                     {

-                        /* Background is already in screen gamma */

-                        *sp = (png_byte)png_ptr->background.red;

-                        *(sp + 1) = (png_byte)png_ptr->background.green;

-                        *(sp + 2) = (png_byte)png_ptr->background.blue;

-                     }

-

-                     else

-                     {

-                        png_byte v, w;

-

-                        v = gamma_to_1[*sp];

-                        png_composite(w, v, a, png_ptr->background_1.red);

-                        if (!optimize) w = gamma_from_1[w];

-                        *sp = w;

-

-                        v = gamma_to_1[*(sp + 1)];

-                        png_composite(w, v, a, png_ptr->background_1.green);

-                        if (!optimize) w = gamma_from_1[w];

-                        *(sp + 1) = w;

-

-                        v = gamma_to_1[*(sp + 2)];

-                        png_composite(w, v, a, png_ptr->background_1.blue);

-                        if (!optimize) w = gamma_from_1[w];

-                        *(sp + 2) = w;

-                     }

-                  }

-               }

-               else

-#endif

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 4)

-                  {

-                     png_byte a = *(sp + 3);

-

-                     if (a == 0)

-                     {

-                        *sp = (png_byte)png_ptr->background.red;

-                        *(sp + 1) = (png_byte)png_ptr->background.green;

-                        *(sp + 2) = (png_byte)png_ptr->background.blue;

-                     }

-

-                     else if (a < 0xff)

-                     {

-                        png_composite(*sp, *sp, a, png_ptr->background.red);

-

-                        png_composite(*(sp + 1), *(sp + 1), a,

-                            png_ptr->background.green);

-

-                        png_composite(*(sp + 2), *(sp + 2), a,

-                            png_ptr->background.blue);

-                     }

-                  }

-               }

-            }

-            else /* if (row_info->bit_depth == 16) */

-            {

-#ifdef PNG_READ_GAMMA_SUPPORTED

-               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&

-                   gamma_16_to_1 != NULL)

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 8)

-                  {

-                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))

-                         << 8) + (png_uint_16)(*(sp + 7)));

-

-                     if (a == (png_uint_16)0xffff)

-                     {

-                        png_uint_16 v;

-

-                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];

-                        *sp = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(v & 0xff);

-

-                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];

-                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 3) = (png_byte)(v & 0xff);

-

-                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];

-                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 5) = (png_byte)(v & 0xff);

-                     }

-

-                     else if (a == 0)

-                     {

-                        /* Background is already in screen gamma */

-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);

-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)

-                                & 0xff);

-                        *(sp + 3) = (png_byte)(png_ptr->background.green

-                                & 0xff);

-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)

-                                & 0xff);

-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);

-                     }

-

-                     else

-                     {

-                        png_uint_16 v, w;

-

-                        v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];

-                        png_composite_16(w, v, a, png_ptr->background_1.red);

-                        if (!optimize)

-                           w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >>

-                                8];

-                        *sp = (png_byte)((w >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(w & 0xff);

-

-                        v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];

-                        png_composite_16(w, v, a, png_ptr->background_1.green);

-                        if (!optimize)

-                           w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >>

-                                8];

-

-                        *(sp + 2) = (png_byte)((w >> 8) & 0xff);

-                        *(sp + 3) = (png_byte)(w & 0xff);

-

-                        v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];

-                        png_composite_16(w, v, a, png_ptr->background_1.blue);

-                        if (!optimize)

-                           w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >>

-                                8];

-

-                        *(sp + 4) = (png_byte)((w >> 8) & 0xff);

-                        *(sp + 5) = (png_byte)(w & 0xff);

-                     }

-                  }

-               }

-

-               else

-#endif

-               {

-                  sp = row;

-                  for (i = 0; i < row_width; i++, sp += 8)

-                  {

-                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))

-                         << 8) + (png_uint_16)(*(sp + 7)));

-

-                     if (a == 0)

-                     {

-                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);

-                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)

-                                & 0xff);

-                        *(sp + 3) = (png_byte)(png_ptr->background.green

-                                & 0xff);

-                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)

-                                & 0xff);

-                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);

-                     }

-

-                     else if (a < 0xffff)

-                     {

-                        png_uint_16 v;

-

-                        png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));

-                        png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)

-                            + *(sp + 3));

-                        png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)

-                            + *(sp + 5));

-

-                        png_composite_16(v, r, a, png_ptr->background.red);

-                        *sp = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 1) = (png_byte)(v & 0xff);

-

-                        png_composite_16(v, g, a, png_ptr->background.green);

-                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 3) = (png_byte)(v & 0xff);

-

-                        png_composite_16(v, b, a, png_ptr->background.blue);

-                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);

-                        *(sp + 5) = (png_byte)(v & 0xff);

-                     }

-                  }

-               }

-            }

-            break;

-         }

-

-         default:

-            break;

-      }

-   }

-}

-#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_READ_ALPHA_MODE_SUPPORTED */

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-/* Gamma correct the image, avoiding the alpha channel.  Make sure

- * you do this after you deal with the transparency issue on grayscale

- * or RGB images. If your bit depth is 8, use gamma_table, if it

- * is 16, use gamma_16_table and gamma_shift.  Build these with

- * build_gamma_table().

- */

-void /* PRIVATE */

-png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr)

-{

-   png_const_bytep gamma_table = png_ptr->gamma_table;

-   png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table;

-   int gamma_shift = png_ptr->gamma_shift;

-

-   png_bytep sp;

-   png_uint_32 i;

-   png_uint_32 row_width=row_info->width;

-

-   png_debug(1, "in png_do_gamma");

-

-   if (((row_info->bit_depth <= 8 && gamma_table != NULL) ||

-       (row_info->bit_depth == 16 && gamma_16_table != NULL)))

-   {

-      switch (row_info->color_type)

-      {

-         case PNG_COLOR_TYPE_RGB:

-         {

-            if (row_info->bit_depth == 8)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  *sp = gamma_table[*sp];

-                  sp++;

-                  *sp = gamma_table[*sp];

-                  sp++;

-                  *sp = gamma_table[*sp];

-                  sp++;

-               }

-            }

-

-            else /* if (row_info->bit_depth == 16) */

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  png_uint_16 v;

-

-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 2;

-

-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 2;

-

-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 2;

-               }

-            }

-            break;

-         }

-

-         case PNG_COLOR_TYPE_RGB_ALPHA:

-         {

-            if (row_info->bit_depth == 8)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  *sp = gamma_table[*sp];

-                  sp++;

-

-                  *sp = gamma_table[*sp];

-                  sp++;

-

-                  *sp = gamma_table[*sp];

-                  sp++;

-

-                  sp++;

-               }

-            }

-

-            else /* if (row_info->bit_depth == 16) */

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 2;

-

-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 2;

-

-                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 4;

-               }

-            }

-            break;

-         }

-

-         case PNG_COLOR_TYPE_GRAY_ALPHA:

-         {

-            if (row_info->bit_depth == 8)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  *sp = gamma_table[*sp];

-                  sp += 2;

-               }

-            }

-

-            else /* if (row_info->bit_depth == 16) */

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 4;

-               }

-            }

-            break;

-         }

-

-         case PNG_COLOR_TYPE_GRAY:

-         {

-            if (row_info->bit_depth == 2)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i += 4)

-               {

-                  int a = *sp & 0xc0;

-                  int b = *sp & 0x30;

-                  int c = *sp & 0x0c;

-                  int d = *sp & 0x03;

-

-                  *sp = (png_byte)(

-                      ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|

-                      ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|

-                      ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|

-                      ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));

-                  sp++;

-               }

-            }

-

-            if (row_info->bit_depth == 4)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i += 2)

-               {

-                  int msb = *sp & 0xf0;

-                  int lsb = *sp & 0x0f;

-

-                  *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)

-                      | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));

-                  sp++;

-               }

-            }

-

-            else if (row_info->bit_depth == 8)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  *sp = gamma_table[*sp];

-                  sp++;

-               }

-            }

-

-            else if (row_info->bit_depth == 16)

-            {

-               sp = row;

-               for (i = 0; i < row_width; i++)

-               {

-                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];

-                  *sp = (png_byte)((v >> 8) & 0xff);

-                  *(sp + 1) = (png_byte)(v & 0xff);

-                  sp += 2;

-               }

-            }

-            break;

-         }

-

-         default:

-            break;

-      }

-   }

-}

-#endif

-

-#ifdef PNG_READ_ALPHA_MODE_SUPPORTED

-/* Encode the alpha channel to the output gamma (the input channel is always

- * linear.)  Called only with color types that have an alpha channel.  Needs the

- * from_1 tables.

- */

-void /* PRIVATE */

-png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr)

-{

-   png_uint_32 row_width = row_info->width;

-

-   png_debug(1, "in png_do_encode_alpha");

-

-   if (row_info->color_type & PNG_COLOR_MASK_ALPHA)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         PNG_CONST png_bytep table = png_ptr->gamma_from_1;

-

-         if (table != NULL)

-         {

-            PNG_CONST int step =

-               (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;

-

-            /* The alpha channel is the last component: */

-            row += step - 1;

-

-            for (; row_width > 0; --row_width, row += step)

-               *row = table[*row];

-

-            return;

-         }

-      }

-

-      else if (row_info->bit_depth == 16)

-      {

-         PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1;

-         PNG_CONST int gamma_shift = png_ptr->gamma_shift;

-

-         if (table != NULL)

-         {

-            PNG_CONST int step =

-               (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;

-

-            /* The alpha channel is the last component: */

-            row += step - 2;

-

-            for (; row_width > 0; --row_width, row += step)

-            {

-               png_uint_16 v;

-

-               v = table[*(row + 1) >> gamma_shift][*row];

-               *row = (png_byte)((v >> 8) & 0xff);

-               *(row + 1) = (png_byte)(v & 0xff);

-            }

-

-            return;

-         }

-      }

-   }

-

-   /* Only get to here if called with a weird row_info; no harm has been done,

-    * so just issue a warning.

-    */

-   png_warning(png_ptr, "png_do_encode_alpha: unexpected call");

-}

-#endif

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-/* Expands a palette row to an RGB or RGBA row depending

- * upon whether you supply trans and num_trans.

- */

-void /* PRIVATE */

-png_do_expand_palette(png_row_infop row_info, png_bytep row,

-   png_const_colorp palette, png_const_bytep trans_alpha, int num_trans)

-{

-   int shift, value;

-   png_bytep sp, dp;

-   png_uint_32 i;

-   png_uint_32 row_width=row_info->width;

-

-   png_debug(1, "in png_do_expand_palette");

-

-   if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)

-   {

-      if (row_info->bit_depth < 8)

-      {

-         switch (row_info->bit_depth)

-         {

-            case 1:

-            {

-               sp = row + (png_size_t)((row_width - 1) >> 3);

-               dp = row + (png_size_t)row_width - 1;

-               shift = 7 - (int)((row_width + 7) & 0x07);

-               for (i = 0; i < row_width; i++)

-               {

-                  if ((*sp >> shift) & 0x01)

-                     *dp = 1;

-

-                  else

-                     *dp = 0;

-

-                  if (shift == 7)

-                  {

-                     shift = 0;

-                     sp--;

-                  }

-

-                  else

-                     shift++;

-

-                  dp--;

-               }

-               break;

-            }

-

-            case 2:

-            {

-               sp = row + (png_size_t)((row_width - 1) >> 2);

-               dp = row + (png_size_t)row_width - 1;

-               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);

-               for (i = 0; i < row_width; i++)

-               {

-                  value = (*sp >> shift) & 0x03;

-                  *dp = (png_byte)value;

-                  if (shift == 6)

-                  {

-                     shift = 0;

-                     sp--;

-                  }

-

-                  else

-                     shift += 2;

-

-                  dp--;

-               }

-               break;

-            }

-

-            case 4:

-            {

-               sp = row + (png_size_t)((row_width - 1) >> 1);

-               dp = row + (png_size_t)row_width - 1;

-               shift = (int)((row_width & 0x01) << 2);

-               for (i = 0; i < row_width; i++)

-               {

-                  value = (*sp >> shift) & 0x0f;

-                  *dp = (png_byte)value;

-                  if (shift == 4)

-                  {

-                     shift = 0;

-                     sp--;

-                  }

-

-                  else

-                     shift += 4;

-

-                  dp--;

-               }

-               break;

-            }

-

-            default:

-               break;

-         }

-         row_info->bit_depth = 8;

-         row_info->pixel_depth = 8;

-         row_info->rowbytes = row_width;

-      }

-

-      if (row_info->bit_depth == 8)

-      {

-         {

-            if (num_trans > 0)

-            {

-               sp = row + (png_size_t)row_width - 1;

-               dp = row + (png_size_t)(row_width << 2) - 1;

-

-               for (i = 0; i < row_width; i++)

-               {

-                  if ((int)(*sp) >= num_trans)

-                     *dp-- = 0xff;

-

-                  else

-                     *dp-- = trans_alpha[*sp];

-

-                  *dp-- = palette[*sp].blue;

-                  *dp-- = palette[*sp].green;

-                  *dp-- = palette[*sp].red;

-                  sp--;

-               }

-               row_info->bit_depth = 8;

-               row_info->pixel_depth = 32;

-               row_info->rowbytes = row_width * 4;

-               row_info->color_type = 6;

-               row_info->channels = 4;

-            }

-

-            else

-            {

-               sp = row + (png_size_t)row_width - 1;

-               dp = row + (png_size_t)(row_width * 3) - 1;

-

-               for (i = 0; i < row_width; i++)

-               {

-                  *dp-- = palette[*sp].blue;

-                  *dp-- = palette[*sp].green;

-                  *dp-- = palette[*sp].red;

-                  sp--;

-               }

-

-               row_info->bit_depth = 8;

-               row_info->pixel_depth = 24;

-               row_info->rowbytes = row_width * 3;

-               row_info->color_type = 2;

-               row_info->channels = 3;

-            }

-         }

-      }

-   }

-}

-

-/* If the bit depth < 8, it is expanded to 8.  Also, if the already

- * expanded transparency value is supplied, an alpha channel is built.

- */

-void /* PRIVATE */

-png_do_expand(png_row_infop row_info, png_bytep row,

-    png_const_color_16p trans_color)

-{

-   int shift, value;

-   png_bytep sp, dp;

-   png_uint_32 i;

-   png_uint_32 row_width=row_info->width;

-

-   png_debug(1, "in png_do_expand");

-

-   {

-      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)

-      {

-         unsigned int gray = trans_color ? trans_color->gray : 0;

-

-         if (row_info->bit_depth < 8)

-         {

-            switch (row_info->bit_depth)

-            {

-               case 1:

-               {

-                  gray = (gray & 0x01) * 0xff;

-                  sp = row + (png_size_t)((row_width - 1) >> 3);

-                  dp = row + (png_size_t)row_width - 1;

-                  shift = 7 - (int)((row_width + 7) & 0x07);

-                  for (i = 0; i < row_width; i++)

-                  {

-                     if ((*sp >> shift) & 0x01)

-                        *dp = 0xff;

-

-                     else

-                        *dp = 0;

-

-                     if (shift == 7)

-                     {

-                        shift = 0;

-                        sp--;

-                     }

-

-                     else

-                        shift++;

-

-                     dp--;

-                  }

-                  break;

-               }

-

-               case 2:

-               {

-                  gray = (gray & 0x03) * 0x55;

-                  sp = row + (png_size_t)((row_width - 1) >> 2);

-                  dp = row + (png_size_t)row_width - 1;

-                  shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);

-                  for (i = 0; i < row_width; i++)

-                  {

-                     value = (*sp >> shift) & 0x03;

-                     *dp = (png_byte)(value | (value << 2) | (value << 4) |

-                        (value << 6));

-                     if (shift == 6)

-                     {

-                        shift = 0;

-                        sp--;

-                     }

-

-                     else

-                        shift += 2;

-

-                     dp--;

-                  }

-                  break;

-               }

-

-               case 4:

-               {

-                  gray = (gray & 0x0f) * 0x11;

-                  sp = row + (png_size_t)((row_width - 1) >> 1);

-                  dp = row + (png_size_t)row_width - 1;

-                  shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);

-                  for (i = 0; i < row_width; i++)

-                  {

-                     value = (*sp >> shift) & 0x0f;

-                     *dp = (png_byte)(value | (value << 4));

-                     if (shift == 4)

-                     {

-                        shift = 0;

-                        sp--;

-                     }

-

-                     else

-                        shift = 4;

-

-                     dp--;

-                  }

-                  break;

-               }

-

-               default:

-                  break;

-            }

-

-            row_info->bit_depth = 8;

-            row_info->pixel_depth = 8;

-            row_info->rowbytes = row_width;

-         }

-

-         if (trans_color != NULL)

-         {

-            if (row_info->bit_depth == 8)

-            {

-               gray = gray & 0xff;

-               sp = row + (png_size_t)row_width - 1;

-               dp = row + (png_size_t)(row_width << 1) - 1;

-

-               for (i = 0; i < row_width; i++)

-               {

-                  if (*sp == gray)

-                     *dp-- = 0;

-

-                  else

-                     *dp-- = 0xff;

-

-                  *dp-- = *sp--;

-               }

-            }

-

-            else if (row_info->bit_depth == 16)

-            {

-               unsigned int gray_high = (gray >> 8) & 0xff;

-               unsigned int gray_low = gray & 0xff;

-               sp = row + row_info->rowbytes - 1;

-               dp = row + (row_info->rowbytes << 1) - 1;

-               for (i = 0; i < row_width; i++)

-               {

-                  if (*(sp - 1) == gray_high && *(sp) == gray_low)

-                  {

-                     *dp-- = 0;

-                     *dp-- = 0;

-                  }

-

-                  else

-                  {

-                     *dp-- = 0xff;

-                     *dp-- = 0xff;

-                  }

-

-                  *dp-- = *sp--;

-                  *dp-- = *sp--;

-               }

-            }

-

-            row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;

-            row_info->channels = 2;

-            row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);

-            row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,

-               row_width);

-         }

-      }

-      else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_color)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            png_byte red = (png_byte)(trans_color->red & 0xff);

-            png_byte green = (png_byte)(trans_color->green & 0xff);

-            png_byte blue = (png_byte)(trans_color->blue & 0xff);

-            sp = row + (png_size_t)row_info->rowbytes - 1;

-            dp = row + (png_size_t)(row_width << 2) - 1;

-            for (i = 0; i < row_width; i++)

-            {

-               if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)

-                  *dp-- = 0;

-

-               else

-                  *dp-- = 0xff;

-

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-            }

-         }

-         else if (row_info->bit_depth == 16)

-         {

-            png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);

-            png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);

-            png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);

-            png_byte red_low = (png_byte)(trans_color->red & 0xff);

-            png_byte green_low = (png_byte)(trans_color->green & 0xff);

-            png_byte blue_low = (png_byte)(trans_color->blue & 0xff);

-            sp = row + row_info->rowbytes - 1;

-            dp = row + (png_size_t)(row_width << 3) - 1;

-            for (i = 0; i < row_width; i++)

-            {

-               if (*(sp - 5) == red_high &&

-                   *(sp - 4) == red_low &&

-                   *(sp - 3) == green_high &&

-                   *(sp - 2) == green_low &&

-                   *(sp - 1) == blue_high &&

-                   *(sp    ) == blue_low)

-               {

-                  *dp-- = 0;

-                  *dp-- = 0;

-               }

-

-               else

-               {

-                  *dp-- = 0xff;

-                  *dp-- = 0xff;

-               }

-

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-               *dp-- = *sp--;

-            }

-         }

-         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;

-         row_info->channels = 4;

-         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);

-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);

-      }

-   }

-}

-#endif

-

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-/* If the bit depth is 8 and the color type is not a palette type expand the

- * whole row to 16 bits.  Has no effect otherwise.

- */

-void /* PRIVATE */

-png_do_expand_16(png_row_infop row_info, png_bytep row)

-{

-   if (row_info->bit_depth == 8 &&

-      row_info->color_type != PNG_COLOR_TYPE_PALETTE)

-   {

-      /* The row have a sequence of bytes containing [0..255] and we need

-       * to turn it into another row containing [0..65535], to do this we

-       * calculate:

-       *

-       *  (input / 255) * 65535

-       *

-       *  Which happens to be exactly input * 257 and this can be achieved

-       *  simply by byte replication in place (copying backwards).

-       */

-      png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */

-      png_byte *dp = sp + row_info->rowbytes;  /* destination, end + 1 */

-      while (dp > sp)

-         dp[-2] = dp[-1] = *--sp, dp -= 2;

-

-      row_info->rowbytes *= 2;

-      row_info->bit_depth = 16;

-      row_info->pixel_depth = (png_byte)(row_info->channels * 16);

-   }

-}

-#endif

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-void /* PRIVATE */

-png_do_quantize(png_row_infop row_info, png_bytep row,

-    png_const_bytep palette_lookup, png_const_bytep quantize_lookup)

-{

-   png_bytep sp, dp;

-   png_uint_32 i;

-   png_uint_32 row_width=row_info->width;

-

-   png_debug(1, "in png_do_quantize");

-

-   if (row_info->bit_depth == 8)

-   {

-      if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup)

-      {

-         int r, g, b, p;

-         sp = row;

-         dp = row;

-         for (i = 0; i < row_width; i++)

-         {

-            r = *sp++;

-            g = *sp++;

-            b = *sp++;

-

-            /* This looks real messy, but the compiler will reduce

-             * it down to a reasonable formula.  For example, with

-             * 5 bits per color, we get:

-             * p = (((r >> 3) & 0x1f) << 10) |

-             *    (((g >> 3) & 0x1f) << 5) |

-             *    ((b >> 3) & 0x1f);

-             */

-            p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &

-                ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<

-                (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |

-                (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &

-                ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<

-                (PNG_QUANTIZE_BLUE_BITS)) |

-                ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &

-                ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));

-

-            *dp++ = palette_lookup[p];

-         }

-

-         row_info->color_type = PNG_COLOR_TYPE_PALETTE;

-         row_info->channels = 1;

-         row_info->pixel_depth = row_info->bit_depth;

-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);

-      }

-

-      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&

-         palette_lookup != NULL)

-      {

-         int r, g, b, p;

-         sp = row;

-         dp = row;

-         for (i = 0; i < row_width; i++)

-         {

-            r = *sp++;

-            g = *sp++;

-            b = *sp++;

-            sp++;

-

-            p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &

-                ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<

-                (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |

-                (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &

-                ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<

-                (PNG_QUANTIZE_BLUE_BITS)) |

-                ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &

-                ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));

-

-            *dp++ = palette_lookup[p];

-         }

-

-         row_info->color_type = PNG_COLOR_TYPE_PALETTE;

-         row_info->channels = 1;

-         row_info->pixel_depth = row_info->bit_depth;

-         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);

-      }

-

-      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&

-         quantize_lookup)

-      {

-         sp = row;

-

-         for (i = 0; i < row_width; i++, sp++)

-         {

-            *sp = quantize_lookup[*sp];

-         }

-      }

-   }

-}

-#endif /* PNG_READ_QUANTIZE_SUPPORTED */

-#endif /* PNG_READ_TRANSFORMS_SUPPORTED */

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-/* Undoes intrapixel differencing  */

-void /* PRIVATE */

-png_do_read_intrapixel(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_read_intrapixel");

-

-   if (

-       (row_info->color_type & PNG_COLOR_MASK_COLOR))

-   {

-      int bytes_per_pixel;

-      png_uint_32 row_width = row_info->width;

-

-      if (row_info->bit_depth == 8)

-      {

-         png_bytep rp;

-         png_uint_32 i;

-

-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-            bytes_per_pixel = 3;

-

-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-            bytes_per_pixel = 4;

-

-         else

-            return;

-

-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)

-         {

-            *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff);

-            *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff);

-         }

-      }

-      else if (row_info->bit_depth == 16)

-      {

-         png_bytep rp;

-         png_uint_32 i;

-

-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-            bytes_per_pixel = 6;

-

-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-            bytes_per_pixel = 8;

-

-         else

-            return;

-

-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)

-         {

-            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);

-            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);

-            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);

-            png_uint_32 red  = (s0 + s1 + 65536) & 0xffff;

-            png_uint_32 blue = (s2 + s1 + 65536) & 0xffff;

-            *(rp    ) = (png_byte)((red >> 8) & 0xff);

-            *(rp + 1) = (png_byte)(red & 0xff);

-            *(rp + 4) = (png_byte)((blue >> 8) & 0xff);

-            *(rp + 5) = (png_byte)(blue & 0xff);

-         }

-      }

-   }

-}

-#endif /* PNG_MNG_FEATURES_SUPPORTED */

-#endif /* PNG_READ_SUPPORTED */

+
+/* pngrtran.c - transforms the data in a row for PNG readers
+ *
+ * Last changed in libpng 1.6.19 [November 12, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file contains functions optionally called by an application
+ * in order to tell libpng how to handle data when reading a PNG.
+ * Transformations that are used in both reading and writing are
+ * in pngtrans.c.
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+/* Set the action on getting a CRC error for an ancillary or critical chunk. */
+void PNGAPI
+png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action)
+{
+   png_debug(1, "in png_set_crc_action");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* Tell libpng how we react to CRC errors in critical chunks */
+   switch (crit_action)
+   {
+      case PNG_CRC_NO_CHANGE:                        /* Leave setting as is */
+         break;
+
+      case PNG_CRC_WARN_USE:                               /* Warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
+         break;
+
+      case PNG_CRC_QUIET_USE:                             /* Quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
+                           PNG_FLAG_CRC_CRITICAL_IGNORE;
+         break;
+
+      case PNG_CRC_WARN_DISCARD:    /* Not a valid action for critical data */
+         png_warning(png_ptr,
+            "Can't discard critical data on CRC error");
+      case PNG_CRC_ERROR_QUIT:                                /* Error/quit */
+
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         break;
+   }
+
+   /* Tell libpng how we react to CRC errors in ancillary chunks */
+   switch (ancil_action)
+   {
+      case PNG_CRC_NO_CHANGE:                       /* Leave setting as is */
+         break;
+
+      case PNG_CRC_WARN_USE:                              /* Warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
+         break;
+
+      case PNG_CRC_QUIET_USE:                            /* Quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
+                           PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+
+      case PNG_CRC_ERROR_QUIT:                               /* Error/quit */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+
+      case PNG_CRC_WARN_DISCARD:                      /* Warn/discard data */
+
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         break;
+   }
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Is it OK to set a transformation now?  Only if png_start_read_image or
+ * png_read_update_info have not been called.  It is not necessary for the IHDR
+ * to have been read in all cases; the need_IHDR parameter allows for this
+ * check too.
+ */
+static int
+png_rtran_ok(png_structrp png_ptr, int need_IHDR)
+{
+   if (png_ptr != NULL)
+   {
+      if ((png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
+         png_app_error(png_ptr,
+            "invalid after png_start_read_image or png_read_update_info");
+
+      else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0)
+         png_app_error(png_ptr, "invalid before the PNG header has been read");
+
+      else
+      {
+         /* Turn on failure to initialize correctly for all transforms. */
+         png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;
+
+         return 1; /* Ok */
+      }
+   }
+
+   return 0; /* no png_error possible! */
+}
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* Handle alpha and tRNS via a background color */
+void PNGFAPI
+png_set_background_fixed(png_structrp png_ptr,
+    png_const_color_16p background_color, int background_gamma_code,
+    int need_expand, png_fixed_point background_gamma)
+{
+   png_debug(1, "in png_set_background_fixed");
+
+   if (png_rtran_ok(png_ptr, 0) == 0 || background_color == NULL)
+      return;
+
+   if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
+   {
+      png_warning(png_ptr, "Application must supply a known background gamma");
+      return;
+   }
+
+   png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA;
+   png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+   png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+
+   png_ptr->background = *background_color;
+   png_ptr->background_gamma = background_gamma;
+   png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
+   if (need_expand != 0)
+      png_ptr->transformations |= PNG_BACKGROUND_EXPAND;
+   else
+      png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_background(png_structrp png_ptr,
+    png_const_color_16p background_color, int background_gamma_code,
+    int need_expand, double background_gamma)
+{
+   png_set_background_fixed(png_ptr, background_color, background_gamma_code,
+      need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));
+}
+#  endif  /* FLOATING_POINT */
+#endif /* READ_BACKGROUND */
+
+/* Scale 16-bit depth files to 8-bit depth.  If both of these are set then the
+ * one that pngrtran does first (scale) happens.  This is necessary to allow the
+ * TRANSFORM and API behavior to be somewhat consistent, and it's simpler.
+ */
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+void PNGAPI
+png_set_scale_16(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_scale_16");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= PNG_SCALE_16_TO_8;
+}
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+/* Chop 16-bit depth files to 8-bit depth */
+void PNGAPI
+png_set_strip_16(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_strip_16");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= PNG_16_TO_8;
+}
+#endif
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+void PNGAPI
+png_set_strip_alpha(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_strip_alpha");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= PNG_STRIP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
+static png_fixed_point
+translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma,
+   int is_screen)
+{
+   /* Check for flag values.  The main reason for having the old Mac value as a
+    * flag is that it is pretty near impossible to work out what the correct
+    * value is from Apple documentation - a working Mac system is needed to
+    * discover the value!
+    */
+   if (output_gamma == PNG_DEFAULT_sRGB ||
+      output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)
+   {
+      /* If there is no sRGB support this just sets the gamma to the standard
+       * sRGB value.  (This is a side effect of using this function!)
+       */
+#     ifdef PNG_READ_sRGB_SUPPORTED
+         png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;
+#     else
+         PNG_UNUSED(png_ptr)
+#     endif
+      if (is_screen != 0)
+         output_gamma = PNG_GAMMA_sRGB;
+      else
+         output_gamma = PNG_GAMMA_sRGB_INVERSE;
+   }
+
+   else if (output_gamma == PNG_GAMMA_MAC_18 ||
+      output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18)
+   {
+      if (is_screen != 0)
+         output_gamma = PNG_GAMMA_MAC_OLD;
+      else
+         output_gamma = PNG_GAMMA_MAC_INVERSE;
+   }
+
+   return output_gamma;
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+static png_fixed_point
+convert_gamma_value(png_structrp png_ptr, double output_gamma)
+{
+   /* The following silently ignores cases where fixed point (times 100,000)
+    * gamma values are passed to the floating point API.  This is safe and it
+    * means the fixed point constants work just fine with the floating point
+    * API.  The alternative would just lead to undetected errors and spurious
+    * bug reports.  Negative values fail inside the _fixed API unless they
+    * correspond to the flag values.
+    */
+   if (output_gamma > 0 && output_gamma < 128)
+      output_gamma *= PNG_FP_1;
+
+   /* This preserves -1 and -2 exactly: */
+   output_gamma = floor(output_gamma + .5);
+
+   if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN)
+      png_fixed_error(png_ptr, "gamma value");
+
+   return (png_fixed_point)output_gamma;
+}
+#  endif
+#endif /* READ_ALPHA_MODE || READ_GAMMA */
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+void PNGFAPI
+png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
+   png_fixed_point output_gamma)
+{
+   int compose = 0;
+   png_fixed_point file_gamma;
+
+   png_debug(1, "in png_set_alpha_mode");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
+
+   /* Validate the value to ensure it is in a reasonable range. The value
+    * is expected to be 1 or greater, but this range test allows for some
+    * viewing correction values.  The intent is to weed out users of this API
+    * who use the inverse of the gamma value accidentally!  Since some of these
+    * values are reasonable this may have to be changed.
+    */
+   if (output_gamma < 70000 || output_gamma > 300000)
+      png_error(png_ptr, "output gamma out of expected range");
+
+   /* The default file gamma is the inverse of the output gamma; the output
+    * gamma may be changed below so get the file value first:
+    */
+   file_gamma = png_reciprocal(output_gamma);
+
+   /* There are really 8 possibilities here, composed of any combination
+    * of:
+    *
+    *    premultiply the color channels
+    *    do not encode non-opaque pixels
+    *    encode the alpha as well as the color channels
+    *
+    * The differences disappear if the input/output ('screen') gamma is 1.0,
+    * because then the encoding is a no-op and there is only the choice of
+    * premultiplying the color channels or not.
+    *
+    * png_set_alpha_mode and png_set_background interact because both use
+    * png_compose to do the work.  Calling both is only useful when
+    * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along
+    * with a default gamma value.  Otherwise PNG_COMPOSE must not be set.
+    */
+   switch (mode)
+   {
+      case PNG_ALPHA_PNG:        /* default: png standard */
+         /* No compose, but it may be set by png_set_background! */
+         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+         break;
+
+      case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
+         compose = 1;
+         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+         /* The output is linear: */
+         output_gamma = PNG_FP_1;
+         break;
+
+      case PNG_ALPHA_OPTIMIZED:  /* associated, non-opaque pixels linear */
+         compose = 1;
+         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+         png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA;
+         /* output_gamma records the encoding of opaque pixels! */
+         break;
+
+      case PNG_ALPHA_BROKEN:     /* associated, non-linear, alpha encoded */
+         compose = 1;
+         png_ptr->transformations |= PNG_ENCODE_ALPHA;
+         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+         break;
+
+      default:
+         png_error(png_ptr, "invalid alpha mode");
+   }
+
+   /* Only set the default gamma if the file gamma has not been set (this has
+    * the side effect that the gamma in a second call to png_set_alpha_mode will
+    * be ignored.)
+    */
+   if (png_ptr->colorspace.gamma == 0)
+   {
+      png_ptr->colorspace.gamma = file_gamma;
+      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+   }
+
+   /* But always set the output gamma: */
+   png_ptr->screen_gamma = output_gamma;
+
+   /* Finally, if pre-multiplying, set the background fields to achieve the
+    * desired result.
+    */
+   if (compose != 0)
+   {
+      /* And obtain alpha pre-multiplication by composing on black: */
+      memset(&png_ptr->background, 0, (sizeof png_ptr->background));
+      png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */
+      png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;
+      png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
+
+      if ((png_ptr->transformations & PNG_COMPOSE) != 0)
+         png_error(png_ptr,
+            "conflicting calls to set alpha mode and background");
+
+      png_ptr->transformations |= PNG_COMPOSE;
+   }
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma)
+{
+   png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
+      output_gamma));
+}
+#  endif
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* Dither file to 8-bit.  Supply a palette, the current number
+ * of elements in the palette, the maximum number of elements
+ * allowed, and a histogram if possible.  If the current number
+ * of colors is greater than the maximum number, the palette will be
+ * modified to fit in the maximum number.  "full_quantize" indicates
+ * whether we need a quantizing cube set up for RGB images, or if we
+ * simply are reducing the number of colors in a paletted image.
+ */
+
+typedef struct png_dsort_struct
+{
+   struct png_dsort_struct * next;
+   png_byte left;
+   png_byte right;
+} png_dsort;
+typedef png_dsort *   png_dsortp;
+typedef png_dsort * * png_dsortpp;
+
+void PNGAPI
+png_set_quantize(png_structrp png_ptr, png_colorp palette,
+    int num_palette, int maximum_colors, png_const_uint_16p histogram,
+    int full_quantize)
+{
+   png_debug(1, "in png_set_quantize");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= PNG_QUANTIZE;
+
+   if (full_quantize == 0)
+   {
+      int i;
+
+      png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
+          (png_uint_32)(num_palette * (sizeof (png_byte))));
+      for (i = 0; i < num_palette; i++)
+         png_ptr->quantize_index[i] = (png_byte)i;
+   }
+
+   if (num_palette > maximum_colors)
+   {
+      if (histogram != NULL)
+      {
+         /* This is easy enough, just throw out the least used colors.
+          * Perhaps not the best solution, but good enough.
+          */
+
+         int i;
+
+         /* Initialize an array to sort colors */
+         png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
+             (png_uint_32)(num_palette * (sizeof (png_byte))));
+
+         /* Initialize the quantize_sort array */
+         for (i = 0; i < num_palette; i++)
+            png_ptr->quantize_sort[i] = (png_byte)i;
+
+         /* Find the least used palette entries by starting a
+          * bubble sort, and running it until we have sorted
+          * out enough colors.  Note that we don't care about
+          * sorting all the colors, just finding which are
+          * least used.
+          */
+
+         for (i = num_palette - 1; i >= maximum_colors; i--)
+         {
+            int done; /* To stop early if the list is pre-sorted */
+            int j;
+
+            done = 1;
+            for (j = 0; j < i; j++)
+            {
+               if (histogram[png_ptr->quantize_sort[j]]
+                   < histogram[png_ptr->quantize_sort[j + 1]])
+               {
+                  png_byte t;
+
+                  t = png_ptr->quantize_sort[j];
+                  png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1];
+                  png_ptr->quantize_sort[j + 1] = t;
+                  done = 0;
+               }
+            }
+
+            if (done != 0)
+               break;
+         }
+
+         /* Swap the palette around, and set up a table, if necessary */
+         if (full_quantize != 0)
+         {
+            int j = num_palette;
+
+            /* Put all the useful colors within the max, but don't
+             * move the others.
+             */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
+               {
+                  do
+                     j--;
+                  while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
+
+                  palette[i] = palette[j];
+               }
+            }
+         }
+         else
+         {
+            int j = num_palette;
+
+            /* Move all the used colors inside the max limit, and
+             * develop a translation table.
+             */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               /* Only move the colors we need to */
+               if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
+               {
+                  png_color tmp_color;
+
+                  do
+                     j--;
+                  while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
+
+                  tmp_color = palette[j];
+                  palette[j] = palette[i];
+                  palette[i] = tmp_color;
+                  /* Indicate where the color went */
+                  png_ptr->quantize_index[j] = (png_byte)i;
+                  png_ptr->quantize_index[i] = (png_byte)j;
+               }
+            }
+
+            /* Find closest color for those colors we are not using */
+            for (i = 0; i < num_palette; i++)
+            {
+               if ((int)png_ptr->quantize_index[i] >= maximum_colors)
+               {
+                  int min_d, k, min_k, d_index;
+
+                  /* Find the closest color to one we threw out */
+                  d_index = png_ptr->quantize_index[i];
+                  min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
+                  for (k = 1, min_k = 0; k < maximum_colors; k++)
+                  {
+                     int d;
+
+                     d = PNG_COLOR_DIST(palette[d_index], palette[k]);
+
+                     if (d < min_d)
+                     {
+                        min_d = d;
+                        min_k = k;
+                     }
+                  }
+                  /* Point to closest color */
+                  png_ptr->quantize_index[i] = (png_byte)min_k;
+               }
+            }
+         }
+         png_free(png_ptr, png_ptr->quantize_sort);
+         png_ptr->quantize_sort = NULL;
+      }
+      else
+      {
+         /* This is much harder to do simply (and quickly).  Perhaps
+          * we need to go through a median cut routine, but those
+          * don't always behave themselves with only a few colors
+          * as input.  So we will just find the closest two colors,
+          * and throw out one of them (chosen somewhat randomly).
+          * [We don't understand this at all, so if someone wants to
+          *  work on improving it, be our guest - AED, GRP]
+          */
+         int i;
+         int max_d;
+         int num_new_palette;
+         png_dsortp t;
+         png_dsortpp hash;
+
+         t = NULL;
+
+         /* Initialize palette index arrays */
+         png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
+             (png_uint_32)(num_palette * (sizeof (png_byte))));
+         png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
+             (png_uint_32)(num_palette * (sizeof (png_byte))));
+
+         /* Initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+         {
+            png_ptr->index_to_palette[i] = (png_byte)i;
+            png_ptr->palette_to_index[i] = (png_byte)i;
+         }
+
+         hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 *
+             (sizeof (png_dsortp))));
+
+         num_new_palette = num_palette;
+
+         /* Initial wild guess at how far apart the farthest pixel
+          * pair we will be eliminating will be.  Larger
+          * numbers mean more areas will be allocated, Smaller
+          * numbers run the risk of not saving enough data, and
+          * having to do this all over again.
+          *
+          * I have not done extensive checking on this number.
+          */
+         max_d = 96;
+
+         while (num_new_palette > maximum_colors)
+         {
+            for (i = 0; i < num_new_palette - 1; i++)
+            {
+               int j;
+
+               for (j = i + 1; j < num_new_palette; j++)
+               {
+                  int d;
+
+                  d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+                  if (d <= max_d)
+                  {
+
+                     t = (png_dsortp)png_malloc_warn(png_ptr,
+                         (png_uint_32)(sizeof (png_dsort)));
+
+                     if (t == NULL)
+                         break;
+
+                     t->next = hash[d];
+                     t->left = (png_byte)i;
+                     t->right = (png_byte)j;
+                     hash[d] = t;
+                  }
+               }
+               if (t == NULL)
+                  break;
+            }
+
+            if (t != NULL)
+            for (i = 0; i <= max_d; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p;
+
+                  for (p = hash[i]; p; p = p->next)
+                  {
+                     if ((int)png_ptr->index_to_palette[p->left]
+                         < num_new_palette &&
+                         (int)png_ptr->index_to_palette[p->right]
+                         < num_new_palette)
+                     {
+                        int j, next_j;
+
+                        if (num_new_palette & 0x01)
+                        {
+                           j = p->left;
+                           next_j = p->right;
+                        }
+                        else
+                        {
+                           j = p->right;
+                           next_j = p->left;
+                        }
+
+                        num_new_palette--;
+                        palette[png_ptr->index_to_palette[j]]
+                            = palette[num_new_palette];
+                        if (full_quantize == 0)
+                        {
+                           int k;
+
+                           for (k = 0; k < num_palette; k++)
+                           {
+                              if (png_ptr->quantize_index[k] ==
+                                  png_ptr->index_to_palette[j])
+                                 png_ptr->quantize_index[k] =
+                                     png_ptr->index_to_palette[next_j];
+
+                              if ((int)png_ptr->quantize_index[k] ==
+                                  num_new_palette)
+                                 png_ptr->quantize_index[k] =
+                                     png_ptr->index_to_palette[j];
+                           }
+                        }
+
+                        png_ptr->index_to_palette[png_ptr->palette_to_index
+                            [num_new_palette]] = png_ptr->index_to_palette[j];
+
+                        png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
+                            = png_ptr->palette_to_index[num_new_palette];
+
+                        png_ptr->index_to_palette[j] =
+                            (png_byte)num_new_palette;
+
+                        png_ptr->palette_to_index[num_new_palette] =
+                            (png_byte)j;
+                     }
+                     if (num_new_palette <= maximum_colors)
+                        break;
+                  }
+                  if (num_new_palette <= maximum_colors)
+                     break;
+               }
+            }
+
+            for (i = 0; i < 769; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p = hash[i];
+                  while (p)
+                  {
+                     t = p->next;
+                     png_free(png_ptr, p);
+                     p = t;
+                  }
+               }
+               hash[i] = 0;
+            }
+            max_d += 96;
+         }
+         png_free(png_ptr, hash);
+         png_free(png_ptr, png_ptr->palette_to_index);
+         png_free(png_ptr, png_ptr->index_to_palette);
+         png_ptr->palette_to_index = NULL;
+         png_ptr->index_to_palette = NULL;
+      }
+      num_palette = maximum_colors;
+   }
+   if (png_ptr->palette == NULL)
+   {
+      png_ptr->palette = palette;
+   }
+   png_ptr->num_palette = (png_uint_16)num_palette;
+
+   if (full_quantize != 0)
+   {
+      int i;
+      png_bytep distance;
+      int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS +
+          PNG_QUANTIZE_BLUE_BITS;
+      int num_red = (1 << PNG_QUANTIZE_RED_BITS);
+      int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);
+      int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);
+      png_size_t num_entries = ((png_size_t)1 << total_bits);
+
+      png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
+          (png_uint_32)(num_entries * (sizeof (png_byte))));
+
+      distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
+          (sizeof (png_byte))));
+
+      memset(distance, 0xff, num_entries * (sizeof (png_byte)));
+
+      for (i = 0; i < num_palette; i++)
+      {
+         int ir, ig, ib;
+         int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));
+         int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));
+         int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));
+
+         for (ir = 0; ir < num_red; ir++)
+         {
+            /* int dr = abs(ir - r); */
+            int dr = ((ir > r) ? ir - r : r - ir);
+            int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
+                PNG_QUANTIZE_GREEN_BITS));
+
+            for (ig = 0; ig < num_green; ig++)
+            {
+               /* int dg = abs(ig - g); */
+               int dg = ((ig > g) ? ig - g : g - ig);
+               int dt = dr + dg;
+               int dm = ((dr > dg) ? dr : dg);
+               int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);
+
+               for (ib = 0; ib < num_blue; ib++)
+               {
+                  int d_index = index_g | ib;
+                  /* int db = abs(ib - b); */
+                  int db = ((ib > b) ? ib - b : b - ib);
+                  int dmax = ((dm > db) ? dm : db);
+                  int d = dmax + dt + db;
+
+                  if (d < (int)distance[d_index])
+                  {
+                     distance[d_index] = (png_byte)d;
+                     png_ptr->palette_lookup[d_index] = (png_byte)i;
+                  }
+               }
+            }
+         }
+      }
+
+      png_free(png_ptr, distance);
+   }
+}
+#endif /* READ_QUANTIZE */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+void PNGFAPI
+png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma,
+   png_fixed_point file_gamma)
+{
+   png_debug(1, "in png_set_gamma_fixed");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   /* New in libpng-1.5.4 - reserve particular negative values as flags. */
+   scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
+   file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
+
+   /* Checking the gamma values for being >0 was added in 1.5.4 along with the
+    * premultiplied alpha support; this actually hides an undocumented feature
+    * of the previous implementation which allowed gamma processing to be
+    * disabled in background handling.  There is no evidence (so far) that this
+    * was being used; however, png_set_background itself accepted and must still
+    * accept '0' for the gamma value it takes, because it isn't always used.
+    *
+    * Since this is an API change (albeit a very minor one that removes an
+    * undocumented API feature) the following checks were only enabled in
+    * libpng-1.6.0.
+    */
+   if (file_gamma <= 0)
+      png_error(png_ptr, "invalid file gamma in png_set_gamma");
+
+   if (scrn_gamma <= 0)
+      png_error(png_ptr, "invalid screen gamma in png_set_gamma");
+
+   /* Set the gamma values unconditionally - this overrides the value in the PNG
+    * file if a gAMA chunk was present.  png_set_alpha_mode provides a
+    * different, easier, way to default the file gamma.
+    */
+   png_ptr->colorspace.gamma = file_gamma;
+   png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+   png_ptr->screen_gamma = scrn_gamma;
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma)
+{
+   png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),
+      convert_gamma_value(png_ptr, file_gamma));
+}
+#  endif /* FLOATING_POINT */
+#endif /* READ_GAMMA */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expand paletted images to RGB, expand grayscale images of
+ * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
+ * to alpha channels.
+ */
+void PNGAPI
+png_set_expand(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_expand");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+}
+
+/* GRR 19990627:  the following three functions currently are identical
+ *  to png_set_expand().  However, it is entirely reasonable that someone
+ *  might wish to expand an indexed image to RGB but *not* expand a single,
+ *  fully transparent palette entry to a full alpha channel--perhaps instead
+ *  convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
+ *  the transparent color with a particular RGB value, or drop tRNS entirely.
+ *  IOW, a future version of the library may make the transformations flag
+ *  a bit more fine-grained, with separate bits for each of these three
+ *  functions.
+ *
+ *  More to the point, these functions make it obvious what libpng will be
+ *  doing, whereas "expand" can (and does) mean any number of things.
+ *
+ *  GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified
+ *  to expand only the sample depth but not to expand the tRNS to alpha
+ *  and its name was changed to png_set_expand_gray_1_2_4_to_8().
+ */
+
+/* Expand paletted images to RGB. */
+void PNGAPI
+png_set_palette_to_rgb(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_palette_to_rgb");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+}
+
+/* Expand grayscale images of less than 8-bit depth to 8 bits. */
+void PNGAPI
+png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_expand_gray_1_2_4_to_8");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= PNG_EXPAND;
+}
+
+/* Expand tRNS chunks to alpha channels. */
+void PNGAPI
+png_set_tRNS_to_alpha(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_tRNS_to_alpha");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+}
+#endif /* READ_EXPAND */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise
+ * it may not work correctly.)
+ */
+void PNGAPI
+png_set_expand_16(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_expand_16");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
+}
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+void PNGAPI
+png_set_gray_to_rgb(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_gray_to_rgb");
+
+   if (png_rtran_ok(png_ptr, 0) == 0)
+      return;
+
+   /* Because rgb must be 8 bits or more: */
+   png_set_expand_gray_1_2_4_to_8(png_ptr);
+   png_ptr->transformations |= PNG_GRAY_TO_RGB;
+}
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+void PNGFAPI
+png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
+    png_fixed_point red, png_fixed_point green)
+{
+   png_debug(1, "in png_set_rgb_to_gray");
+
+   /* Need the IHDR here because of the check on color_type below. */
+   /* TODO: fix this */
+   if (png_rtran_ok(png_ptr, 1) == 0)
+      return;
+
+   switch (error_action)
+   {
+      case PNG_ERROR_ACTION_NONE:
+         png_ptr->transformations |= PNG_RGB_TO_GRAY;
+         break;
+
+      case PNG_ERROR_ACTION_WARN:
+         png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
+         break;
+
+      case PNG_ERROR_ACTION_ERROR:
+         png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
+         break;
+
+      default:
+         png_error(png_ptr, "invalid error action to rgb_to_gray");
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#ifdef PNG_READ_EXPAND_SUPPORTED
+      png_ptr->transformations |= PNG_EXPAND;
+#else
+   {
+      /* Make this an error in 1.6 because otherwise the application may assume
+       * that it just worked and get a memory overwrite.
+       */
+      png_error(png_ptr,
+        "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
+
+      /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */
+   }
+#endif
+   {
+      if (red >= 0 && green >= 0 && red + green <= PNG_FP_1)
+      {
+         png_uint_16 red_int, green_int;
+
+         /* NOTE: this calculation does not round, but this behavior is retained
+          * for consistency; the inaccuracy is very small.  The code here always
+          * overwrites the coefficients, regardless of whether they have been
+          * defaulted or set already.
+          */
+         red_int = (png_uint_16)(((png_uint_32)red*32768)/100000);
+         green_int = (png_uint_16)(((png_uint_32)green*32768)/100000);
+
+         png_ptr->rgb_to_gray_red_coeff   = red_int;
+         png_ptr->rgb_to_gray_green_coeff = green_int;
+         png_ptr->rgb_to_gray_coefficients_set = 1;
+      }
+
+      else
+      {
+         if (red >= 0 && green >= 0)
+            png_app_warning(png_ptr,
+               "ignoring out of range rgb_to_gray coefficients");
+
+         /* Use the defaults, from the cHRM chunk if set, else the historical
+          * values which are close to the sRGB/HDTV/ITU-Rec 709 values.  See
+          * png_do_rgb_to_gray for more discussion of the values.  In this case
+          * the coefficients are not marked as 'set' and are not overwritten if
+          * something has already provided a default.
+          */
+         if (png_ptr->rgb_to_gray_red_coeff == 0 &&
+            png_ptr->rgb_to_gray_green_coeff == 0)
+         {
+            png_ptr->rgb_to_gray_red_coeff   = 6968;
+            png_ptr->rgb_to_gray_green_coeff = 23434;
+            /* png_ptr->rgb_to_gray_blue_coeff  = 2366; */
+         }
+      }
+   }
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+/* Convert a RGB image to a grayscale of the same width.  This allows us,
+ * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
+ */
+
+void PNGAPI
+png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
+   double green)
+{
+   png_set_rgb_to_gray_fixed(png_ptr, error_action,
+      png_fixed(png_ptr, red, "rgb to gray red coefficient"),
+      png_fixed(png_ptr, green, "rgb to gray green coefficient"));
+}
+#endif /* FLOATING POINT */
+
+#endif /* RGB_TO_GRAY */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+void PNGAPI
+png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
+    read_user_transform_fn)
+{
+   png_debug(1, "in png_set_read_user_transform_fn");
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->read_user_transform_fn = read_user_transform_fn;
+#endif
+}
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* In the case of gamma transformations only do transformations on images where
+ * the [file] gamma and screen_gamma are not close reciprocals, otherwise it
+ * slows things down slightly, and also needlessly introduces small errors.
+ */
+static int /* PRIVATE */
+png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma)
+{
+   /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
+    * correction as a difference of the overall transform from 1.0
+    *
+    * We want to compare the threshold with s*f - 1, if we get
+    * overflow here it is because of wacky gamma values so we
+    * turn on processing anyway.
+    */
+   png_fixed_point gtest;
+   return !png_muldiv(&gtest, screen_gamma, file_gamma, PNG_FP_1) ||
+       png_gamma_significant(gtest);
+}
+#endif
+
+/* Initialize everything needed for the read.  This includes modifying
+ * the palette.
+ */
+
+/* For the moment 'png_init_palette_transformations' and
+ * 'png_init_rgb_transformations' only do some flag canceling optimizations.
+ * The intent is that these two routines should have palette or rgb operations
+ * extracted from 'png_init_read_transformations'.
+ */
+static void /* PRIVATE */
+png_init_palette_transformations(png_structrp png_ptr)
+{
+   /* Called to handle the (input) palette case.  In png_do_read_transformations
+    * the first step is to expand the palette if requested, so this code must
+    * take care to only make changes that are invariant with respect to the
+    * palette expansion, or only do them if there is no expansion.
+    *
+    * STRIP_ALPHA has already been handled in the caller (by setting num_trans
+    * to 0.)
+    */
+   int input_has_alpha = 0;
+   int input_has_transparency = 0;
+
+   if (png_ptr->num_trans > 0)
+   {
+      int i;
+
+      /* Ignore if all the entries are opaque (unlikely!) */
+      for (i=0; i<png_ptr->num_trans; ++i)
+      {
+         if (png_ptr->trans_alpha[i] == 255)
+            continue;
+         else if (png_ptr->trans_alpha[i] == 0)
+            input_has_transparency = 1;
+         else
+         {
+            input_has_transparency = 1;
+            input_has_alpha = 1;
+            break;
+         }
+      }
+   }
+
+   /* If no alpha we can optimize. */
+   if (input_has_alpha == 0)
+   {
+      /* Any alpha means background and associative alpha processing is
+       * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA
+       * and ENCODE_ALPHA are irrelevant.
+       */
+      png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+
+      if (input_has_transparency == 0)
+         png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
+   }
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* png_set_background handling - deals with the complexity of whether the
+    * background color is in the file format or the screen format in the case
+    * where an 'expand' will happen.
+    */
+
+   /* The following code cannot be entered in the alpha pre-multiplication case
+    * because PNG_BACKGROUND_EXPAND is cancelled below.
+    */
+   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 &&
+       (png_ptr->transformations & PNG_EXPAND) != 0)
+   {
+      {
+         png_ptr->background.red   =
+             png_ptr->palette[png_ptr->background.index].red;
+         png_ptr->background.green =
+             png_ptr->palette[png_ptr->background.index].green;
+         png_ptr->background.blue  =
+             png_ptr->palette[png_ptr->background.index].blue;
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+        if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
+        {
+           if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
+           {
+              /* Invert the alpha channel (in tRNS) unless the pixels are
+               * going to be expanded, in which case leave it for later
+               */
+              int i, istop = png_ptr->num_trans;
+
+              for (i=0; i<istop; i++)
+                 png_ptr->trans_alpha[i] = (png_byte)(255 -
+                    png_ptr->trans_alpha[i]);
+           }
+        }
+#endif /* READ_INVERT_ALPHA */
+      }
+   } /* background expand and (therefore) no alpha association. */
+#endif /* READ_EXPAND && READ_BACKGROUND */
+}
+
+static void /* PRIVATE */
+png_init_rgb_transformations(png_structrp png_ptr)
+{
+   /* Added to libpng-1.5.4: check the color type to determine whether there
+    * is any alpha or transparency in the image and simply cancel the
+    * background and alpha mode stuff if there isn't.
+    */
+   int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0;
+   int input_has_transparency = png_ptr->num_trans > 0;
+
+   /* If no alpha we can optimize. */
+   if (input_has_alpha == 0)
+   {
+      /* Any alpha means background and associative alpha processing is
+       * required, however if the alpha is 0 or 1 throughout OPTIMIZE_ALPHA
+       * and ENCODE_ALPHA are irrelevant.
+       */
+#     ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+         png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+         png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+#     endif
+
+      if (input_has_transparency == 0)
+         png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
+   }
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* png_set_background handling - deals with the complexity of whether the
+    * background color is in the file format or the screen format in the case
+    * where an 'expand' will happen.
+    */
+
+   /* The following code cannot be entered in the alpha pre-multiplication case
+    * because PNG_BACKGROUND_EXPAND is cancelled below.
+    */
+   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0 &&
+       (png_ptr->transformations & PNG_EXPAND) != 0 &&
+       (png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
+       /* i.e., GRAY or GRAY_ALPHA */
+   {
+      {
+         /* Expand background and tRNS chunks */
+         int gray = png_ptr->background.gray;
+         int trans_gray = png_ptr->trans_color.gray;
+
+         switch (png_ptr->bit_depth)
+         {
+            case 1:
+               gray *= 0xff;
+               trans_gray *= 0xff;
+               break;
+
+            case 2:
+               gray *= 0x55;
+               trans_gray *= 0x55;
+               break;
+
+            case 4:
+               gray *= 0x11;
+               trans_gray *= 0x11;
+               break;
+
+            default:
+
+            case 8:
+               /* FALL THROUGH (Already 8 bits) */
+
+            case 16:
+               /* Already a full 16 bits */
+               break;
+         }
+
+         png_ptr->background.red = png_ptr->background.green =
+            png_ptr->background.blue = (png_uint_16)gray;
+
+         if ((png_ptr->transformations & PNG_EXPAND_tRNS) == 0)
+         {
+            png_ptr->trans_color.red = png_ptr->trans_color.green =
+               png_ptr->trans_color.blue = (png_uint_16)trans_gray;
+         }
+      }
+   } /* background expand and (therefore) no alpha association. */
+#endif /* READ_EXPAND && READ_BACKGROUND */
+}
+
+void /* PRIVATE */
+png_init_read_transformations(png_structrp png_ptr)
+{
+   png_debug(1, "in png_init_read_transformations");
+
+   /* This internal function is called from png_read_start_row in pngrutil.c
+    * and it is called before the 'rowbytes' calculation is done, so the code
+    * in here can change or update the transformations flags.
+    *
+    * First do updates that do not depend on the details of the PNG image data
+    * being processed.
+    */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds
+    * png_set_alpha_mode and this is another source for a default file gamma so
+    * the test needs to be performed later - here.  In addition prior to 1.5.4
+    * the tests were repeated for the PALETTE color type here - this is no
+    * longer necessary (and doesn't seem to have been necessary before.)
+    */
+   {
+      /* The following temporary indicates if overall gamma correction is
+       * required.
+       */
+      int gamma_correction = 0;
+
+      if (png_ptr->colorspace.gamma != 0) /* has been set */
+      {
+         if (png_ptr->screen_gamma != 0) /* screen set too */
+            gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma,
+               png_ptr->screen_gamma);
+
+         else
+            /* Assume the output matches the input; a long time default behavior
+             * of libpng, although the standard has nothing to say about this.
+             */
+            png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma);
+      }
+
+      else if (png_ptr->screen_gamma != 0)
+         /* The converse - assume the file matches the screen, note that this
+          * perhaps undesireable default can (from 1.5.4) be changed by calling
+          * png_set_alpha_mode (even if the alpha handling mode isn't required
+          * or isn't changed from the default.)
+          */
+         png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma);
+
+      else /* neither are set */
+         /* Just in case the following prevents any processing - file and screen
+          * are both assumed to be linear and there is no way to introduce a
+          * third gamma value other than png_set_background with 'UNIQUE', and,
+          * prior to 1.5.4
+          */
+         png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1;
+
+      /* We have a gamma value now. */
+      png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA;
+
+      /* Now turn the gamma transformation on or off as appropriate.  Notice
+       * that PNG_GAMMA just refers to the file->screen correction.  Alpha
+       * composition may independently cause gamma correction because it needs
+       * linear data (e.g. if the file has a gAMA chunk but the screen gamma
+       * hasn't been specified.)  In any case this flag may get turned off in
+       * the code immediately below if the transform can be handled outside the
+       * row loop.
+       */
+      if (gamma_correction != 0)
+         png_ptr->transformations |= PNG_GAMMA;
+
+      else
+         png_ptr->transformations &= ~PNG_GAMMA;
+   }
+#endif
+
+   /* Certain transformations have the effect of preventing other
+    * transformations that happen afterward in png_do_read_transformations;
+    * resolve the interdependencies here.  From the code of
+    * png_do_read_transformations the order is:
+    *
+    *  1) PNG_EXPAND (including PNG_EXPAND_tRNS)
+    *  2) PNG_STRIP_ALPHA (if no compose)
+    *  3) PNG_RGB_TO_GRAY
+    *  4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY
+    *  5) PNG_COMPOSE
+    *  6) PNG_GAMMA
+    *  7) PNG_STRIP_ALPHA (if compose)
+    *  8) PNG_ENCODE_ALPHA
+    *  9) PNG_SCALE_16_TO_8
+    * 10) PNG_16_TO_8
+    * 11) PNG_QUANTIZE (converts to palette)
+    * 12) PNG_EXPAND_16
+    * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY
+    * 14) PNG_INVERT_MONO
+    * 15) PNG_INVERT_ALPHA
+    * 16) PNG_SHIFT
+    * 17) PNG_PACK
+    * 18) PNG_BGR
+    * 19) PNG_PACKSWAP
+    * 20) PNG_FILLER (includes PNG_ADD_ALPHA)
+    * 21) PNG_SWAP_ALPHA
+    * 22) PNG_SWAP_BYTES
+    * 23) PNG_USER_TRANSFORM [must be last]
+    */
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
+       (png_ptr->transformations & PNG_COMPOSE) == 0)
+   {
+      /* Stripping the alpha channel happens immediately after the 'expand'
+       * transformations, before all other transformation, so it cancels out
+       * the alpha handling.  It has the side effect negating the effect of
+       * PNG_EXPAND_tRNS too:
+       */
+      png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA |
+         PNG_EXPAND_tRNS);
+      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+
+      /* Kill the tRNS chunk itself too.  Prior to 1.5.4 this did not happen
+       * so transparency information would remain just so long as it wasn't
+       * expanded.  This produces unexpected API changes if the set of things
+       * that do PNG_EXPAND_tRNS changes (perfectly possible given the
+       * documentation - which says ask for what you want, accept what you
+       * get.)  This makes the behavior consistent from 1.5.4:
+       */
+      png_ptr->num_trans = 0;
+   }
+#endif /* STRIP_ALPHA supported, no COMPOSE */
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+   /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA
+    * settings will have no effect.
+    */
+   if (png_gamma_significant(png_ptr->screen_gamma) == 0)
+   {
+      png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+      png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+   }
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+   /* Make sure the coefficients for the rgb to gray conversion are set
+    * appropriately.
+    */
+   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
+      png_colorspace_set_rgb_coefficients(png_ptr);
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* Detect gray background and attempt to enable optimization for
+    * gray --> RGB case.
+    *
+    * Note:  if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or
+    * RGB_ALPHA (in which case need_expand is superfluous anyway), the
+    * background color might actually be gray yet not be flagged as such.
+    * This is not a problem for the current code, which uses
+    * PNG_BACKGROUND_IS_GRAY only to decide when to do the
+    * png_do_gray_to_rgb() transformation.
+    *
+    * TODO: this code needs to be revised to avoid the complexity and
+    * interdependencies.  The color type of the background should be recorded in
+    * png_set_background, along with the bit depth, then the code has a record
+    * of exactly what color space the background is currently in.
+    */
+   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) != 0)
+   {
+      /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if
+       * the file was grayscale the background value is gray.
+       */
+      if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
+         png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+   }
+
+   else if ((png_ptr->transformations & PNG_COMPOSE) != 0)
+   {
+      /* PNG_COMPOSE: png_set_background was called with need_expand false,
+       * so the color is in the color space of the output or png_set_alpha_mode
+       * was called and the color is black.  Ignore RGB_TO_GRAY because that
+       * happens before GRAY_TO_RGB.
+       */
+      if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
+      {
+         if (png_ptr->background.red == png_ptr->background.green &&
+             png_ptr->background.red == png_ptr->background.blue)
+         {
+            png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+            png_ptr->background.gray = png_ptr->background.red;
+         }
+      }
+   }
+#endif /* READ_EXPAND && READ_BACKGROUND */
+#endif /* READ_GRAY_TO_RGB */
+
+   /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations
+    * can be performed directly on the palette, and some (such as rgb to gray)
+    * can be optimized inside the palette.  This is particularly true of the
+    * composite (background and alpha) stuff, which can be pretty much all done
+    * in the palette even if the result is expanded to RGB or gray afterward.
+    *
+    * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and
+    * earlier and the palette stuff is actually handled on the first row.  This
+    * leads to the reported bug that the palette returned by png_get_PLTE is not
+    * updated.
+    */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      png_init_palette_transformations(png_ptr);
+
+   else
+      png_init_rgb_transformations(png_ptr);
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+   defined(PNG_READ_EXPAND_16_SUPPORTED)
+   if ((png_ptr->transformations & PNG_EXPAND_16) != 0 &&
+       (png_ptr->transformations & PNG_COMPOSE) != 0 &&
+       (png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 &&
+       png_ptr->bit_depth != 16)
+   {
+      /* TODO: fix this.  Because the expand_16 operation is after the compose
+       * handling the background color must be 8, not 16, bits deep, but the
+       * application will supply a 16-bit value so reduce it here.
+       *
+       * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at
+       * present, so that case is ok (until do_expand_16 is moved.)
+       *
+       * NOTE: this discards the low 16 bits of the user supplied background
+       * color, but until expand_16 works properly there is no choice!
+       */
+#     define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x))
+      CHOP(png_ptr->background.red);
+      CHOP(png_ptr->background.green);
+      CHOP(png_ptr->background.blue);
+      CHOP(png_ptr->background.gray);
+#     undef CHOP
+   }
+#endif /* READ_BACKGROUND && READ_EXPAND_16 */
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+   (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \
+   defined(PNG_READ_STRIP_16_TO_8_SUPPORTED))
+   if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) != 0 &&
+       (png_ptr->transformations & PNG_COMPOSE) != 0 &&
+       (png_ptr->transformations & PNG_BACKGROUND_EXPAND) == 0 &&
+       png_ptr->bit_depth == 16)
+   {
+      /* On the other hand, if a 16-bit file is to be reduced to 8-bits per
+       * component this will also happen after PNG_COMPOSE and so the background
+       * color must be pre-expanded here.
+       *
+       * TODO: fix this too.
+       */
+      png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257);
+      png_ptr->background.green =
+         (png_uint_16)(png_ptr->background.green * 257);
+      png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257);
+      png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257);
+   }
+#endif
+
+   /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the
+    * background support (see the comments in scripts/pnglibconf.dfa), this
+    * allows pre-multiplication of the alpha channel to be implemented as
+    * compositing on black.  This is probably sub-optimal and has been done in
+    * 1.5.4 betas simply to enable external critique and testing (i.e. to
+    * implement the new API quickly, without lots of internal changes.)
+    */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+#  ifdef PNG_READ_BACKGROUND_SUPPORTED
+      /* Includes ALPHA_MODE */
+      png_ptr->background_1 = png_ptr->background;
+#  endif
+
+   /* This needs to change - in the palette image case a whole set of tables are
+    * built when it would be quicker to just calculate the correct value for
+    * each palette entry directly.  Also, the test is too tricky - why check
+    * PNG_RGB_TO_GRAY if PNG_GAMMA is not set?  The answer seems to be that
+    * PNG_GAMMA is cancelled even if the gamma is known?  The test excludes the
+    * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction
+    * the gamma tables will not be built even if composition is required on a
+    * gamma encoded value.
+    *
+    * In 1.5.4 this is addressed below by an additional check on the individual
+    * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the
+    * tables.
+    */
+   if ((png_ptr->transformations & PNG_GAMMA) != 0 ||
+       ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0 &&
+        (png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
+         png_gamma_significant(png_ptr->screen_gamma) != 0)) ||
+        ((png_ptr->transformations & PNG_COMPOSE) != 0 &&
+         (png_gamma_significant(png_ptr->colorspace.gamma) != 0 ||
+          png_gamma_significant(png_ptr->screen_gamma) != 0
+#  ifdef PNG_READ_BACKGROUND_SUPPORTED
+         || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE &&
+           png_gamma_significant(png_ptr->background_gamma) != 0)
+#  endif
+        )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 &&
+       png_gamma_significant(png_ptr->screen_gamma) != 0))
+   {
+      png_build_gamma_table(png_ptr, png_ptr->bit_depth);
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+      if ((png_ptr->transformations & PNG_COMPOSE) != 0)
+      {
+         /* Issue a warning about this combination: because RGB_TO_GRAY is
+          * optimized to do the gamma transform if present yet do_background has
+          * to do the same thing if both options are set a
+          * double-gamma-correction happens.  This is true in all versions of
+          * libpng to date.
+          */
+         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
+            png_warning(png_ptr,
+               "libpng does not support gamma+background+rgb_to_gray");
+
+         if ((png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) != 0)
+         {
+            /* We don't get to here unless there is a tRNS chunk with non-opaque
+             * entries - see the checking code at the start of this function.
+             */
+            png_color back, back_1;
+            png_colorp palette = png_ptr->palette;
+            int num_palette = png_ptr->num_palette;
+            int i;
+            if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+            {
+
+               back.red = png_ptr->gamma_table[png_ptr->background.red];
+               back.green = png_ptr->gamma_table[png_ptr->background.green];
+               back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+               back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+               back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+               back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+            }
+            else
+            {
+               png_fixed_point g, gs;
+
+               switch (png_ptr->background_gamma_type)
+               {
+                  case PNG_BACKGROUND_GAMMA_SCREEN:
+                     g = (png_ptr->screen_gamma);
+                     gs = PNG_FP_1;
+                     break;
+
+                  case PNG_BACKGROUND_GAMMA_FILE:
+                     g = png_reciprocal(png_ptr->colorspace.gamma);
+                     gs = png_reciprocal2(png_ptr->colorspace.gamma,
+                        png_ptr->screen_gamma);
+                     break;
+
+                  case PNG_BACKGROUND_GAMMA_UNIQUE:
+                     g = png_reciprocal(png_ptr->background_gamma);
+                     gs = png_reciprocal2(png_ptr->background_gamma,
+                        png_ptr->screen_gamma);
+                     break;
+                  default:
+                     g = PNG_FP_1;    /* back_1 */
+                     gs = PNG_FP_1;   /* back */
+                     break;
+               }
+
+               if (png_gamma_significant(gs) != 0)
+               {
+                  back.red = png_gamma_8bit_correct(png_ptr->background.red,
+                      gs);
+                  back.green = png_gamma_8bit_correct(png_ptr->background.green,
+                      gs);
+                  back.blue = png_gamma_8bit_correct(png_ptr->background.blue,
+                      gs);
+               }
+
+               else
+               {
+                  back.red   = (png_byte)png_ptr->background.red;
+                  back.green = (png_byte)png_ptr->background.green;
+                  back.blue  = (png_byte)png_ptr->background.blue;
+               }
+
+               if (png_gamma_significant(g) != 0)
+               {
+                  back_1.red = png_gamma_8bit_correct(png_ptr->background.red,
+                     g);
+                  back_1.green = png_gamma_8bit_correct(
+                     png_ptr->background.green, g);
+                  back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,
+                     g);
+               }
+
+               else
+               {
+                  back_1.red   = (png_byte)png_ptr->background.red;
+                  back_1.green = (png_byte)png_ptr->background.green;
+                  back_1.blue  = (png_byte)png_ptr->background.blue;
+               }
+            }
+
+            for (i = 0; i < num_palette; i++)
+            {
+               if (i < (int)png_ptr->num_trans &&
+                   png_ptr->trans_alpha[i] != 0xff)
+               {
+                  if (png_ptr->trans_alpha[i] == 0)
+                  {
+                     palette[i] = back;
+                  }
+                  else /* if (png_ptr->trans_alpha[i] != 0xff) */
+                  {
+                     png_byte v, w;
+
+                     v = png_ptr->gamma_to_1[palette[i].red];
+                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.red);
+                     palette[i].red = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].green];
+                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.green);
+                     palette[i].green = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].blue];
+                     png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue);
+                     palette[i].blue = png_ptr->gamma_from_1[w];
+                  }
+               }
+               else
+               {
+                  palette[i].red = png_ptr->gamma_table[palette[i].red];
+                  palette[i].green = png_ptr->gamma_table[palette[i].green];
+                  palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+               }
+            }
+
+            /* Prevent the transformations being done again.
+             *
+             * NOTE: this is highly dubious; it removes the transformations in
+             * place.  This seems inconsistent with the general treatment of the
+             * transformations elsewhere.
+             */
+            png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
+         } /* color_type == PNG_COLOR_TYPE_PALETTE */
+
+         /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
+         else /* color_type != PNG_COLOR_TYPE_PALETTE */
+         {
+            int gs_sig, g_sig;
+            png_fixed_point g = PNG_FP_1;  /* Correction to linear */
+            png_fixed_point gs = PNG_FP_1; /* Correction to screen */
+
+            switch (png_ptr->background_gamma_type)
+            {
+               case PNG_BACKGROUND_GAMMA_SCREEN:
+                  g = png_ptr->screen_gamma;
+                  /* gs = PNG_FP_1; */
+                  break;
+
+               case PNG_BACKGROUND_GAMMA_FILE:
+                  g = png_reciprocal(png_ptr->colorspace.gamma);
+                  gs = png_reciprocal2(png_ptr->colorspace.gamma,
+                     png_ptr->screen_gamma);
+                  break;
+
+               case PNG_BACKGROUND_GAMMA_UNIQUE:
+                  g = png_reciprocal(png_ptr->background_gamma);
+                  gs = png_reciprocal2(png_ptr->background_gamma,
+                      png_ptr->screen_gamma);
+                  break;
+
+               default:
+                  png_error(png_ptr, "invalid background gamma type");
+            }
+
+            g_sig = png_gamma_significant(g);
+            gs_sig = png_gamma_significant(gs);
+
+            if (g_sig != 0)
+               png_ptr->background_1.gray = png_gamma_correct(png_ptr,
+                   png_ptr->background.gray, g);
+
+            if (gs_sig != 0)
+               png_ptr->background.gray = png_gamma_correct(png_ptr,
+                   png_ptr->background.gray, gs);
+
+            if ((png_ptr->background.red != png_ptr->background.green) ||
+                (png_ptr->background.red != png_ptr->background.blue) ||
+                (png_ptr->background.red != png_ptr->background.gray))
+            {
+               /* RGB or RGBA with color background */
+               if (g_sig != 0)
+               {
+                  png_ptr->background_1.red = png_gamma_correct(png_ptr,
+                      png_ptr->background.red, g);
+
+                  png_ptr->background_1.green = png_gamma_correct(png_ptr,
+                      png_ptr->background.green, g);
+
+                  png_ptr->background_1.blue = png_gamma_correct(png_ptr,
+                      png_ptr->background.blue, g);
+               }
+
+               if (gs_sig != 0)
+               {
+                  png_ptr->background.red = png_gamma_correct(png_ptr,
+                      png_ptr->background.red, gs);
+
+                  png_ptr->background.green = png_gamma_correct(png_ptr,
+                      png_ptr->background.green, gs);
+
+                  png_ptr->background.blue = png_gamma_correct(png_ptr,
+                      png_ptr->background.blue, gs);
+               }
+            }
+
+            else
+            {
+               /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */
+               png_ptr->background_1.red = png_ptr->background_1.green
+                   = png_ptr->background_1.blue = png_ptr->background_1.gray;
+
+               png_ptr->background.red = png_ptr->background.green
+                   = png_ptr->background.blue = png_ptr->background.gray;
+            }
+
+            /* The background is now in screen gamma: */
+            png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN;
+         } /* color_type != PNG_COLOR_TYPE_PALETTE */
+      }/* png_ptr->transformations & PNG_BACKGROUND */
+
+      else
+      /* Transformation does not include PNG_BACKGROUND */
+#endif /* READ_BACKGROUND */
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+         /* RGB_TO_GRAY needs to have non-gamma-corrected values! */
+         && ((png_ptr->transformations & PNG_EXPAND) == 0 ||
+         (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
+#endif
+         )
+      {
+         png_colorp palette = png_ptr->palette;
+         int num_palette = png_ptr->num_palette;
+         int i;
+
+         /* NOTE: there are other transformations that should probably be in
+          * here too.
+          */
+         for (i = 0; i < num_palette; i++)
+         {
+            palette[i].red = png_ptr->gamma_table[palette[i].red];
+            palette[i].green = png_ptr->gamma_table[palette[i].green];
+            palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+         }
+
+         /* Done the gamma correction. */
+         png_ptr->transformations &= ~PNG_GAMMA;
+      } /* color_type == PALETTE && !PNG_BACKGROUND transformation */
+   }
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+   else
+#endif
+#endif /* READ_GAMMA */
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+   /* No GAMMA transformation (see the hanging else 4 lines above) */
+   if ((png_ptr->transformations & PNG_COMPOSE) != 0 &&
+       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
+   {
+      int i;
+      int istop = (int)png_ptr->num_trans;
+      png_color back;
+      png_colorp palette = png_ptr->palette;
+
+      back.red   = (png_byte)png_ptr->background.red;
+      back.green = (png_byte)png_ptr->background.green;
+      back.blue  = (png_byte)png_ptr->background.blue;
+
+      for (i = 0; i < istop; i++)
+      {
+         if (png_ptr->trans_alpha[i] == 0)
+         {
+            palette[i] = back;
+         }
+
+         else if (png_ptr->trans_alpha[i] != 0xff)
+         {
+            /* The png_composite() macro is defined in png.h */
+            png_composite(palette[i].red, palette[i].red,
+                png_ptr->trans_alpha[i], back.red);
+
+            png_composite(palette[i].green, palette[i].green,
+                png_ptr->trans_alpha[i], back.green);
+
+            png_composite(palette[i].blue, palette[i].blue,
+                png_ptr->trans_alpha[i], back.blue);
+         }
+      }
+
+      png_ptr->transformations &= ~PNG_COMPOSE;
+   }
+#endif /* READ_BACKGROUND */
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+   if ((png_ptr->transformations & PNG_SHIFT) != 0 &&
+       (png_ptr->transformations & PNG_EXPAND) == 0 &&
+       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
+   {
+      int i;
+      int istop = png_ptr->num_palette;
+      int shift = 8 - png_ptr->sig_bit.red;
+
+      png_ptr->transformations &= ~PNG_SHIFT;
+
+      /* significant bits can be in the range 1 to 7 for a meaninful result, if
+       * the number of significant bits is 0 then no shift is done (this is an
+       * error condition which is silently ignored.)
+       */
+      if (shift > 0 && shift < 8)
+         for (i=0; i<istop; ++i)
+         {
+            int component = png_ptr->palette[i].red;
+
+            component >>= shift;
+            png_ptr->palette[i].red = (png_byte)component;
+         }
+
+      shift = 8 - png_ptr->sig_bit.green;
+      if (shift > 0 && shift < 8)
+         for (i=0; i<istop; ++i)
+         {
+            int component = png_ptr->palette[i].green;
+
+            component >>= shift;
+            png_ptr->palette[i].green = (png_byte)component;
+         }
+
+      shift = 8 - png_ptr->sig_bit.blue;
+      if (shift > 0 && shift < 8)
+         for (i=0; i<istop; ++i)
+         {
+            int component = png_ptr->palette[i].blue;
+
+            component >>= shift;
+            png_ptr->palette[i].blue = (png_byte)component;
+         }
+   }
+#endif  /* READ_SHIFT */
+}
+
+/* Modify the info structure to reflect the transformations.  The
+ * info should be updated so a PNG file could be written with it,
+ * assuming the transformations result in valid PNG data.
+ */
+void /* PRIVATE */
+png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr)
+{
+   png_debug(1, "in png_read_transform_info");
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+   if ((png_ptr->transformations & PNG_EXPAND) != 0)
+   {
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         /* This check must match what actually happens in
+          * png_do_expand_palette; if it ever checks the tRNS chunk to see if
+          * it is all opaque we must do the same (at present it does not.)
+          */
+         if (png_ptr->num_trans > 0)
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+
+         else
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+
+         info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+
+         if (png_ptr->palette == NULL)
+            png_error (png_ptr, "Palette is NULL in indexed image");
+      }
+      else
+      {
+         if (png_ptr->num_trans != 0)
+         {
+            if ((png_ptr->transformations & PNG_EXPAND_tRNS) != 0)
+               info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+         }
+         if (info_ptr->bit_depth < 8)
+            info_ptr->bit_depth = 8;
+
+         info_ptr->num_trans = 0;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+   /* The following is almost certainly wrong unless the background value is in
+    * the screen space!
+    */
+   if ((png_ptr->transformations & PNG_COMPOSE) != 0)
+      info_ptr->background = png_ptr->background;
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4),
+    * however it seems that the code in png_init_read_transformations, which has
+    * been called before this from png_read_update_info->png_read_start_row
+    * sometimes does the gamma transform and cancels the flag.
+    *
+    * TODO: this looks wrong; the info_ptr should end up with a gamma equal to
+    * the screen_gamma value.  The following probably results in weirdness if
+    * the info_ptr is used by the app after the rows have been read.
+    */
+   info_ptr->colorspace.gamma = png_ptr->colorspace.gamma;
+#endif
+
+   if (info_ptr->bit_depth == 16)
+   {
+#  ifdef PNG_READ_16BIT_SUPPORTED
+#     ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+         if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0)
+            info_ptr->bit_depth = 8;
+#     endif
+
+#     ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+         if ((png_ptr->transformations & PNG_16_TO_8) != 0)
+            info_ptr->bit_depth = 8;
+#     endif
+
+#  else
+      /* No 16-bit support: force chopping 16-bit input down to 8, in this case
+       * the app program can chose if both APIs are available by setting the
+       * correct scaling to use.
+       */
+#     ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+         /* For compatibility with previous versions use the strip method by
+          * default.  This code works because if PNG_SCALE_16_TO_8 is already
+          * set the code below will do that in preference to the chop.
+          */
+         png_ptr->transformations |= PNG_16_TO_8;
+         info_ptr->bit_depth = 8;
+#     else
+
+#        ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+            png_ptr->transformations |= PNG_SCALE_16_TO_8;
+            info_ptr->bit_depth = 8;
+#        else
+
+            CONFIGURATION ERROR: you must enable at least one 16 to 8 method
+#        endif
+#    endif
+#endif /* !READ_16BIT */
+   }
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
+      info_ptr->color_type = (png_byte)(info_ptr->color_type |
+         PNG_COLOR_MASK_COLOR);
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
+      info_ptr->color_type = (png_byte)(info_ptr->color_type &
+         ~PNG_COLOR_MASK_COLOR);
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+   if ((png_ptr->transformations & PNG_QUANTIZE) != 0)
+   {
+      if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+          (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
+          png_ptr->palette_lookup != 0 && info_ptr->bit_depth == 8)
+      {
+         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+      }
+   }
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   if ((png_ptr->transformations & PNG_EXPAND_16) != 0 &&
+       info_ptr->bit_depth == 8 &&
+       info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      info_ptr->bit_depth = 16;
+   }
+#endif
+
+#ifdef PNG_READ_PACK_SUPPORTED
+   if ((png_ptr->transformations & PNG_PACK) != 0 &&
+       (info_ptr->bit_depth < 8))
+      info_ptr->bit_depth = 8;
+#endif
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+
+   else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      info_ptr->channels = 3;
+
+   else
+      info_ptr->channels = 1;
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0)
+   {
+      info_ptr->color_type = (png_byte)(info_ptr->color_type &
+         ~PNG_COLOR_MASK_ALPHA);
+      info_ptr->num_trans = 0;
+   }
+#endif
+
+   if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      info_ptr->channels++;
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+   /* STRIP_ALPHA and FILLER allowed:  MASK_ALPHA bit stripped above */
+   if ((png_ptr->transformations & PNG_FILLER) != 0 &&
+       (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+       info_ptr->color_type == PNG_COLOR_TYPE_GRAY))
+   {
+      info_ptr->channels++;
+      /* If adding a true alpha channel not just filler */
+      if ((png_ptr->transformations & PNG_ADD_ALPHA) != 0)
+         info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+   }
+#endif
+
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
+defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
+   {
+      if (png_ptr->user_transform_depth != 0)
+         info_ptr->bit_depth = png_ptr->user_transform_depth;
+
+      if (png_ptr->user_transform_channels != 0)
+         info_ptr->channels = png_ptr->user_transform_channels;
+   }
+#endif
+
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
+       info_ptr->bit_depth);
+
+   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width);
+
+   /* Adding in 1.5.4: cache the above value in png_struct so that we can later
+    * check in png_rowbytes that the user buffer won't get overwritten.  Note
+    * that the field is not always set - if png_read_update_info isn't called
+    * the application has to either not do any transforms or get the calculation
+    * right itself.
+    */
+   png_ptr->info_rowbytes = info_ptr->rowbytes;
+
+#ifndef PNG_READ_EXPAND_SUPPORTED
+   if (png_ptr != NULL)
+      return;
+#endif
+}
+
+#ifdef PNG_READ_PACK_SUPPORTED
+/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+ * without changing the actual values.  Thus, if you had a row with
+ * a bit depth of 1, you would end up with bytes that only contained
+ * the numbers 0 or 1.  If you would rather they contain 0 and 255, use
+ * png_do_shift() after this.
+ */
+static void
+png_do_unpack(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_unpack");
+
+   if (row_info->bit_depth < 8)
+   {
+      png_uint_32 i;
+      png_uint_32 row_width=row_info->width;
+
+      switch (row_info->bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x01);
+
+               if (shift == 7)
+               {
+                  shift = 0;
+                  sp--;
+               }
+
+               else
+                  shift++;
+
+               dp--;
+            }
+            break;
+         }
+
+         case 2:
+         {
+
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x03);
+
+               if (shift == 6)
+               {
+                  shift = 0;
+                  sp--;
+               }
+
+               else
+                  shift += 2;
+
+               dp--;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x0f);
+
+               if (shift == 4)
+               {
+                  shift = 0;
+                  sp--;
+               }
+
+               else
+                  shift = 4;
+
+               dp--;
+            }
+            break;
+         }
+
+         default:
+            break;
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_width * row_info->channels;
+   }
+}
+#endif
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+/* Reverse the effects of png_do_shift.  This routine merely shifts the
+ * pixels back to their significant bits values.  Thus, if you have
+ * a row of bit depth 8, but only 5 are significant, this will shift
+ * the values back to 0 through 31.
+ */
+static void
+png_do_unshift(png_row_infop row_info, png_bytep row,
+    png_const_color_8p sig_bits)
+{
+   int color_type;
+
+   png_debug(1, "in png_do_unshift");
+
+   /* The palette case has already been handled in the _init routine. */
+   color_type = row_info->color_type;
+
+   if (color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift[4];
+      int channels = 0;
+      int bit_depth = row_info->bit_depth;
+
+      if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
+      {
+         shift[channels++] = bit_depth - sig_bits->red;
+         shift[channels++] = bit_depth - sig_bits->green;
+         shift[channels++] = bit_depth - sig_bits->blue;
+      }
+
+      else
+      {
+         shift[channels++] = bit_depth - sig_bits->gray;
+      }
+
+      if ((color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      {
+         shift[channels++] = bit_depth - sig_bits->alpha;
+      }
+
+      {
+         int c, have_shift;
+
+         for (c = have_shift = 0; c < channels; ++c)
+         {
+            /* A shift of more than the bit depth is an error condition but it
+             * gets ignored here.
+             */
+            if (shift[c] <= 0 || shift[c] >= bit_depth)
+               shift[c] = 0;
+
+            else
+               have_shift = 1;
+         }
+
+         if (have_shift == 0)
+            return;
+      }
+
+      switch (bit_depth)
+      {
+         default:
+         /* Must be 1bpp gray: should not be here! */
+            /* NOTREACHED */
+            break;
+
+         case 2:
+         /* Must be 2bpp gray */
+         /* assert(channels == 1 && shift[0] == 1) */
+         {
+            png_bytep bp = row;
+            png_bytep bp_end = bp + row_info->rowbytes;
+
+            while (bp < bp_end)
+            {
+               int b = (*bp >> 1) & 0x55;
+               *bp++ = (png_byte)b;
+            }
+            break;
+         }
+
+         case 4:
+         /* Must be 4bpp gray */
+         /* assert(channels == 1) */
+         {
+            png_bytep bp = row;
+            png_bytep bp_end = bp + row_info->rowbytes;
+            int gray_shift = shift[0];
+            int mask =  0xf >> gray_shift;
+
+            mask |= mask << 4;
+
+            while (bp < bp_end)
+            {
+               int b = (*bp >> gray_shift) & mask;
+               *bp++ = (png_byte)b;
+            }
+            break;
+         }
+
+         case 8:
+         /* Single byte components, G, GA, RGB, RGBA */
+         {
+            png_bytep bp = row;
+            png_bytep bp_end = bp + row_info->rowbytes;
+            int channel = 0;
+
+            while (bp < bp_end)
+            {
+               int b = *bp >> shift[channel];
+               if (++channel >= channels)
+                  channel = 0;
+               *bp++ = (png_byte)b;
+            }
+            break;
+         }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+         case 16:
+         /* Double byte components, G, GA, RGB, RGBA */
+         {
+            png_bytep bp = row;
+            png_bytep bp_end = bp + row_info->rowbytes;
+            int channel = 0;
+
+            while (bp < bp_end)
+            {
+               int value = (bp[0] << 8) + bp[1];
+
+               value >>= shift[channel];
+               if (++channel >= channels)
+                  channel = 0;
+               *bp++ = (png_byte)(value >> 8);
+               *bp++ = (png_byte)value;
+            }
+            break;
+         }
+#endif
+      }
+   }
+}
+#endif
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+/* Scale rows of bit depth 16 down to 8 accurately */
+static void
+png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_scale_16_to_8");
+
+   if (row_info->bit_depth == 16)
+   {
+      png_bytep sp = row; /* source */
+      png_bytep dp = row; /* destination */
+      png_bytep ep = sp + row_info->rowbytes; /* end+1 */
+
+      while (sp < ep)
+      {
+         /* The input is an array of 16-bit components, these must be scaled to
+          * 8 bits each.  For a 16-bit value V the required value (from the PNG
+          * specification) is:
+          *
+          *    (V * 255) / 65535
+          *
+          * This reduces to round(V / 257), or floor((V + 128.5)/257)
+          *
+          * Represent V as the two byte value vhi.vlo.  Make a guess that the
+          * result is the top byte of V, vhi, then the correction to this value
+          * is:
+          *
+          *    error = floor(((V-vhi.vhi) + 128.5) / 257)
+          *          = floor(((vlo-vhi) + 128.5) / 257)
+          *
+          * This can be approximated using integer arithmetic (and a signed
+          * shift):
+          *
+          *    error = (vlo-vhi+128) >> 8;
+          *
+          * The approximate differs from the exact answer only when (vlo-vhi) is
+          * 128; it then gives a correction of +1 when the exact correction is
+          * 0.  This gives 128 errors.  The exact answer (correct for all 16-bit
+          * input values) is:
+          *
+          *    error = (vlo-vhi+128)*65535 >> 24;
+          *
+          * An alternative arithmetic calculation which also gives no errors is:
+          *
+          *    (V * 255 + 32895) >> 16
+          */
+
+         png_int_32 tmp = *sp++; /* must be signed! */
+         tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24;
+         *dp++ = (png_byte)tmp;
+      }
+
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+static void
+/* Simply discard the low byte.  This was the default behavior prior
+ * to libpng-1.5.4.
+ */
+png_do_chop(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_chop");
+
+   if (row_info->bit_depth == 16)
+   {
+      png_bytep sp = row; /* source */
+      png_bytep dp = row; /* destination */
+      png_bytep ep = sp + row_info->rowbytes; /* end+1 */
+
+      while (sp < ep)
+      {
+         *dp++ = *sp;
+         sp += 2; /* skip low byte */
+      }
+
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+#endif
+
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+static void
+png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_swap_alpha");
+
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from RGBA to ARGB */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+         /* This converts from RRGGBBAA to AARRGGBB */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+#endif
+      }
+
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from GA to AG */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+         /* This converts from GGAA to AAGG */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+#endif
+      }
+   }
+}
+#endif
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+static void
+png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_uint_32 row_width;
+   png_debug(1, "in png_do_read_invert_alpha");
+
+   row_width = row_info->width;
+   if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         /* This inverts the alpha channel in RGBA */
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_uint_32 i;
+
+         for (i = 0; i < row_width; i++)
+         {
+            *(--dp) = (png_byte)(255 - *(--sp));
+
+/*          This does nothing:
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            We can replace it with:
+*/
+            sp-=3;
+            dp=sp;
+         }
+      }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+      /* This inverts the alpha channel in RRGGBBAA */
+      else
+      {
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_uint_32 i;
+
+         for (i = 0; i < row_width; i++)
+         {
+            *(--dp) = (png_byte)(255 - *(--sp));
+            *(--dp) = (png_byte)(255 - *(--sp));
+
+/*          This does nothing:
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+            We can replace it with:
+*/
+            sp-=6;
+            dp=sp;
+         }
+      }
+#endif
+   }
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         /* This inverts the alpha channel in GA */
+         png_bytep sp = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_uint_32 i;
+
+         for (i = 0; i < row_width; i++)
+         {
+            *(--dp) = (png_byte)(255 - *(--sp));
+            *(--dp) = *(--sp);
+         }
+      }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+      else
+      {
+         /* This inverts the alpha channel in GGAA */
+         png_bytep sp  = row + row_info->rowbytes;
+         png_bytep dp = sp;
+         png_uint_32 i;
+
+         for (i = 0; i < row_width; i++)
+         {
+            *(--dp) = (png_byte)(255 - *(--sp));
+            *(--dp) = (png_byte)(255 - *(--sp));
+/*
+            *(--dp) = *(--sp);
+            *(--dp) = *(--sp);
+*/
+            sp-=2;
+            dp=sp;
+         }
+      }
+#endif
+   }
+}
+#endif
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+/* Add filler channel if we have RGB color */
+static void
+png_do_read_filler(png_row_infop row_info, png_bytep row,
+    png_uint_32 filler, png_uint_32 flags)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+   png_byte hi_filler = (png_byte)(filler>>8);
+#endif
+   png_byte lo_filler = (png_byte)filler;
+
+   png_debug(1, "in png_do_read_filler");
+
+   if (
+       row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
+         {
+            /* This changes the data from G to GX */
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp =  sp + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+
+         else
+         {
+            /* This changes the data from G to XG */
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+      else if (row_info->bit_depth == 16)
+      {
+         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
+         {
+            /* This changes the data from GG to GGXX */
+            png_bytep sp = row + (png_size_t)row_width * 2;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = hi_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            *(--dp) = hi_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+
+         else
+         {
+            /* This changes the data from GG to XXGG */
+            png_bytep sp = row + (png_size_t)row_width * 2;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+               *(--dp) = hi_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+#endif
+   } /* COLOR_TYPE == GRAY */
+   else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
+         {
+            /* This changes the data from RGB to RGBX */
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+
+         else
+         {
+            /* This changes the data from RGB to XRGB */
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+      else if (row_info->bit_depth == 16)
+      {
+         if ((flags & PNG_FLAG_FILLER_AFTER) != 0)
+         {
+            /* This changes the data from RRGGBB to RRGGBBXX */
+            png_bytep sp = row + (png_size_t)row_width * 6;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = hi_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            *(--dp) = hi_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 8;
+         }
+
+         else
+         {
+            /* This changes the data from RRGGBB to XXRRGGBB */
+            png_bytep sp = row + (png_size_t)row_width * 6;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+               *(--dp) = hi_filler;
+            }
+
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 8;
+         }
+      }
+#endif
+   } /* COLOR_TYPE == RGB */
+}
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+/* Expand grayscale files to RGB, with or without alpha */
+static void
+png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_debug(1, "in png_do_gray_to_rgb");
+
+   if (row_info->bit_depth >= 8 &&
+       (row_info->color_type & PNG_COLOR_MASK_COLOR) == 0)
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This changes G to RGB */
+            png_bytep sp = row + (png_size_t)row_width - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *(sp--);
+            }
+         }
+
+         else
+         {
+            /* This changes GG to RRGGBB */
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+            }
+         }
+      }
+
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This changes GA to RGBA */
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *(sp--);
+            }
+         }
+
+         else
+         {
+            /* This changes GGAA to RRGGBBAA */
+            png_bytep sp = row + (png_size_t)row_width * 4 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+            }
+         }
+      }
+      row_info->channels = (png_byte)(row_info->channels + 2);
+      row_info->color_type |= PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+          row_info->bit_depth);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+   }
+}
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* Reduce RGB files to grayscale, with or without alpha
+ * using the equation given in Poynton's ColorFAQ of 1998-01-04 at
+ * <http://www.inforamp.net/~poynton/>  (THIS LINK IS DEAD June 2008 but
+ * versions dated 1998 through November 2002 have been archived at
+ * http://web.archive.org/web/20000816232553/http://www.inforamp.net/
+ * ~poynton/notes/colour_and_gamma/ColorFAQ.txt )
+ * Charles Poynton poynton at poynton.com
+ *
+ *     Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+ *
+ *  which can be expressed with integers as
+ *
+ *     Y = (6969 * R + 23434 * G + 2365 * B)/32768
+ *
+ * Poynton's current link (as of January 2003 through July 2011):
+ * <http://www.poynton.com/notes/colour_and_gamma/>
+ * has changed the numbers slightly:
+ *
+ *     Y = 0.2126*R + 0.7152*G + 0.0722*B
+ *
+ *  which can be expressed with integers as
+ *
+ *     Y = (6966 * R + 23436 * G + 2366 * B)/32768
+ *
+ *  Historically, however, libpng uses numbers derived from the ITU-R Rec 709
+ *  end point chromaticities and the D65 white point.  Depending on the
+ *  precision used for the D65 white point this produces a variety of different
+ *  numbers, however if the four decimal place value used in ITU-R Rec 709 is
+ *  used (0.3127,0.3290) the Y calculation would be:
+ *
+ *     Y = (6968 * R + 23435 * G + 2366 * B)/32768
+ *
+ *  While this is correct the rounding results in an overflow for white, because
+ *  the sum of the rounded coefficients is 32769, not 32768.  Consequently
+ *  libpng uses, instead, the closest non-overflowing approximation:
+ *
+ *     Y = (6968 * R + 23434 * G + 2366 * B)/32768
+ *
+ *  Starting with libpng-1.5.5, if the image being converted has a cHRM chunk
+ *  (including an sRGB chunk) then the chromaticities are used to calculate the
+ *  coefficients.  See the chunk handling in pngrutil.c for more information.
+ *
+ *  In all cases the calculation is to be done in a linear colorspace.  If no
+ *  gamma information is available to correct the encoding of the original RGB
+ *  values this results in an implicit assumption that the original PNG RGB
+ *  values were linear.
+ *
+ *  Other integer coefficents can be used via png_set_rgb_to_gray().  Because
+ *  the API takes just red and green coefficients the blue coefficient is
+ *  calculated to make the sum 32768.  This will result in different rounding
+ *  to that used above.
+ */
+static int
+png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row)
+
+{
+   int rgb_error = 0;
+
+   png_debug(1, "in png_do_rgb_to_gray");
+
+   if ((row_info->color_type & PNG_COLOR_MASK_PALETTE) == 0 &&
+       (row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
+      PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
+      PNG_CONST png_uint_32 bc = 32768 - rc - gc;
+      PNG_CONST png_uint_32 row_width = row_info->width;
+      PNG_CONST int have_alpha =
+         (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;
+
+      if (row_info->bit_depth == 8)
+      {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+         /* Notice that gamma to/from 1 are not necessarily inverses (if
+          * there is an overall gamma correction).  Prior to 1.5.5 this code
+          * checked the linearized values for equality; this doesn't match
+          * the documentation, the original values must be checked.
+          */
+         if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+         {
+            png_bytep sp = row;
+            png_bytep dp = row;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte red   = *(sp++);
+               png_byte green = *(sp++);
+               png_byte blue  = *(sp++);
+
+               if (red != green || red != blue)
+               {
+                  red = png_ptr->gamma_to_1[red];
+                  green = png_ptr->gamma_to_1[green];
+                  blue = png_ptr->gamma_to_1[blue];
+
+                  rgb_error |= 1;
+                  *(dp++) = png_ptr->gamma_from_1[
+                      (rc*red + gc*green + bc*blue + 16384)>>15];
+               }
+
+               else
+               {
+                  /* If there is no overall correction the table will not be
+                   * set.
+                   */
+                  if (png_ptr->gamma_table != NULL)
+                     red = png_ptr->gamma_table[red];
+
+                  *(dp++) = red;
+               }
+
+               if (have_alpha != 0)
+                  *(dp++) = *(sp++);
+            }
+         }
+         else
+#endif
+         {
+            png_bytep sp = row;
+            png_bytep dp = row;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte red   = *(sp++);
+               png_byte green = *(sp++);
+               png_byte blue  = *(sp++);
+
+               if (red != green || red != blue)
+               {
+                  rgb_error |= 1;
+                  /* NOTE: this is the historical approach which simply
+                   * truncates the results.
+                   */
+                  *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
+               }
+
+               else
+                  *(dp++) = red;
+
+               if (have_alpha != 0)
+                  *(dp++) = *(sp++);
+            }
+         }
+      }
+
+      else /* RGB bit_depth == 16 */
+      {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+         if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL)
+         {
+            png_bytep sp = row;
+            png_bytep dp = row;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               png_uint_16 red, green, blue, w;
+               png_byte hi,lo;
+
+               hi=*(sp)++; lo=*(sp)++; red   = (png_uint_16)((hi << 8) | (lo));
+               hi=*(sp)++; lo=*(sp)++; green = (png_uint_16)((hi << 8) | (lo));
+               hi=*(sp)++; lo=*(sp)++; blue  = (png_uint_16)((hi << 8) | (lo));
+
+               if (red == green && red == blue)
+               {
+                  if (png_ptr->gamma_16_table != NULL)
+                     w = png_ptr->gamma_16_table[(red & 0xff)
+                         >> png_ptr->gamma_shift][red >> 8];
+
+                  else
+                     w = red;
+               }
+
+               else
+               {
+                  png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red & 0xff)
+                      >> png_ptr->gamma_shift][red>>8];
+                  png_uint_16 green_1 =
+                      png_ptr->gamma_16_to_1[(green & 0xff) >>
+                      png_ptr->gamma_shift][green>>8];
+                  png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue & 0xff)
+                      >> png_ptr->gamma_shift][blue>>8];
+                  png_uint_16 gray16  = (png_uint_16)((rc*red_1 + gc*green_1
+                      + bc*blue_1 + 16384)>>15);
+                  w = png_ptr->gamma_16_from_1[(gray16 & 0xff) >>
+                      png_ptr->gamma_shift][gray16 >> 8];
+                  rgb_error |= 1;
+               }
+
+               *(dp++) = (png_byte)((w>>8) & 0xff);
+               *(dp++) = (png_byte)(w & 0xff);
+
+               if (have_alpha != 0)
+               {
+                  *(dp++) = *(sp++);
+                  *(dp++) = *(sp++);
+               }
+            }
+         }
+         else
+#endif
+         {
+            png_bytep sp = row;
+            png_bytep dp = row;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               png_uint_16 red, green, blue, gray16;
+               png_byte hi,lo;
+
+               hi=*(sp)++; lo=*(sp)++; red   = (png_uint_16)((hi << 8) | (lo));
+               hi=*(sp)++; lo=*(sp)++; green = (png_uint_16)((hi << 8) | (lo));
+               hi=*(sp)++; lo=*(sp)++; blue  = (png_uint_16)((hi << 8) | (lo));
+
+               if (red != green || red != blue)
+                  rgb_error |= 1;
+
+               /* From 1.5.5 in the 16-bit case do the accurate conversion even
+                * in the 'fast' case - this is because this is where the code
+                * ends up when handling linear 16-bit data.
+                */
+               gray16  = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >>
+                  15);
+               *(dp++) = (png_byte)((gray16 >> 8) & 0xff);
+               *(dp++) = (png_byte)(gray16 & 0xff);
+
+               if (have_alpha != 0)
+               {
+                  *(dp++) = *(sp++);
+                  *(dp++) = *(sp++);
+               }
+            }
+         }
+      }
+
+      row_info->channels = (png_byte)(row_info->channels - 2);
+      row_info->color_type = (png_byte)(row_info->color_type &
+          ~PNG_COLOR_MASK_COLOR);
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+          row_info->bit_depth);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+   }
+   return rgb_error;
+}
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+/* Replace any alpha or transparency with the supplied background color.
+ * "background" is already in the screen gamma, while "background_1" is
+ * at a gamma of 1.0.  Paletted files have already been taken care of.
+ */
+static void
+png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
+{
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   png_const_bytep gamma_table = png_ptr->gamma_table;
+   png_const_bytep gamma_from_1 = png_ptr->gamma_from_1;
+   png_const_bytep gamma_to_1 = png_ptr->gamma_to_1;
+   png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table;
+   png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1;
+   png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1;
+   int gamma_shift = png_ptr->gamma_shift;
+   int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
+#endif
+
+   png_bytep sp;
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+   int shift;
+
+   png_debug(1, "in png_do_compose");
+
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  sp = row;
+                  shift = 7;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x01)
+                        == png_ptr->trans_color.gray)
+                     {
+                        unsigned int tmp = *sp & (0x7f7f >> (7 - shift));
+                        tmp |= png_ptr->background.gray << shift;
+                        *sp = (png_byte)(tmp & 0xff);
+                     }
+
+                     if (shift == 0)
+                     {
+                        shift = 7;
+                        sp++;
+                     }
+
+                     else
+                        shift--;
+                  }
+                  break;
+               }
+
+               case 2:
+               {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x03)
+                            == png_ptr->trans_color.gray)
+                        {
+                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
+                           tmp |= png_ptr->background.gray << shift;
+                           *sp = (png_byte)(tmp & 0xff);
+                        }
+
+                        else
+                        {
+                           unsigned int p = (*sp >> shift) & 0x03;
+                           unsigned int g = (gamma_table [p | (p << 2) |
+                               (p << 4) | (p << 6)] >> 6) & 0x03;
+                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
+                           tmp |= g << shift;
+                           *sp = (png_byte)(tmp & 0xff);
+                        }
+
+                        if (shift == 0)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+
+                        else
+                           shift -= 2;
+                     }
+                  }
+
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x03)
+                            == png_ptr->trans_color.gray)
+                        {
+                           unsigned int tmp = *sp & (0x3f3f >> (6 - shift));
+                           tmp |= png_ptr->background.gray << shift;
+                           *sp = (png_byte)(tmp & 0xff);
+                        }
+
+                        if (shift == 0)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  break;
+               }
+
+               case 4:
+               {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x0f)
+                            == png_ptr->trans_color.gray)
+                        {
+                           unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
+                           tmp |= png_ptr->background.gray << shift;
+                           *sp = (png_byte)(tmp & 0xff);
+                        }
+
+                        else
+                        {
+                           unsigned int p = (*sp >> shift) & 0x0f;
+                           unsigned int g = (gamma_table[p | (p << 4)] >> 4) &
+                              0x0f;
+                           unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
+                           tmp |= g << shift;
+                           *sp = (png_byte)(tmp & 0xff);
+                        }
+
+                        if (shift == 0)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+
+                        else
+                           shift -= 4;
+                     }
+                  }
+
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x0f)
+                            == png_ptr->trans_color.gray)
+                        {
+                           unsigned int tmp = *sp & (0x0f0f >> (4 - shift));
+                           tmp |= png_ptr->background.gray << shift;
+                           *sp = (png_byte)(tmp & 0xff);
+                        }
+
+                        if (shift == 0)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  break;
+               }
+
+               case 8:
+               {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == png_ptr->trans_color.gray)
+                           *sp = (png_byte)png_ptr->background.gray;
+
+                        else
+                           *sp = gamma_table[*sp];
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == png_ptr->trans_color.gray)
+                           *sp = (png_byte)png_ptr->background.gray;
+                     }
+                  }
+                  break;
+               }
+
+               case 16:
+               {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+                  if (gamma_16 != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+                        if (v == png_ptr->trans_color.gray)
+                        {
+                           /* Background is already in screen gamma */
+                           *sp = (png_byte)((png_ptr->background.gray >> 8)
+                                & 0xff);
+                           *(sp + 1) = (png_byte)(png_ptr->background.gray
+                                & 0xff);
+                        }
+
+                        else
+                        {
+                           v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                           *sp = (png_byte)((v >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(v & 0xff);
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+                        if (v == png_ptr->trans_color.gray)
+                        {
+                           *sp = (png_byte)((png_ptr->background.gray >> 8)
+                                & 0xff);
+                           *(sp + 1) = (png_byte)(png_ptr->background.gray
+                                & 0xff);
+                        }
+                     }
+                  }
+                  break;
+               }
+
+               default:
+                  break;
+            }
+            break;
+         }
+
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == png_ptr->trans_color.red &&
+                         *(sp + 1) == png_ptr->trans_color.green &&
+                         *(sp + 2) == png_ptr->trans_color.blue)
+                     {
+                        *sp = (png_byte)png_ptr->background.red;
+                        *(sp + 1) = (png_byte)png_ptr->background.green;
+                        *(sp + 2) = (png_byte)png_ptr->background.blue;
+                     }
+
+                     else
+                     {
+                        *sp = gamma_table[*sp];
+                        *(sp + 1) = gamma_table[*(sp + 1)];
+                        *(sp + 2) = gamma_table[*(sp + 2)];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == png_ptr->trans_color.red &&
+                         *(sp + 1) == png_ptr->trans_color.green &&
+                         *(sp + 2) == png_ptr->trans_color.blue)
+                     {
+                        *sp = (png_byte)png_ptr->background.red;
+                        *(sp + 1) = (png_byte)png_ptr->background.green;
+                        *(sp + 2) = (png_byte)png_ptr->background.blue;
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_16 != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                         + *(sp + 3));
+
+                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                         + *(sp + 5));
+
+                     if (r == png_ptr->trans_color.red &&
+                         g == png_ptr->trans_color.green &&
+                         b == png_ptr->trans_color.blue)
+                     {
+                        /* Background is already in screen gamma */
+                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                                & 0xff);
+                        *(sp + 3) = (png_byte)(png_ptr->background.green
+                                & 0xff);
+                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                                & 0xff);
+                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                     }
+
+                     else
+                     {
+                        png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(v & 0xff);
+
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+                     png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                         + *(sp + 3));
+
+                     png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                         + *(sp + 5));
+
+                     if (r == png_ptr->trans_color.red &&
+                         g == png_ptr->trans_color.green &&
+                         b == png_ptr->trans_color.blue)
+                     {
+                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                                & 0xff);
+                        *(sp + 3) = (png_byte)(png_ptr->background.green
+                                & 0xff);
+                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                                & 0xff);
+                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 2)
+                  {
+                     png_uint_16 a = *(sp + 1);
+
+                     if (a == 0xff)
+                        *sp = gamma_table[*sp];
+
+                     else if (a == 0)
+                     {
+                        /* Background is already in screen gamma */
+                        *sp = (png_byte)png_ptr->background.gray;
+                     }
+
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, png_ptr->background_1.gray);
+                        if (optimize == 0)
+                           w = gamma_from_1[w];
+                        *sp = w;
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 2)
+                  {
+                     png_byte a = *(sp + 1);
+
+                     if (a == 0)
+                        *sp = (png_byte)png_ptr->background.gray;
+
+                     else if (a < 0xff)
+                        png_composite(*sp, *sp, a, png_ptr->background.gray);
+                  }
+               }
+            }
+            else /* if (png_ptr->bit_depth == 16) */
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 4)
+                  {
+                     png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+                         + *(sp + 3));
+
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+                     }
+
+                     else if (a == 0)
+                     {
+                        /* Background is already in screen gamma */
+                        *sp = (png_byte)((png_ptr->background.gray >> 8)
+                                & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
+                     }
+
+                     else
+                     {
+                        png_uint_16 g, v, w;
+
+                        g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(v, g, a, png_ptr->background_1.gray);
+                        if (optimize != 0)
+                           w = v;
+                        else
+                           w = gamma_16_from_1[(v & 0xff) >>
+                               gamma_shift][v >> 8];
+                        *sp = (png_byte)((w >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(w & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 4)
+                  {
+                     png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+                         + *(sp + 3));
+
+                     if (a == 0)
+                     {
+                        *sp = (png_byte)((png_ptr->background.gray >> 8)
+                                & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
+                     }
+
+                     else if (a < 0xffff)
+                     {
+                        png_uint_16 g, v;
+
+                        g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        png_composite_16(v, g, a, png_ptr->background.gray);
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 4)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *sp = gamma_table[*sp];
+                        *(sp + 1) = gamma_table[*(sp + 1)];
+                        *(sp + 2) = gamma_table[*(sp + 2)];
+                     }
+
+                     else if (a == 0)
+                     {
+                        /* Background is already in screen gamma */
+                        *sp = (png_byte)png_ptr->background.red;
+                        *(sp + 1) = (png_byte)png_ptr->background.green;
+                        *(sp + 2) = (png_byte)png_ptr->background.blue;
+                     }
+
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, png_ptr->background_1.red);
+                        if (optimize == 0) w = gamma_from_1[w];
+                        *sp = w;
+
+                        v = gamma_to_1[*(sp + 1)];
+                        png_composite(w, v, a, png_ptr->background_1.green);
+                        if (optimize == 0) w = gamma_from_1[w];
+                        *(sp + 1) = w;
+
+                        v = gamma_to_1[*(sp + 2)];
+                        png_composite(w, v, a, png_ptr->background_1.blue);
+                        if (optimize == 0) w = gamma_from_1[w];
+                        *(sp + 2) = w;
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 4)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0)
+                     {
+                        *sp = (png_byte)png_ptr->background.red;
+                        *(sp + 1) = (png_byte)png_ptr->background.green;
+                        *(sp + 2) = (png_byte)png_ptr->background.blue;
+                     }
+
+                     else if (a < 0xff)
+                     {
+                        png_composite(*sp, *sp, a, png_ptr->background.red);
+
+                        png_composite(*(sp + 1), *(sp + 1), a,
+                            png_ptr->background.green);
+
+                        png_composite(*(sp + 2), *(sp + 2), a,
+                            png_ptr->background.blue);
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 8)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                         << 8) + (png_uint_16)(*(sp + 7)));
+
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(v & 0xff);
+
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(v & 0xff);
+                     }
+
+                     else if (a == 0)
+                     {
+                        /* Background is already in screen gamma */
+                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                                & 0xff);
+                        *(sp + 3) = (png_byte)(png_ptr->background.green
+                                & 0xff);
+                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                                & 0xff);
+                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                     }
+
+                     else
+                     {
+                        png_uint_16 v, w;
+
+                        v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(w, v, a, png_ptr->background_1.red);
+                        if (optimize == 0)
+                           w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
+                                8];
+                        *sp = (png_byte)((w >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(w & 0xff);
+
+                        v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        png_composite_16(w, v, a, png_ptr->background_1.green);
+                        if (optimize == 0)
+                           w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
+                                8];
+
+                        *(sp + 2) = (png_byte)((w >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(w & 0xff);
+
+                        v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        png_composite_16(w, v, a, png_ptr->background_1.blue);
+                        if (optimize == 0)
+                           w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >>
+                                8];
+
+                        *(sp + 4) = (png_byte)((w >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(w & 0xff);
+                     }
+                  }
+               }
+
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 8)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                         << 8) + (png_uint_16)(*(sp + 7)));
+
+                     if (a == 0)
+                     {
+                        *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+                        *(sp + 2) = (png_byte)((png_ptr->background.green >> 8)
+                                & 0xff);
+                        *(sp + 3) = (png_byte)(png_ptr->background.green
+                                & 0xff);
+                        *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8)
+                                & 0xff);
+                        *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
+                     }
+
+                     else if (a < 0xffff)
+                     {
+                        png_uint_16 v;
+
+                        png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                            + *(sp + 3));
+                        png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                            + *(sp + 5));
+
+                        png_composite_16(v, r, a, png_ptr->background.red);
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+
+                        png_composite_16(v, g, a, png_ptr->background.green);
+                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(v & 0xff);
+
+                        png_composite_16(v, b, a, png_ptr->background.blue);
+                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+
+         default:
+            break;
+      }
+   }
+}
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Gamma correct the image, avoiding the alpha channel.  Make sure
+ * you do this after you deal with the transparency issue on grayscale
+ * or RGB images. If your bit depth is 8, use gamma_table, if it
+ * is 16, use gamma_16_table and gamma_shift.  Build these with
+ * build_gamma_table().
+ */
+static void
+png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
+{
+   png_const_bytep gamma_table = png_ptr->gamma_table;
+   png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table;
+   int gamma_shift = png_ptr->gamma_shift;
+
+   png_bytep sp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_gamma");
+
+   if (((row_info->bit_depth <= 8 && gamma_table != NULL) ||
+       (row_info->bit_depth == 16 && gamma_16_table != NULL)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+
+                  *sp = gamma_table[*sp];
+                  sp++;
+
+                  *sp = gamma_table[*sp];
+                  sp++;
+
+                  sp++;
+               }
+            }
+
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp += 2;
+               }
+            }
+
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            if (row_info->bit_depth == 2)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 4)
+               {
+                  int a = *sp & 0xc0;
+                  int b = *sp & 0x30;
+                  int c = *sp & 0x0c;
+                  int d = *sp & 0x03;
+
+                  *sp = (png_byte)(
+                      ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|
+                      ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|
+                      ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|
+                      ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));
+                  sp++;
+               }
+            }
+
+            if (row_info->bit_depth == 4)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 2)
+               {
+                  int msb = *sp & 0xf0;
+                  int lsb = *sp & 0x0f;
+
+                  *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)
+                      | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));
+                  sp++;
+               }
+            }
+
+            else if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+
+            else if (row_info->bit_depth == 16)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+
+         default:
+            break;
+      }
+   }
+}
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+/* Encode the alpha channel to the output gamma (the input channel is always
+ * linear.)  Called only with color types that have an alpha channel.  Needs the
+ * from_1 tables.
+ */
+static void
+png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr)
+{
+   png_uint_32 row_width = row_info->width;
+
+   png_debug(1, "in png_do_encode_alpha");
+
+   if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         PNG_CONST png_bytep table = png_ptr->gamma_from_1;
+
+         if (table != NULL)
+         {
+            PNG_CONST int step =
+               (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;
+
+            /* The alpha channel is the last component: */
+            row += step - 1;
+
+            for (; row_width > 0; --row_width, row += step)
+               *row = table[*row];
+
+            return;
+         }
+      }
+
+      else if (row_info->bit_depth == 16)
+      {
+         PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1;
+         PNG_CONST int gamma_shift = png_ptr->gamma_shift;
+
+         if (table != NULL)
+         {
+            PNG_CONST int step =
+               (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;
+
+            /* The alpha channel is the last component: */
+            row += step - 2;
+
+            for (; row_width > 0; --row_width, row += step)
+            {
+               png_uint_16 v;
+
+               v = table[*(row + 1) >> gamma_shift][*row];
+               *row = (png_byte)((v >> 8) & 0xff);
+               *(row + 1) = (png_byte)(v & 0xff);
+            }
+
+            return;
+         }
+      }
+   }
+
+   /* Only get to here if called with a weird row_info; no harm has been done,
+    * so just issue a warning.
+    */
+   png_warning(png_ptr, "png_do_encode_alpha: unexpected call");
+}
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expands a palette row to an RGB or RGBA row depending
+ * upon whether you supply trans and num_trans.
+ */
+static void
+png_do_expand_palette(png_row_infop row_info, png_bytep row,
+   png_const_colorp palette, png_const_bytep trans_alpha, int num_trans)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand_palette");
+
+   if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (row_info->bit_depth < 8)
+      {
+         switch (row_info->bit_depth)
+         {
+            case 1:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 3);
+               dp = row + (png_size_t)row_width - 1;
+               shift = 7 - (int)((row_width + 7) & 0x07);
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((*sp >> shift) & 0x01)
+                     *dp = 1;
+
+                  else
+                     *dp = 0;
+
+                  if (shift == 7)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+
+                  else
+                     shift++;
+
+                  dp--;
+               }
+               break;
+            }
+
+            case 2:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 2);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x03;
+                  *dp = (png_byte)value;
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+
+            case 4:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 1);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((row_width & 0x01) << 2);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x0f;
+                  *dp = (png_byte)value;
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+
+                  else
+                     shift += 4;
+
+                  dp--;
+               }
+               break;
+            }
+
+            default:
+               break;
+         }
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_width;
+      }
+
+      if (row_info->bit_depth == 8)
+      {
+         {
+            if (num_trans > 0)
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 2) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((int)(*sp) >= num_trans)
+                     *dp-- = 0xff;
+
+                  else
+                     *dp-- = trans_alpha[*sp];
+
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 32;
+               row_info->rowbytes = row_width * 4;
+               row_info->color_type = 6;
+               row_info->channels = 4;
+            }
+
+            else
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width * 3) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 24;
+               row_info->rowbytes = row_width * 3;
+               row_info->color_type = 2;
+               row_info->channels = 3;
+            }
+         }
+      }
+   }
+}
+
+/* If the bit depth < 8, it is expanded to 8.  Also, if the already
+ * expanded transparency value is supplied, an alpha channel is built.
+ */
+static void
+png_do_expand(png_row_infop row_info, png_bytep row,
+    png_const_color_16p trans_color)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand");
+
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         unsigned int gray = trans_color != NULL ? trans_color->gray : 0;
+
+         if (row_info->bit_depth < 8)
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  gray = (gray & 0x01) * 0xff;
+                  sp = row + (png_size_t)((row_width - 1) >> 3);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = 7 - (int)((row_width + 7) & 0x07);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((*sp >> shift) & 0x01)
+                        *dp = 0xff;
+
+                     else
+                        *dp = 0;
+
+                     if (shift == 7)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+
+                     else
+                        shift++;
+
+                     dp--;
+                  }
+                  break;
+               }
+
+               case 2:
+               {
+                  gray = (gray & 0x03) * 0x55;
+                  sp = row + (png_size_t)((row_width - 1) >> 2);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x03;
+                     *dp = (png_byte)(value | (value << 2) | (value << 4) |
+                        (value << 6));
+                     if (shift == 6)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+
+                     else
+                        shift += 2;
+
+                     dp--;
+                  }
+                  break;
+               }
+
+               case 4:
+               {
+                  gray = (gray & 0x0f) * 0x11;
+                  sp = row + (png_size_t)((row_width - 1) >> 1);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x0f;
+                     *dp = (png_byte)(value | (value << 4));
+                     if (shift == 4)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+
+                     else
+                        shift = 4;
+
+                     dp--;
+                  }
+                  break;
+               }
+
+               default:
+                  break;
+            }
+
+            row_info->bit_depth = 8;
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+
+         if (trans_color != NULL)
+         {
+            if (row_info->bit_depth == 8)
+            {
+               gray = gray & 0xff;
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 1) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((*sp & 0xffU) == gray)
+                     *dp-- = 0;
+
+                  else
+                     *dp-- = 0xff;
+
+                  *dp-- = *sp--;
+               }
+            }
+
+            else if (row_info->bit_depth == 16)
+            {
+               unsigned int gray_high = (gray >> 8) & 0xff;
+               unsigned int gray_low = gray & 0xff;
+               sp = row + row_info->rowbytes - 1;
+               dp = row + (row_info->rowbytes << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((*(sp - 1) & 0xffU) == gray_high &&
+                      (*(sp) & 0xffU) == gray_low)
+                  {
+                     *dp-- = 0;
+                     *dp-- = 0;
+                  }
+
+                  else
+                  {
+                     *dp-- = 0xff;
+                     *dp-- = 0xff;
+                  }
+
+                  *dp-- = *sp--;
+                  *dp-- = *sp--;
+               }
+            }
+
+            row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+            row_info->channels = 2;
+            row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
+            row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+               row_width);
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
+          trans_color != NULL)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_byte red = (png_byte)(trans_color->red & 0xff);
+            png_byte green = (png_byte)(trans_color->green & 0xff);
+            png_byte blue = (png_byte)(trans_color->blue & 0xff);
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 2) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
+                  *dp-- = 0;
+
+               else
+                  *dp-- = 0xff;
+
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         else if (row_info->bit_depth == 16)
+         {
+            png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);
+            png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);
+            png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);
+            png_byte red_low = (png_byte)(trans_color->red & 0xff);
+            png_byte green_low = (png_byte)(trans_color->green & 0xff);
+            png_byte blue_low = (png_byte)(trans_color->blue & 0xff);
+            sp = row + row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 3) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if (*(sp - 5) == red_high &&
+                   *(sp - 4) == red_low &&
+                   *(sp - 3) == green_high &&
+                   *(sp - 2) == green_low &&
+                   *(sp - 1) == blue_high &&
+                   *(sp    ) == blue_low)
+               {
+                  *dp-- = 0;
+                  *dp-- = 0;
+               }
+
+               else
+               {
+                  *dp-- = 0xff;
+                  *dp-- = 0xff;
+               }
+
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         row_info->channels = 4;
+         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+      }
+   }
+}
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* If the bit depth is 8 and the color type is not a palette type expand the
+ * whole row to 16 bits.  Has no effect otherwise.
+ */
+static void
+png_do_expand_16(png_row_infop row_info, png_bytep row)
+{
+   if (row_info->bit_depth == 8 &&
+      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      /* The row have a sequence of bytes containing [0..255] and we need
+       * to turn it into another row containing [0..65535], to do this we
+       * calculate:
+       *
+       *  (input / 255) * 65535
+       *
+       *  Which happens to be exactly input * 257 and this can be achieved
+       *  simply by byte replication in place (copying backwards).
+       */
+      png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */
+      png_byte *dp = sp + row_info->rowbytes;  /* destination, end + 1 */
+      while (dp > sp)
+         dp[-2] = dp[-1] = *--sp, dp -= 2;
+
+      row_info->rowbytes *= 2;
+      row_info->bit_depth = 16;
+      row_info->pixel_depth = (png_byte)(row_info->channels * 16);
+   }
+}
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+static void
+png_do_quantize(png_row_infop row_info, png_bytep row,
+    png_const_bytep palette_lookup, png_const_bytep quantize_lookup)
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_quantize");
+
+   if (row_info->bit_depth == 8)
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+
+            /* This looks real messy, but the compiler will reduce
+             * it down to a reasonable formula.  For example, with
+             * 5 bits per color, we get:
+             * p = (((r >> 3) & 0x1f) << 10) |
+             *    (((g >> 3) & 0x1f) << 5) |
+             *    ((b >> 3) & 0x1f);
+             */
+            p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
+                ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
+                (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
+                (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
+                ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
+                (PNG_QUANTIZE_BLUE_BITS)) |
+                ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
+                ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+      }
+
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         palette_lookup != NULL)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+            sp++;
+
+            p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
+                ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
+                (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
+                (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
+                ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
+                (PNG_QUANTIZE_BLUE_BITS)) |
+                ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
+                ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+      }
+
+      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+         quantize_lookup)
+      {
+         sp = row;
+
+         for (i = 0; i < row_width; i++, sp++)
+         {
+            *sp = quantize_lookup[*sp];
+         }
+      }
+   }
+}
+#endif /* READ_QUANTIZE */
+
+/* Transform the row.  The order of transformations is significant,
+ * and is very touchy.  If you add a transformation, take care to
+ * decide how it fits in with the other transformations here.
+ */
+void /* PRIVATE */
+png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info)
+{
+   png_debug(1, "in png_do_read_transformations");
+
+   if (png_ptr->row_buf == NULL)
+   {
+      /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this
+       * error is incredibly rare and incredibly easy to debug without this
+       * information.
+       */
+      png_error(png_ptr, "NULL row buffer");
+   }
+
+   /* The following is debugging; prior to 1.5.4 the code was never compiled in;
+    * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro
+    * PNG_WARN_UNINITIALIZED_ROW removed.  In 1.6 the new flag is set only for
+    * all transformations, however in practice the ROW_INIT always gets done on
+    * demand, if necessary.
+    */
+   if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 &&
+       (png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
+   {
+      /* Application has failed to call either png_read_start_image() or
+       * png_read_update_info() after setting transforms that expand pixels.
+       * This check added to libpng-1.2.19 (but not enabled until 1.5.4).
+       */
+      png_error(png_ptr, "Uninitialized row");
+   }
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+   if ((png_ptr->transformations & PNG_EXPAND) != 0)
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_do_expand_palette(row_info, png_ptr->row_buf + 1,
+             png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans);
+      }
+
+      else
+      {
+         if (png_ptr->num_trans != 0 &&
+             (png_ptr->transformations & PNG_EXPAND_tRNS) != 0)
+            png_do_expand(row_info, png_ptr->row_buf + 1,
+                &(png_ptr->trans_color));
+
+         else
+            png_do_expand(row_info, png_ptr->row_buf + 1,
+                NULL);
+      }
+   }
+#endif
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
+       (png_ptr->transformations & PNG_COMPOSE) == 0 &&
+       (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+       row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
+      png_do_strip_channel(row_info, png_ptr->row_buf + 1,
+         0 /* at_start == false, because SWAP_ALPHA happens later */);
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+   if ((png_ptr->transformations & PNG_RGB_TO_GRAY) != 0)
+   {
+      int rgb_error =
+          png_do_rgb_to_gray(png_ptr, row_info,
+              png_ptr->row_buf + 1);
+
+      if (rgb_error != 0)
+      {
+         png_ptr->rgb_to_gray_status=1;
+         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
+             PNG_RGB_TO_GRAY_WARN)
+            png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+
+         if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
+             PNG_RGB_TO_GRAY_ERR)
+            png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+      }
+   }
+#endif
+
+/* From Andreas Dilger e-mail to png-implement, 26 March 1998:
+ *
+ *   In most cases, the "simple transparency" should be done prior to doing
+ *   gray-to-RGB, or you will have to test 3x as many bytes to check if a
+ *   pixel is transparent.  You would also need to make sure that the
+ *   transparency information is upgraded to RGB.
+ *
+ *   To summarize, the current flow is:
+ *   - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
+ *                                   with background "in place" if transparent,
+ *                                   convert to RGB if necessary
+ *   - Gray + alpha -> composite with gray background and remove alpha bytes,
+ *                                   convert to RGB if necessary
+ *
+ *   To support RGB backgrounds for gray images we need:
+ *   - Gray + simple transparency -> convert to RGB + simple transparency,
+ *                                   compare 3 or 6 bytes and composite with
+ *                                   background "in place" if transparent
+ *                                   (3x compare/pixel compared to doing
+ *                                   composite with gray bkgrnd)
+ *   - Gray + alpha -> convert to RGB + alpha, composite with background and
+ *                                   remove alpha bytes (3x float
+ *                                   operations/pixel compared with composite
+ *                                   on gray background)
+ *
+ *  Greg's change will do this.  The reason it wasn't done before is for
+ *  performance, as this increases the per-pixel operations.  If we would check
+ *  in advance if the background was gray or RGB, and position the gray-to-RGB
+ *  transform appropriately, then it would save a lot of work/time.
+ */
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+   /* If gray -> RGB, do so now only if background is non-gray; else do later
+    * for performance reasons
+    */
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 &&
+       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY) == 0)
+      png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+   if ((png_ptr->transformations & PNG_COMPOSE) != 0)
+      png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr);
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   if ((png_ptr->transformations & PNG_GAMMA) != 0 &&
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+      /* Because RGB_TO_GRAY does the gamma transform. */
+      (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0 &&
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+      /* Because PNG_COMPOSE does the gamma transform if there is something to
+       * do (if there is an alpha channel or transparency.)
+       */
+       !((png_ptr->transformations & PNG_COMPOSE) != 0 &&
+       ((png_ptr->num_trans != 0) ||
+       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)) &&
+#endif
+      /* Because png_init_read_transformations transforms the palette, unless
+       * RGB_TO_GRAY will do the transform.
+       */
+       (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
+      png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr);
+#endif
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_STRIP_ALPHA) != 0 &&
+       (png_ptr->transformations & PNG_COMPOSE) != 0 &&
+       (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+       row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
+      png_do_strip_channel(row_info, png_ptr->row_buf + 1,
+          0 /* at_start == false, because SWAP_ALPHA happens later */);
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+   if ((png_ptr->transformations & PNG_ENCODE_ALPHA) != 0 &&
+       (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr);
+#endif
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+   if ((png_ptr->transformations & PNG_SCALE_16_TO_8) != 0)
+      png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+   /* There is no harm in doing both of these because only one has any effect,
+    * by putting the 'scale' option first if the app asks for scale (either by
+    * calling the API or in a TRANSFORM flag) this is what happens.
+    */
+   if ((png_ptr->transformations & PNG_16_TO_8) != 0)
+      png_do_chop(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+   if ((png_ptr->transformations & PNG_QUANTIZE) != 0)
+   {
+      png_do_quantize(row_info, png_ptr->row_buf + 1,
+          png_ptr->palette_lookup, png_ptr->quantize_index);
+
+      if (row_info->rowbytes == 0)
+         png_error(png_ptr, "png_do_quantize returned rowbytes=0");
+   }
+#endif /* READ_QUANTIZE */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   /* Do the expansion now, after all the arithmetic has been done.  Notice
+    * that previous transformations can handle the PNG_EXPAND_16 flag if this
+    * is efficient (particularly true in the case of gamma correction, where
+    * better accuracy results faster!)
+    */
+   if ((png_ptr->transformations & PNG_EXPAND_16) != 0)
+      png_do_expand_16(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+   /* NOTE: moved here in 1.5.4 (from much later in this list.) */
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0 &&
+       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY) != 0)
+      png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_INVERT_SUPPORTED
+   if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
+      png_do_invert(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
+      png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+   if ((png_ptr->transformations & PNG_SHIFT) != 0)
+      png_do_unshift(row_info, png_ptr->row_buf + 1,
+          &(png_ptr->shift));
+#endif
+
+#ifdef PNG_READ_PACK_SUPPORTED
+   if ((png_ptr->transformations & PNG_PACK) != 0)
+      png_do_unpack(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+   /* Added at libpng-1.5.10 */
+   if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+       png_ptr->num_palette_max >= 0)
+      png_do_check_palette_indexes(png_ptr, row_info);
+#endif
+
+#ifdef PNG_READ_BGR_SUPPORTED
+   if ((png_ptr->transformations & PNG_BGR) != 0)
+      png_do_bgr(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+   if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
+      png_do_packswap(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+   if ((png_ptr->transformations & PNG_FILLER) != 0)
+      png_do_read_filler(row_info, png_ptr->row_buf + 1,
+          (png_uint_32)png_ptr->filler, png_ptr->flags);
+#endif
+
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0)
+      png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+#ifdef PNG_READ_SWAP_SUPPORTED
+   if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
+      png_do_swap(row_info, png_ptr->row_buf + 1);
+#endif
+#endif
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
+   {
+      if (png_ptr->read_user_transform_fn != NULL)
+         (*(png_ptr->read_user_transform_fn)) /* User read transform function */
+             (png_ptr,     /* png_ptr */
+             row_info,     /* row_info: */
+                /*  png_uint_32 width;       width of row */
+                /*  png_size_t rowbytes;     number of bytes in row */
+                /*  png_byte color_type;     color type of pixels */
+                /*  png_byte bit_depth;      bit depth of samples */
+                /*  png_byte channels;       number of channels (1-4) */
+                /*  png_byte pixel_depth;    bits per pixel (depth*channels) */
+             png_ptr->row_buf + 1);    /* start of pixel data for row */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+      if (png_ptr->user_transform_depth != 0)
+         row_info->bit_depth = png_ptr->user_transform_depth;
+
+      if (png_ptr->user_transform_channels != 0)
+         row_info->channels = png_ptr->user_transform_channels;
+#endif
+      row_info->pixel_depth = (png_byte)(row_info->bit_depth *
+          row_info->channels);
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width);
+   }
+#endif
+}
+
+#endif /* READ_TRANSFORMS */
+#endif /* READ */
diff --git a/third_party/lpng_v163/pngrutil.c b/third_party/libpng/pngrutil.c
similarity index 85%
rename from third_party/lpng_v163/pngrutil.c
rename to third_party/libpng/pngrutil.c
index 970ec4d..6189251 100644
--- a/third_party/lpng_v163/pngrutil.c
+++ b/third_party/libpng/pngrutil.c
@@ -1,4471 +1,4529 @@
-/* pngrutil.c - utilities to read a PNG file

- *

- * Last changed in libpng 1.6.3 [July 18, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file contains routines that are only called from within

- * libpng itself during the course of reading an image.

- */

-

-#include "pngpriv.h"

-

-#ifdef PNG_READ_SUPPORTED

-

-png_uint_32 PNGAPI

-png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)

-{

-   png_uint_32 uval = png_get_uint_32(buf);

-

-   if (uval > PNG_UINT_31_MAX)

-      png_error(png_ptr, "PNG unsigned integer out of range");

-

-   return (uval);

-}

-

-#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)

-/* The following is a variation on the above for use with the fixed

- * point values used for gAMA and cHRM.  Instead of png_error it

- * issues a warning and returns (-1) - an invalid value because both

- * gAMA and cHRM use *unsigned* integers for fixed point values.

- */

-#define PNG_FIXED_ERROR (-1)

-

-static png_fixed_point /* PRIVATE */

-png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf)

-{

-   png_uint_32 uval = png_get_uint_32(buf);

-

-   if (uval <= PNG_UINT_31_MAX)

-      return (png_fixed_point)uval; /* known to be in range */

-

-   /* The caller can turn off the warning by passing NULL. */

-   if (png_ptr != NULL)

-      png_warning(png_ptr, "PNG fixed point integer out of range");

-

-   return PNG_FIXED_ERROR;

-}

-#endif

-

-#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED

-/* NOTE: the read macros will obscure these definitions, so that if

- * PNG_USE_READ_MACROS is set the library will not use them internally,

- * but the APIs will still be available externally.

- *

- * The parentheses around "PNGAPI function_name" in the following three

- * functions are necessary because they allow the macros to co-exist with

- * these (unused but exported) functions.

- */

-

-/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */

-png_uint_32 (PNGAPI

-png_get_uint_32)(png_const_bytep buf)

-{

-   png_uint_32 uval =

-       ((png_uint_32)(*(buf    )) << 24) +

-       ((png_uint_32)(*(buf + 1)) << 16) +

-       ((png_uint_32)(*(buf + 2)) <<  8) +

-       ((png_uint_32)(*(buf + 3))      ) ;

-

-   return uval;

-}

-

-/* Grab a signed 32-bit integer from a buffer in big-endian format.  The

- * data is stored in the PNG file in two's complement format and there

- * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore

- * the following code does a two's complement to native conversion.

- */

-png_int_32 (PNGAPI

-png_get_int_32)(png_const_bytep buf)

-{

-   png_uint_32 uval = png_get_uint_32(buf);

-   if ((uval & 0x80000000) == 0) /* non-negative */

-      return uval;

-

-   uval = (uval ^ 0xffffffff) + 1;  /* 2's complement: -x = ~x+1 */

-   return -(png_int_32)uval;

-}

-

-/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */

-png_uint_16 (PNGAPI

-png_get_uint_16)(png_const_bytep buf)

-{

-   /* ANSI-C requires an int value to accomodate at least 16 bits so this

-    * works and allows the compiler not to worry about possible narrowing

-    * on 32 bit systems.  (Pre-ANSI systems did not make integers smaller

-    * than 16 bits either.)

-    */

-   unsigned int val =

-       ((unsigned int)(*buf) << 8) +

-       ((unsigned int)(*(buf + 1)));

-

-   return (png_uint_16)val;

-}

-

-#endif /* PNG_READ_INT_FUNCTIONS_SUPPORTED */

-

-/* Read and check the PNG file signature */

-void /* PRIVATE */

-png_read_sig(png_structrp png_ptr, png_inforp info_ptr)

-{

-   png_size_t num_checked, num_to_check;

-

-   /* Exit if the user application does not expect a signature. */

-   if (png_ptr->sig_bytes >= 8)

-      return;

-

-   num_checked = png_ptr->sig_bytes;

-   num_to_check = 8 - num_checked;

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE;

-#endif

-

-   /* The signature must be serialized in a single I/O call. */

-   png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);

-   png_ptr->sig_bytes = 8;

-

-   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))

-   {

-      if (num_checked < 4 &&

-          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))

-         png_error(png_ptr, "Not a PNG file");

-      else

-         png_error(png_ptr, "PNG file corrupted by ASCII conversion");

-   }

-   if (num_checked < 3)

-      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;

-}

-

-/* Read the chunk header (length + type name).

- * Put the type name into png_ptr->chunk_name, and return the length.

- */

-png_uint_32 /* PRIVATE */

-png_read_chunk_header(png_structrp png_ptr)

-{

-   png_byte buf[8];

-   png_uint_32 length;

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR;

-#endif

-

-   /* Read the length and the chunk name.

-    * This must be performed in a single I/O call.

-    */

-   png_read_data(png_ptr, buf, 8);

-   length = png_get_uint_31(png_ptr, buf);

-

-   /* Put the chunk name into png_ptr->chunk_name. */

-   png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4);

-

-   png_debug2(0, "Reading %lx chunk, length = %lu",

-       (unsigned long)png_ptr->chunk_name, (unsigned long)length);

-

-   /* Reset the crc and run it over the chunk name. */

-   png_reset_crc(png_ptr);

-   png_calculate_crc(png_ptr, buf + 4, 4);

-

-   /* Check to see if chunk name is valid. */

-   png_check_chunk_name(png_ptr, png_ptr->chunk_name);

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;

-#endif

-

-   return length;

-}

-

-/* Read data, and (optionally) run it through the CRC. */

-void /* PRIVATE */

-png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_read_data(png_ptr, buf, length);

-   png_calculate_crc(png_ptr, buf, length);

-}

-

-/* Optionally skip data and then check the CRC.  Depending on whether we

- * 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_structrp png_ptr, png_uint_32 skip)

-{

-   /* 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_uint_32 len;

-      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];

-

-      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_ANCILLARY(png_ptr->chunk_name) ?

-          !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) :

-          (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))

-      {

-         png_chunk_warning(png_ptr, "CRC error");

-      }

-

-      else

-      {

-         png_chunk_benign_error(png_ptr, "CRC error");

-         return (0);

-      }

-

-      return (1);

-   }

-

-   return (0);

-}

-

-/* Compare the CRC stored in the PNG file with that calculated by libpng from

- * the data it has read thus far.

- */

-int /* PRIVATE */

-png_crc_error(png_structrp png_ptr)

-{

-   png_byte crc_bytes[4];

-   png_uint_32 crc;

-   int need_crc = 1;

-

-   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))

-         need_crc = 0;

-   }

-

-   else /* critical */

-   {

-      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)

-         need_crc = 0;

-   }

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;

-#endif

-

-   /* The chunk CRC must be serialized in a single I/O call. */

-   png_read_data(png_ptr, crc_bytes, 4);

-

-   if (need_crc)

-   {

-      crc = png_get_uint_32(crc_bytes);

-      return ((int)(crc != png_ptr->crc));

-   }

-

-   else

-      return (0);

-}

-

-/* 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_bytep buffer = png_ptr->read_buffer;

-

-   if (buffer != NULL && new_size > png_ptr->read_buffer_size)

-   {

-      png_ptr->read_buffer = NULL;

-      png_ptr->read_buffer = NULL;

-      png_ptr->read_buffer_size = 0;

-      png_free(png_ptr, buffer);

-      buffer = NULL;

-   }

-

-   if (buffer == NULL)

-   {

-      buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));

-

-      if (buffer != NULL)

-      {

-         png_ptr->read_buffer = buffer;

-         png_ptr->read_buffer_size = new_size;

-      }

-

-      else if (warn < 2) /* else silent */

-      {

-#ifdef PNG_WARNINGS_SUPPORTED

-         if (warn)

-             png_chunk_warning(png_ptr, "insufficient memory to read chunk");

-         else

-#endif

-         {

-#ifdef PNG_ERROR_TEXT_SUPPORTED

-             png_chunk_error(png_ptr, "insufficient memory to read chunk");

-#endif

-         }

-      }

-   }

-

-   return buffer;

-}

-

-/* png_inflate_claim: claim the zstream for some nefarious purpose that involves

- * decompression.  Returns Z_OK on success, else a zlib error code.  It checks

- * the owner but, in final release builds, just issues a warning if some other

- * chunk apparently owns the stream.  Prior to release it does a png_error.

- */

-static int

-png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)

-{

-   if (png_ptr->zowner != 0)

-   {

-      char msg[64];

-

-      PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner);

-      /* So the message that results is "<chunk> using zstream"; this is an

-       * internal error, but is very useful for debugging.  i18n requirements

-       * are minimal.

-       */

-      (void)png_safecat(msg, (sizeof msg), 4, " using zstream");

-#     if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC

-         png_chunk_warning(png_ptr, msg);

-         png_ptr->zowner = 0;

-#     else

-         png_chunk_error(png_ptr, msg);

-#     endif

-   }

-

-   /* Implementation note: unlike 'png_deflate_claim' this internal function

-    * does not take the size of the data as an argument.  Some efficiency could

-    * be gained by using this when it is known *if* the zlib stream itself does

-    * 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 */

-#     if PNG_ZLIB_VERNUM >= 0x1240

-

-#        if defined(PNG_SET_OPTION_SUPPORTED) && \

-            defined(PNG_MAXIMUM_INFLATE_WINDOW)

-            int window_bits;

-

-            if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==

-               PNG_OPTION_ON)

-               window_bits = 15;

-

-            else

-               window_bits = 0;

-#        else

-#           define window_bits 0

-#        endif

-#     endif

-

-      /* Set this for safety, just in case the previous owner left pointers to

-       * memory allocations.

-       */

-      png_ptr->zstream.next_in = NULL;

-      png_ptr->zstream.avail_in = 0;

-      png_ptr->zstream.next_out = NULL;

-      png_ptr->zstream.avail_out = 0;

-

-      if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)

-      {

-#        if PNG_ZLIB_VERNUM < 0x1240

-            ret = inflateReset(&png_ptr->zstream);

-#        else

-            ret = inflateReset2(&png_ptr->zstream, window_bits);

-#        endif

-      }

-

-      else

-      {

-#        if PNG_ZLIB_VERNUM < 0x1240

-            ret = inflateInit(&png_ptr->zstream);

-#        else

-            ret = inflateInit2(&png_ptr->zstream, window_bits);

-#        endif

-

-         if (ret == Z_OK)

-            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;

-      }

-

-      if (ret == Z_OK)

-         png_ptr->zowner = owner;

-

-      else

-         png_zstream_error(png_ptr, ret);

-

-      return ret;

-   }

-

-#  ifdef window_bits

-#     undef window_bits

-#  endif

-}

-

-#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;

-      png_ptr->zstream.avail_out = 0;

-

-      /* Read directly into the output if it is available (this is set to

-       * a local buffer below if output is NULL).

-       */

-      if (output != NULL)

-         png_ptr->zstream.next_out = output;

-

-      do

-      {

-         uInt avail;

-         Byte local_buffer[PNG_INFLATE_BUF_SIZE];

-

-         /* zlib INPUT BUFFER */

-         /* The setting of 'avail_in' used to be outside the loop; by setting it

-          * inside it is possible to chunk the input to zlib and simply rely on

-          * zlib to advance the 'next_in' pointer.  This allows arbitrary

-          * amounts of data to be passed through zlib at the unavoidable cost of

-          * requiring a window save (memcpy of up to 32768 output bytes)

-          * every ZLIB_IO_MAX input bytes.

-          */

-         avail_in += png_ptr->zstream.avail_in; /* not consumed last time */

-

-         avail = ZLIB_IO_MAX;

-

-         if (avail_in < avail)

-            avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */

-

-         avail_in -= avail;

-         png_ptr->zstream.avail_in = avail;

-

-         /* zlib OUTPUT BUFFER */

-         avail_out += png_ptr->zstream.avail_out; /* not written last time */

-

-         avail = ZLIB_IO_MAX; /* maximum zlib can process */

-

-         if (output == NULL)

-         {

-            /* Reset the output buffer each time round if output is NULL and

-             * make available the full buffer, up to 'remaining_space'

-             */

-            png_ptr->zstream.next_out = local_buffer;

-            if ((sizeof local_buffer) < avail)

-               avail = (sizeof local_buffer);

-         }

-

-         if (avail_out < avail)

-            avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */

-

-         png_ptr->zstream.avail_out = avail;

-         avail_out -= avail;

-

-         /* zlib inflate call */

-         /* In fact 'avail_out' may be 0 at this point, that happens at the end

-          * of the read when the final LZ end code was not passed at the end of

-          * the previous chunk of input data.  Tell zlib if we have reached the

-          * end of the output buffer.

-          */

-         ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH :

-            (finish ? Z_FINISH : Z_SYNC_FLUSH));

-      } while (ret == Z_OK);

-

-      /* For safety kill the local buffer pointer now */

-      if (output == NULL)

-         png_ptr->zstream.next_out = NULL;

-

-      /* Claw back the 'size' and 'remaining_space' byte counts. */

-      avail_in += png_ptr->zstream.avail_in;

-      avail_out += png_ptr->zstream.avail_out;

-

-      /* Update the input and output sizes; the updated values are the amount

-       * consumed or written, effectively the inverse of what zlib uses.

-       */

-      if (avail_out > 0)

-         *output_size_ptr -= avail_out;

-

-      if (avail_in > 0)

-         *input_size_ptr -= avail_in;

-

-      /* Ensure png_ptr->zstream.msg is set (even in the success case!) */

-      png_zstream_error(png_ptr, ret);

-      return ret;

-   }

-

-   else

-   {

-      /* 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 read_buffer

- * points at an allocated area holding the contents of a chunk with a

- * trailing compressed part.  What we get back is an allocated area

- * holding the original prefix part and an uncompressed version of the

- * trailing part (the malloc area passed in is freed).

- */

-static 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*/)

-{

-   /* 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

-

-   if (limit >= prefix_size + (terminate != 0))

-   {

-      int ret;

-

-      limit -= prefix_size + (terminate != 0);

-

-      if (limit < *newlength)

-         *newlength = limit;

-

-      /* Now try to claim the stream. */

-      ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);

-

-      if (ret == Z_OK)

-      {

-         png_uint_32 lzsize = chunklength - prefix_size;

-

-         ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,

-            /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,

-            /* output: */ NULL, newlength);

-

-         if (ret == Z_STREAM_END)

-         {

-            /* 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)

-            {

-               /* 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);

-               }

-            }

-

-            else

-            {

-               /* inflateReset failed, store the error message */

-               png_zstream_error(png_ptr, ret);

-

-               if (ret == Z_STREAM_END)

-                  ret = PNG_UNEXPECTED_ZLIB_RETURN;

-            }

-         }

-

-         else if (ret == Z_OK)

-            ret = PNG_UNEXPECTED_ZLIB_RETURN;

-

-         /* Release the claimed stream */

-         png_ptr->zowner = 0;

-      }

-

-      else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */

-         ret = PNG_UNEXPECTED_ZLIB_RETURN;

-

-      return ret;

-   }

-

-   else

-   {

-      /* Application/configuration limits exceeded */

-      png_zstream_error(png_ptr, Z_MEM_ERROR);

-      return Z_MEM_ERROR;

-   }

-}

-#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */

-

-#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)

-   {

-      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 */

-

-      do

-      {

-         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_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;

-         }

-

-         /* 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));

-      }

-      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;

-   }

-

-   else

-   {

-      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");

-      return Z_STREAM_ERROR;

-   }

-}

-#endif

-

-/* Read and check the IDHR chunk */

-void /* PRIVATE */

-png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_byte buf[13];

-   png_uint_32 width, height;

-   int bit_depth, color_type, compression_type, filter_type;

-   int interlace_type;

-

-   png_debug(1, "in png_handle_IHDR");

-

-   if (png_ptr->mode & PNG_HAVE_IHDR)

-      png_chunk_error(png_ptr, "out of place");

-

-   /* Check the length */

-   if (length != 13)

-      png_chunk_error(png_ptr, "invalid");

-

-   png_ptr->mode |= PNG_HAVE_IHDR;

-

-   png_crc_read(png_ptr, buf, 13);

-   png_crc_finish(png_ptr, 0);

-

-   width = png_get_uint_31(png_ptr, buf);

-   height = png_get_uint_31(png_ptr, buf + 4);

-   bit_depth = buf[8];

-   color_type = buf[9];

-   compression_type = buf[10];

-   filter_type = buf[11];

-   interlace_type = buf[12];

-

-   /* Set internal variables */

-   png_ptr->width = width;

-   png_ptr->height = height;

-   png_ptr->bit_depth = (png_byte)bit_depth;

-   png_ptr->interlaced = (png_byte)interlace_type;

-   png_ptr->color_type = (png_byte)color_type;

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   png_ptr->filter_type = (png_byte)filter_type;

-#endif

-   png_ptr->compression_type = (png_byte)compression_type;

-

-   /* Find number of channels */

-   switch (png_ptr->color_type)

-   {

-      default: /* invalid, png_set_IHDR calls png_error */

-      case PNG_COLOR_TYPE_GRAY:

-      case PNG_COLOR_TYPE_PALETTE:

-         png_ptr->channels = 1;

-         break;

-

-      case PNG_COLOR_TYPE_RGB:

-         png_ptr->channels = 3;

-         break;

-

-      case PNG_COLOR_TYPE_GRAY_ALPHA:

-         png_ptr->channels = 2;

-         break;

-

-      case PNG_COLOR_TYPE_RGB_ALPHA:

-         png_ptr->channels = 4;

-         break;

-   }

-

-   /* Set up other useful info */

-   png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth *

-   png_ptr->channels);

-   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width);

-   png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);

-   png_debug1(3, "channels = %d", png_ptr->channels);

-   png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);

-   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,

-       color_type, interlace_type, compression_type, filter_type);

-}

-

-/* Read and check the palette */

-void /* PRIVATE */

-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;

-#ifdef PNG_POINTER_INDEXING_SUPPORTED

-   png_colorp pal_ptr;

-#endif

-

-   png_debug(1, "in png_handle_PLTE");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      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)

-   {

-      /* 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;

-   }

-

-   png_ptr->mode |= PNG_HAVE_PLTE;

-

-   if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");

-      return;

-   }

-

-#ifndef PNG_READ_OPT_PLTE_SUPPORTED

-   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)

-   {

-      png_crc_finish(png_ptr, length);

-      return;

-   }

-#endif

-

-   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_chunk_benign_error(png_ptr, "invalid");

-

-      else

-         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

-   for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)

-   {

-      png_byte buf[3];

-

-      png_crc_read(png_ptr, buf, 3);

-      pal_ptr->red = buf[0];

-      pal_ptr->green = buf[1];

-      pal_ptr->blue = buf[2];

-   }

-#else

-   for (i = 0; i < num; i++)

-   {

-      png_byte buf[3];

-

-      png_crc_read(png_ptr, buf, 3);

-      /* Don't depend upon png_color being any order */

-      palette[i].red = buf[0];

-      palette[i].green = buf[1];

-      palette[i].blue = buf[2];

-   }

-#endif

-

-   /* If we actually need the PLTE chunk (ie for a paletted image), we do

-    * whatever the normal CRC configuration tells us.  However, if we

-    * have an RGB image, the PLTE can be considered ancillary, so

-    * we will act as though it is.

-    */

-#ifndef PNG_READ_OPT_PLTE_SUPPORTED

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-#endif

-   {

-      png_crc_finish(png_ptr, 0);

-   }

-

-#ifndef PNG_READ_OPT_PLTE_SUPPORTED

-   else if (png_crc_error(png_ptr))  /* Only if we have a CRC error */

-   {

-      /* If we don't want to use the data from an ancillary chunk,

-       * 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))

-      {

-         if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)

-         {

-            png_chunk_benign_error(png_ptr, "CRC error");

-         }

-

-         else

-         {

-            png_chunk_warning(png_ptr, "CRC error");

-            return;

-         }

-      }

-

-      /* Otherwise, we (optionally) emit a warning and use the chunk. */

-      else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN))

-      {

-         png_chunk_warning(png_ptr, "CRC error");

-      }

-   }

-#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->num_trans > 0 ||

-      (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))

-   {

-      /* 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 != 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_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_chunk_error(png_ptr, "out of place");

-

-   png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);

-

-   png_crc_finish(png_ptr, length);

-

-   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_fixed_point igamma;

-   png_byte buf[4];

-

-   png_debug(1, "in png_handle_gAMA");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of place");

-      return;

-   }

-

-   if (length != 4)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, 4);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   igamma = png_get_fixed_point(NULL, buf);

-

-   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   unsigned int truelen;

-   png_byte buf[4];

-

-   png_debug(1, "in png_handle_sBIT");

-

-   buf[0] = buf[1] = buf[2] = buf[3] = 0;

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of place");

-      return;

-   }

-

-   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      truelen = 3;

-

-   else

-      truelen = png_ptr->channels;

-

-   if (length != truelen || length > 4)

-   {

-      png_chunk_benign_error(png_ptr, "invalid");

-      png_crc_finish(png_ptr, length);

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, truelen);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)

-   {

-      png_ptr->sig_bit.red = buf[0];

-      png_ptr->sig_bit.green = buf[1];

-      png_ptr->sig_bit.blue = buf[2];

-      png_ptr->sig_bit.alpha = buf[3];

-   }

-

-   else

-   {

-      png_ptr->sig_bit.gray = buf[0];

-      png_ptr->sig_bit.red = buf[0];

-      png_ptr->sig_bit.green = buf[0];

-      png_ptr->sig_bit.blue = buf[0];

-      png_ptr->sig_bit.alpha = buf[1];

-   }

-

-   png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));

-}

-#endif

-

-#ifdef PNG_READ_cHRM_SUPPORTED

-void /* PRIVATE */

-png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_byte buf[32];

-   png_xy xy;

-

-   png_debug(1, "in png_handle_cHRM");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of place");

-      return;

-   }

-

-   if (length != 32)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, 32);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   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;

-   }

-

-   /* If a colorspace error has already been output skip this chunk */

-   if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)

-      return;

-

-   if (png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM)

-   {

-      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;

-      png_colorspace_sync(png_ptr, info_ptr);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_byte intent;

-

-   png_debug(1, "in png_handle_sRGB");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of place");

-      return;

-   }

-

-   if (length != 1)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, &intent, 1);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   /* If a colorspace error has already been output skip this chunk */

-   if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)

-      return;

-

-   /* 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)

-   {

-      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;

-      png_colorspace_sync(png_ptr, info_ptr);

-      png_chunk_benign_error(png_ptr, "too many profiles");

-      return;

-   }

-

-   (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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-/* Note: this does not properly handle profiles that are > 64K under DOS */

-{

-   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_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of place");

-      return;

-   }

-

-   /* 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_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "too short");

-      return;

-   }

-

-   /* If a colorspace error has already been output skip this chunk */

-   if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID)

-   {

-      png_crc_finish(png_ptr, length);

-      return;

-   }

-

-   /* 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)

-   {

-      uInt read_length, keyword_length;

-      char keyword[81];

-

-      /* 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;

-

-      png_crc_read(png_ptr, (png_bytep)keyword, read_length);

-      length -= read_length;

-

-      keyword_length = 0;

-      while (keyword_length < 80 && keyword_length < read_length &&

-         keyword[keyword_length] != 0)

-         ++keyword_length;

-

-      /* 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;

-

-            if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK)

-            {

-               Byte profile_header[132];

-               Byte local_buffer[PNG_INFLATE_BUF_SIZE];

-               png_alloc_size_t size = (sizeof profile_header);

-

-               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;

-                        }

-

-                        else

-                           errmsg = "out of memory";

-                     }

-

-                     /* else png_icc_check_header output an error */

-                  }

-

-                  /* else png_icc_check_length output an error */

-               }

-

-               else /* profile truncated */

-                  errmsg = png_ptr->zstream.msg;

-

-               /* Release the stream */

-               png_ptr->zowner = 0;

-            }

-

-            else /* png_inflate_claim failed */

-               errmsg = png_ptr->zstream.msg;

-         }

-

-         else

-            errmsg = "bad compression method"; /* or missing */

-      }

-

-      else

-         errmsg = "bad keyword";

-   }

-

-   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_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, 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_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)

-      {

-         png_crc_finish(png_ptr, length);

-         return;

-      }

-

-      if (--png_ptr->user_chunk_cache_max == 1)

-      {

-         png_warning(png_ptr, "No space in chunk cache for sPLT");

-         png_crc_finish(png_ptr, length);

-         return;

-      }

-   }

-#endif

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & PNG_HAVE_IDAT)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of place");

-      return;

-   }

-

-#ifdef PNG_MAX_MALLOC_64K

-   if (length > 65535U)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "too large to fit in memory");

-      return;

-   }

-#endif

-

-   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.

-    */

-   png_crc_read(png_ptr, buffer, length);

-

-   if (png_crc_finish(png_ptr, skip))

-      return;

-

-   buffer[length] = 0;

-

-   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 > buffer + length - 2)

-   {

-      png_warning(png_ptr, "malformed sPLT chunk");

-      return;

-   }

-

-   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.

-    */

-   data_length = length - (png_uint_32)(entry_start - buffer);

-

-   /* Integrity-check the data length */

-   if (data_length % entry_size)

-   {

-      png_warning(png_ptr, "sPLT chunk has bad length");

-      return;

-   }

-

-   dl = (png_int_32)(data_length / entry_size);

-   max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry));

-

-   if (dl > max_dl)

-   {

-       png_warning(png_ptr, "sPLT chunk too long");

-       return;

-   }

-

-   new_palette.nentries = (png_int_32)(data_length / entry_size);

-

-   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(

-       png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)));

-

-   if (new_palette.entries == NULL)

-   {

-       png_warning(png_ptr, "sPLT chunk requires too much memory");

-       return;

-   }

-

-#ifdef PNG_POINTER_INDEXING_SUPPORTED

-   for (i = 0; i < new_palette.nentries; i++)

-   {

-      pp = new_palette.entries + i;

-

-      if (new_palette.depth == 8)

-      {

-         pp->red = *entry_start++;

-         pp->green = *entry_start++;

-         pp->blue = *entry_start++;

-         pp->alpha = *entry_start++;

-      }

-

-      else

-      {

-         pp->red   = png_get_uint_16(entry_start); entry_start += 2;

-         pp->green = png_get_uint_16(entry_start); entry_start += 2;

-         pp->blue  = png_get_uint_16(entry_start); entry_start += 2;

-         pp->alpha = png_get_uint_16(entry_start); entry_start += 2;

-      }

-

-      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;

-   }

-#else

-   pp = new_palette.entries;

-

-   for (i = 0; i < new_palette.nentries; i++)

-   {

-

-      if (new_palette.depth == 8)

-      {

-         pp[i].red   = *entry_start++;

-         pp[i].green = *entry_start++;

-         pp[i].blue  = *entry_start++;

-         pp[i].alpha = *entry_start++;

-      }

-

-      else

-      {

-         pp[i].red   = png_get_uint_16(entry_start); entry_start += 2;

-         pp[i].green = png_get_uint_16(entry_start); entry_start += 2;

-         pp[i].blue  = png_get_uint_16(entry_start); entry_start += 2;

-         pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;

-      }

-

-      pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2;

-   }

-#endif

-

-   /* Discard all chunk data except the name and stash that */

-   new_palette.name = (png_charp)buffer;

-

-   png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);

-

-   png_free(png_ptr, new_palette.entries);

-}

-#endif /* PNG_READ_sPLT_SUPPORTED */

-

-#ifdef PNG_READ_tRNS_SUPPORTED

-void /* PRIVATE */

-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_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & PNG_HAVE_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_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)

-   {

-      png_byte buf[2];

-

-      if (length != 2)

-      {

-         png_crc_finish(png_ptr, length);

-         png_chunk_benign_error(png_ptr, "invalid");

-         return;

-      }

-

-      png_crc_read(png_ptr, buf, 2);

-      png_ptr->num_trans = 1;

-      png_ptr->trans_color.gray = png_get_uint_16(buf);

-   }

-

-   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)

-   {

-      png_byte buf[6];

-

-      if (length != 6)

-      {

-         png_crc_finish(png_ptr, length);

-         png_chunk_benign_error(png_ptr, "invalid");

-         return;

-      }

-

-      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);

-      png_ptr->trans_color.blue = png_get_uint_16(buf + 4);

-   }

-

-   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-   {

-      if (!(png_ptr->mode & PNG_HAVE_PLTE))

-      {

-         /* 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 > png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH ||

-         length == 0)

-      {

-         png_crc_finish(png_ptr, length);

-         png_chunk_benign_error(png_ptr, "invalid");

-         return;

-      }

-

-      png_crc_read(png_ptr, readbuf, length);

-      png_ptr->num_trans = (png_uint_16)length;

-   }

-

-   else

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid with alpha channel");

-      return;

-   }

-

-   if (png_crc_finish(png_ptr, 0))

-   {

-      png_ptr->num_trans = 0;

-      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));

-}

-#endif

-

-#ifdef PNG_READ_bKGD_SUPPORTED

-void /* PRIVATE */

-png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   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_chunk_error(png_ptr, "missing IHDR");

-

-   else if ((png_ptr->mode & PNG_HAVE_IDAT) ||

-      (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&

-       !(png_ptr->mode & PNG_HAVE_PLTE)))

-   {

-      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_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      truelen = 1;

-

-   else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)

-      truelen = 6;

-

-   else

-      truelen = 2;

-

-   if (length != truelen)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, truelen);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   /* We convert the index value into RGB components so that we can allow

-    * arbitrary RGB values for background when we have transparency, and

-    * so it is easy to determine the RGB values of the background color

-    * from the info_ptr struct.

-    */

-   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-   {

-      background.index = buf[0];

-

-      if (info_ptr && info_ptr->num_palette)

-      {

-         if (buf[0] >= info_ptr->num_palette)

-         {

-            png_chunk_benign_error(png_ptr, "invalid index");

-            return;

-         }

-

-         background.red = (png_uint_16)png_ptr->palette[buf[0]].red;

-         background.green = (png_uint_16)png_ptr->palette[buf[0]].green;

-         background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue;

-      }

-

-      else

-         background.red = background.green = background.blue = 0;

-

-      background.gray = 0;

-   }

-

-   else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */

-   {

-      background.index = 0;

-      background.red =

-      background.green =

-      background.blue =

-      background.gray = png_get_uint_16(buf);

-   }

-

-   else

-   {

-      background.index = 0;

-      background.red = png_get_uint_16(buf);

-      background.green = png_get_uint_16(buf + 2);

-      background.blue = png_get_uint_16(buf + 4);

-      background.gray = 0;

-   }

-

-   png_set_bKGD(png_ptr, info_ptr, &background);

-}

-#endif

-

-#ifdef PNG_READ_hIST_SUPPORTED

-void /* PRIVATE */

-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];

-

-   png_debug(1, "in png_handle_hIST");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE))

-   {

-      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_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   num = length / 2 ;

-

-   if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   for (i = 0; i < num; i++)

-   {

-      png_byte buf[2];

-

-      png_crc_read(png_ptr, buf, 2);

-      readbuf[i] = png_get_uint_16(buf);

-   }

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   png_set_hIST(png_ptr, info_ptr, readbuf);

-}

-#endif

-

-#ifdef PNG_READ_pHYs_SUPPORTED

-void /* PRIVATE */

-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;

-   int unit_type;

-

-   png_debug(1, "in png_handle_pHYs");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & PNG_HAVE_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_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   if (length != 9)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, 9);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   res_x = png_get_uint_32(buf);

-   res_y = png_get_uint_32(buf + 4);

-   unit_type = buf[8];

-   png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);

-}

-#endif

-

-#ifdef PNG_READ_oFFs_SUPPORTED

-void /* PRIVATE */

-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;

-   int unit_type;

-

-   png_debug(1, "in png_handle_oFFs");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & PNG_HAVE_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_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   if (length != 9)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, 9);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   offset_x = png_get_int_32(buf);

-   offset_y = png_get_int_32(buf + 4);

-   unit_type = buf[8];

-   png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);

-}

-#endif

-

-#ifdef PNG_READ_pCAL_SUPPORTED

-/* Read the pCAL chunk (described in the PNG Extensions document) */

-void /* PRIVATE */

-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_bytep buffer, buf, units, endptr;

-   png_charpp params;

-   int i;

-

-   png_debug(1, "in png_handle_pCAL");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & PNG_HAVE_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_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);

-

-   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;

-   }

-

-   png_crc_read(png_ptr, buffer, length);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   buffer[length] = 0; /* Null terminate the last string */

-

-   png_debug(3, "Finding end of pCAL purpose string");

-   for (buf = buffer; *buf; buf++)

-      /* Empty loop */ ;

-

-   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_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_debug(3, "Reading pCAL X0, X1, type, nparams, and units");

-   X0 = png_get_int_32((png_bytep)buf+1);

-   X1 = png_get_int_32((png_bytep)buf+5);

-   type = buf[9];

-   nparams = buf[10];

-   units = buf + 11;

-

-   png_debug(3, "Checking pCAL equation type and number of parameters");

-   /* Check that we have the right number of parameters for known

-    * equation types.

-    */

-   if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||

-       (type == PNG_EQUATION_BASE_E && nparams != 3) ||

-       (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||

-       (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))

-   {

-      png_chunk_benign_error(png_ptr, "invalid parameter count");

-      return;

-   }

-

-   else if (type >= PNG_EQUATION_LAST)

-   {

-      png_chunk_benign_error(png_ptr, "unrecognized equation type");

-   }

-

-   for (buf = units; *buf; buf++)

-      /* Empty loop to move past the units string. */ ;

-

-   png_debug(3, "Allocating pCAL parameters array");

-

-   params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,

-       nparams * (sizeof (png_charp))));

-

-   if (params == NULL)

-   {

-      png_chunk_benign_error(png_ptr, "out of memory");

-      return;

-   }

-

-   /* Get pointers to the start of each parameter string. */

-   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] = (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_free(png_ptr, params);

-         png_chunk_benign_error(png_ptr, "invalid data");

-         return;

-      }

-   }

-

-   png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,

-      (png_charp)units, params);

-

-   png_free(png_ptr, params);

-}

-#endif

-

-#ifdef PNG_READ_sCAL_SUPPORTED

-/* Read the sCAL chunk */

-void /* PRIVATE */

-png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_bytep buffer;

-   png_size_t i;

-   int state;

-

-   png_debug(1, "in png_handle_sCAL");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (png_ptr->mode & PNG_HAVE_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_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_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);

-

-   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);

-

-   if (buffer == NULL)

-   {

-      png_chunk_benign_error(png_ptr, "out of memory");

-      png_crc_finish(png_ptr, length);

-      return;

-   }

-

-   png_crc_read(png_ptr, buffer, length);

-   buffer[length] = 0; /* Null terminate the last string */

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   /* Validate the unit. */

-   if (buffer[0] != 1 && buffer[0] != 2)

-   {

-      png_chunk_benign_error(png_ptr, "invalid unit");

-      return;

-   }

-

-   /* Validate the ASCII numbers, need two ASCII numbers separated by

-    * a '\0' and they need to fit exactly in the chunk data.

-    */

-   i = 1;

-   state = 0;

-

-   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_chunk_benign_error(png_ptr, "non-positive width");

-

-   else

-   {

-      png_size_t heighti = i;

-

-      state = 0;

-      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_chunk_benign_error(png_ptr, "non-positive height");

-

-      else

-         /* This is the (only) success case. */

-         png_set_sCAL_s(png_ptr, info_ptr, buffer[0],

-            (png_charp)buffer+1, (png_charp)buffer+heighti);

-   }

-}

-#endif

-

-#ifdef PNG_READ_tIME_SUPPORTED

-void /* PRIVATE */

-png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_byte buf[7];

-   png_time mod_time;

-

-   png_debug(1, "in png_handle_tIME");

-

-   if (!(png_ptr->mode & PNG_HAVE_IHDR))

-      png_chunk_error(png_ptr, "missing IHDR");

-

-   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "duplicate");

-      return;

-   }

-

-   if (png_ptr->mode & PNG_HAVE_IDAT)

-      png_ptr->mode |= PNG_AFTER_IDAT;

-

-   if (length != 7)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "invalid");

-      return;

-   }

-

-   png_crc_read(png_ptr, buf, 7);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   mod_time.second = buf[6];

-   mod_time.minute = buf[5];

-   mod_time.hour = buf[4];

-   mod_time.day = buf[3];

-   mod_time.month = buf[2];

-   mod_time.year = png_get_uint_16(buf);

-

-   png_set_tIME(png_ptr, info_ptr, &mod_time);

-}

-#endif

-

-#ifdef PNG_READ_tEXt_SUPPORTED

-/* Note: this does not properly handle chunks that are > 64K under DOS */

-void /* PRIVATE */

-png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_text  text_info;

-   png_bytep buffer;

-   png_charp key;

-   png_charp text;

-   png_uint_32 skip = 0;

-

-   png_debug(1, "in png_handle_tEXt");

-

-#ifdef PNG_USER_LIMITS_SUPPORTED

-   if (png_ptr->user_chunk_cache_max != 0)

-   {

-      if (png_ptr->user_chunk_cache_max == 1)

-      {

-         png_crc_finish(png_ptr, length);

-         return;

-      }

-

-      if (--png_ptr->user_chunk_cache_max == 1)

-      {

-         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_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 > 65535U)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "too large to fit in memory");

-      return;

-   }

-#endif

-

-   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);

-

-   if (buffer == NULL)

-   {

-     png_chunk_benign_error(png_ptr, "out of memory");

-     return;

-   }

-

-   png_crc_read(png_ptr, buffer, length);

-

-   if (png_crc_finish(png_ptr, skip))

-      return;

-

-   key = (png_charp)buffer;

-   key[length] = 0;

-

-   for (text = key; *text; text++)

-      /* Empty loop to find end of key */ ;

-

-   if (text != key + length)

-      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 (png_set_text_2(png_ptr, info_ptr, &text_info, 1))

-      png_warning(png_ptr, "Insufficient memory to process text chunk");

-}

-#endif

-

-#ifdef PNG_READ_zTXt_SUPPORTED

-/* Note: this does not correctly handle chunks that are > 64K under DOS */

-void /* PRIVATE */

-png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_const_charp errmsg = NULL;

-   png_bytep       buffer;

-   png_uint_32     keyword_length;

-

-   png_debug(1, "in png_handle_zTXt");

-

-#ifdef PNG_USER_LIMITS_SUPPORTED

-   if (png_ptr->user_chunk_cache_max != 0)

-   {

-      if (png_ptr->user_chunk_cache_max == 1)

-      {

-         png_crc_finish(png_ptr, length);

-         return;

-      }

-

-      if (--png_ptr->user_chunk_cache_max == 1)

-      {

-         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_chunk_error(png_ptr, "missing IHDR");

-

-   if (png_ptr->mode & PNG_HAVE_IDAT)

-      png_ptr->mode |= PNG_AFTER_IDAT;

-

-   buffer = png_read_buffer(png_ptr, length, 2/*silent*/);

-

-   if (buffer == NULL)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of memory");

-      return;

-   }

-

-   png_crc_read(png_ptr, buffer, length);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   /* 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 */ ;

-

-   if (keyword_length > 79 || keyword_length < 1)

-      errmsg = "bad keyword";

-

-   /* 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

-   {

-      png_alloc_size_t uncompressed_length = PNG_SIZE_MAX;

-

-      /* 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;

-

-         /* 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";

-      }

-

-      else

-         errmsg = png_ptr->zstream.msg;

-   }

-

-   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)

-{

-   png_const_charp errmsg = NULL;

-   png_bytep buffer;

-   png_uint_32 prefix_length;

-

-   png_debug(1, "in png_handle_iTXt");

-

-#ifdef PNG_USER_LIMITS_SUPPORTED

-   if (png_ptr->user_chunk_cache_max != 0)

-   {

-      if (png_ptr->user_chunk_cache_max == 1)

-      {

-         png_crc_finish(png_ptr, length);

-         return;

-      }

-

-      if (--png_ptr->user_chunk_cache_max == 1)

-      {

-         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_chunk_error(png_ptr, "missing IHDR");

-

-   if (png_ptr->mode & PNG_HAVE_IDAT)

-      png_ptr->mode |= PNG_AFTER_IDAT;

-

-   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);

-

-   if (buffer == NULL)

-   {

-      png_crc_finish(png_ptr, length);

-      png_chunk_benign_error(png_ptr, "out of memory");

-      return;

-   }

-

-   png_crc_read(png_ptr, buffer, length);

-

-   if (png_crc_finish(png_ptr, 0))

-      return;

-

-   /* First the keyword. */

-   for (prefix_length=0;

-      prefix_length < length && buffer[prefix_length] != 0;

-      ++prefix_length)

-      /* Empty loop */ ;

-

-   /* Perform a basic check on the keyword length here. */

-   if (prefix_length > 79 || prefix_length < 1)

-      errmsg = "bad 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";

-

-   else if (buffer[prefix_length+1] == 0 ||

-      (buffer[prefix_length+1] == 1 &&

-      buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))

-   {

-      int compressed = buffer[prefix_length+1] != 0;

-      png_uint_32 language_offset, translated_keyword_offset;

-      png_alloc_size_t uncompressed_length = 0;

-

-      /* Now the language tag */

-      prefix_length += 3;

-      language_offset = prefix_length;

-

-      for (; prefix_length < length && buffer[prefix_length] != 0;

-         ++prefix_length)

-         /* Empty loop */ ;

-

-      /* WARNING: the length may be invalid here, this is checked below. */

-      translated_keyword_offset = ++prefix_length;

-

-      for (; prefix_length < length && buffer[prefix_length] != 0;

-         ++prefix_length)

-         /* Empty loop */ ;

-

-      /* 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;

-

-      if (!compressed && prefix_length <= length)

-         uncompressed_length = length - prefix_length;

-

-      else if (compressed && prefix_length < length)

-      {

-         uncompressed_length = PNG_SIZE_MAX;

-

-         /* 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;

-

-         else

-            errmsg = png_ptr->zstream.msg;

-      }

-

-      else

-         errmsg = "truncated";

-

-      if (errmsg == NULL)

-      {

-         png_text text;

-

-         buffer[uncompressed_length+prefix_length] = 0;

-

-         if (compressed)

-            text.compression = PNG_ITXT_COMPRESSION_NONE;

-

-         else

-            text.compression = PNG_ITXT_COMPRESSION_zTXt;

-

-         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;

-

-         if (png_set_text_2(png_ptr, info_ptr, &text, 1))

-            errmsg = "insufficient memory";

-      }

-   }

-

-   else

-      errmsg = "bad compression info";

-

-   if (errmsg != NULL)

-      png_chunk_benign_error(png_ptr, errmsg);

-}

-#endif

-

-#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_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;

-   }

-

-#  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

-

-   if (length <= limit)

-   {

-      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 (length == 0)

-         png_ptr->unknown_chunk.data = NULL;

-

-      else

-      {

-         /* Do a 'warn' here - it is handled below. */

-         png_ptr->unknown_chunk.data = png_voidcast(png_bytep,

-            png_malloc_warn(png_ptr, length));

-      }

-   }

-

-   if (png_ptr->unknown_chunk.data == NULL && length > 0)

-   {

-      /* 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;

-   }

-

-   else

-   {

-      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

-   /* 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

-

-   /* 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.

-       */

-      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");

-

-            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

-      {

-         /* 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

-

-      {

-         /* 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");

-

-         png_crc_finish(png_ptr, length);

-      }

-#  endif

-

-#  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)

-         {

-            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;

-

-            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

-

-   /* 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;

-

-#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 */

-

-   /* 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.

- * This function can't have the "critical chunk check" incorporated

- * into it, since in the future we will need to be able to call user

- * functions to handle unknown critical chunks after we check that

- * the chunk name itself is valid.

- */

-

-/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is:

- *

- * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))

- */

-

-void /* PRIVATE */

-png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name)

-{

-   int i;

-

-   png_debug(1, "in png_check_chunk_name");

-

-   for (i=1; i<=4; ++i)

-   {

-      int c = chunk_name & 0xff;

-

-      if (c < 65 || c > 122 || (c > 90 && c < 97))

-         png_chunk_error(png_ptr, "invalid chunk type");

-

-      chunk_name >>= 8;

-   }

-}

-

-/* Combines the row recently read in with the existing pixels in the row.  This

- * routine takes care of alpha and transparency if requested.  This routine also

- * handles the two methods of progressive display of interlaced images,

- * depending on the 'display' value; if 'display' is true then the whole row

- * (dp) is filled from the start by replicating the available pixels.  If

- * 'display' is false only those pixels present in the pass are filled in.

- */

-void /* PRIVATE */

-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;

-   png_uint_32 row_width = png_ptr->width;

-   unsigned int pass = png_ptr->pass;

-   png_bytep end_ptr = 0;

-   png_byte end_byte = 0;

-   unsigned int end_mask;

-

-   png_debug(1, "in png_combine_row");

-

-   /* Added in 1.5.6: it should not be possible to enter this routine until at

-    * least one row has been read from the PNG data and transformed.

-    */

-   if (pixel_depth == 0)

-      png_error(png_ptr, "internal row logic error");

-

-   /* Added in 1.5.4: the pixel depth should match the information returned by

-    * any call to png_read_update_info at this point.  Do not continue if we got

-    * this wrong.

-    */

-   if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes !=

-          PNG_ROWBYTES(pixel_depth, row_width))

-      png_error(png_ptr, "internal row size calculation error");

-

-   /* Don't expect this to ever happen: */

-   if (row_width == 0)

-      png_error(png_ptr, "internal row width error");

-

-   /* Preserve the last byte in cases where only part of it will be overwritten,

-    * the multiply below may overflow, we don't care because ANSI-C guarantees

-    * we get the low bits.

-    */

-   end_mask = (pixel_depth * row_width) & 7;

-   if (end_mask != 0)

-   {

-      /* end_ptr == NULL is a flag to say do nothing */

-      end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1;

-      end_byte = *end_ptr;

-#     ifdef PNG_READ_PACKSWAP_SUPPORTED

-         if (png_ptr->transformations & PNG_PACKSWAP) /* little-endian byte */

-            end_mask = 0xff << end_mask;

-

-         else /* big-endian byte */

-#     endif

-         end_mask = 0xff >> end_mask;

-      /* end_mask is now the bits to *keep* from the destination row */

-   }

-

-   /* 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

-    * pass.

-    */

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE) &&

-      pass < 6 && (display == 0 ||

-      /* The following copies everything for 'display' on passes 0, 2 and 4. */

-      (display == 1 && (pass & 1) != 0)))

-   {

-      /* Narrow images may have no bits in a pass; the caller should handle

-       * this, but this test is cheap:

-       */

-      if (row_width <= PNG_PASS_START_COL(pass))

-         return;

-

-      if (pixel_depth < 8)

-      {

-         /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit

-          * into 32 bits, then a single loop over the bytes using the four byte

-          * values in the 32-bit mask can be used.  For the 'display' option the

-          * expanded mask may also not require any masking within a byte.  To

-          * make this work the PACKSWAP option must be taken into account - it

-          * simply requires the pixels to be reversed in each byte.

-          *

-          * The 'regular' case requires a mask for each of the first 6 passes,

-          * the 'display' case does a copy for the even passes in the range

-          * 0..6.  This has already been handled in the test above.

-          *

-          * The masks are arranged as four bytes with the first byte to use in

-          * the lowest bits (little-endian) regardless of the order (PACKSWAP or

-          * not) of the pixels in each byte.

-          *

-          * NOTE: the whole of this logic depends on the caller of this function

-          * only calling it on rows appropriate to the pass.  This function only

-          * understands the 'x' logic; the 'y' logic is handled by the caller.

-          *

-          * The following defines allow generation of compile time constant bit

-          * masks for each pixel depth and each possibility of swapped or not

-          * swapped bytes.  Pass 'p' is in the range 0..6; 'x', a pixel index,

-          * is in the range 0..7; and the result is 1 if the pixel is to be

-          * copied in the pass, 0 if not.  'S' is for the sparkle method, 'B'

-          * for the block method.

-          *

-          * With some compilers a compile time expression of the general form:

-          *

-          *    (shift >= 32) ? (a >> (shift-32)) : (b >> shift)

-          *

-          * Produces warnings with values of 'shift' in the range 33 to 63

-          * because the right hand side of the ?: expression is evaluated by

-          * the compiler even though it isn't used.  Microsoft Visual C (various

-          * versions) and the Intel C compiler are known to do this.  To avoid

-          * this the following macros are used in 1.5.6.  This is a temporary

-          * solution to avoid destabilizing the code during the release process.

-          */

-#        if PNG_USE_COMPILE_TIME_MASKS

-#           define PNG_LSR(x,s) ((x)>>((s) & 0x1f))

-#           define PNG_LSL(x,s) ((x)<<((s) & 0x1f))

-#        else

-#           define PNG_LSR(x,s) ((x)>>(s))

-#           define PNG_LSL(x,s) ((x)<<(s))

-#        endif

-#        define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\

-           PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1)

-#        define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\

-           PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1)

-

-         /* Return a mask for pass 'p' pixel 'x' at depth 'd'.  The mask is

-          * little endian - the first pixel is at bit 0 - however the extra

-          * parameter 's' can be set to cause the mask position to be swapped

-          * within each byte, to match the PNG format.  This is done by XOR of

-          * the shift with 7, 6 or 4 for bit depths 1, 2 and 4.

-          */

-#        define PIXEL_MASK(p,x,d,s) \

-            (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0))))

-

-         /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask.

-          */

-#        define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)

-#        define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)

-

-         /* Combine 8 of these to get the full mask.  For the 1-bpp and 2-bpp

-          * cases the result needs replicating, for the 4-bpp case the above

-          * generates a full 32 bits.

-          */

-#        define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1)))

-

-#        define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\

-            S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\

-            S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d)

-

-#        define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\

-            B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\

-            B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d)

-

-#if PNG_USE_COMPILE_TIME_MASKS

-         /* Utility macros to construct all the masks for a depth/swap

-          * combination.  The 's' parameter says whether the format is PNG

-          * (big endian bytes) or not.  Only the three odd-numbered passes are

-          * required for the display/block algorithm.

-          */

-#        define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\

-            S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) }

-

-#        define B_MASKS(d,s) { B_MASK(1,d,s), S_MASK(3,d,s), S_MASK(5,d,s) }

-

-#        define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2))

-

-         /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and

-          * then pass:

-          */

-         static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] =

-         {

-            /* Little-endian byte masks for PACKSWAP */

-            { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) },

-            /* Normal (big-endian byte) masks - PNG format */

-            { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) }

-         };

-

-         /* display_mask has only three entries for the odd passes, so index by

-          * pass>>1.

-          */

-         static PNG_CONST png_uint_32 display_mask[2][3][3] =

-         {

-            /* Little-endian byte masks for PACKSWAP */

-            { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) },

-            /* Normal (big-endian byte) masks - PNG format */

-            { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) }

-         };

-

-#        define MASK(pass,depth,display,png)\

-            ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\

-               row_mask[png][DEPTH_INDEX(depth)][pass])

-

-#else /* !PNG_USE_COMPILE_TIME_MASKS */

-         /* This is the runtime alternative: it seems unlikely that this will

-          * ever be either smaller or faster than the compile time approach.

-          */

-#        define MASK(pass,depth,display,png)\

-            ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png))

-#endif /* !PNG_USE_COMPILE_TIME_MASKS */

-

-         /* Use the appropriate mask to copy the required bits.  In some cases

-          * the byte mask will be 0 or 0xff, optimize these cases.  row_width is

-          * the number of pixels, but the code copies bytes, so it is necessary

-          * to special case the end.

-          */

-         png_uint_32 pixels_per_byte = 8 / pixel_depth;

-         png_uint_32 mask;

-

-#        ifdef PNG_READ_PACKSWAP_SUPPORTED

-            if (png_ptr->transformations & PNG_PACKSWAP)

-               mask = MASK(pass, pixel_depth, display, 0);

-

-            else

-#        endif

-            mask = MASK(pass, pixel_depth, display, 1);

-

-         for (;;)

-         {

-            png_uint_32 m;

-

-            /* It doesn't matter in the following if png_uint_32 has more than

-             * 32 bits because the high bits always match those in m<<24; it is,

-             * however, essential to use OR here, not +, because of this.

-             */

-            m = mask;

-            mask = (m >> 8) | (m << 24); /* rotate right to good compilers */

-            m &= 0xff;

-

-            if (m != 0) /* something to copy */

-            {

-               if (m != 0xff)

-                  *dp = (png_byte)((*dp & ~m) | (*sp & m));

-               else

-                  *dp = *sp;

-            }

-

-            /* NOTE: this may overwrite the last byte with garbage if the image

-             * is not an exact number of bytes wide; libpng has always done

-             * this.

-             */

-            if (row_width <= pixels_per_byte)

-               break; /* May need to restore part of the last byte */

-

-            row_width -= pixels_per_byte;

-            ++dp;

-            ++sp;

-         }

-      }

-

-      else /* pixel_depth >= 8 */

-      {

-         unsigned int bytes_to_copy, bytes_to_jump;

-

-         /* Validate the depth - it must be a multiple of 8 */

-         if (pixel_depth & 7)

-            png_error(png_ptr, "invalid user transform pixel depth");

-

-         pixel_depth >>= 3; /* now in bytes */

-         row_width *= pixel_depth;

-

-         /* Regardless of pass number the Adam 7 interlace always results in a

-          * fixed number of pixels to copy then to skip.  There may be a

-          * different number of pixels to skip at the start though.

-          */

-         {

-            unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth;

-

-            row_width -= offset;

-            dp += offset;

-            sp += offset;

-         }

-

-         /* Work out the bytes to copy. */

-         if (display)

-         {

-            /* When doing the 'block' algorithm the pixel in the pass gets

-             * replicated to adjacent pixels.  This is why the even (0,2,4,6)

-             * passes are skipped above - the entire expanded row is copied.

-             */

-            bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth;

-

-            /* But don't allow this number to exceed the actual row width. */

-            if (bytes_to_copy > row_width)

-               bytes_to_copy = row_width;

-         }

-

-         else /* normal row; Adam7 only ever gives us one pixel to copy. */

-            bytes_to_copy = pixel_depth;

-

-         /* In Adam7 there is a constant offset between where the pixels go. */

-         bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth;

-

-         /* And simply copy these bytes.  Some optimization is possible here,

-          * depending on the value of 'bytes_to_copy'.  Special case the low

-          * byte counts, which we know to be frequent.

-          *

-          * Notice that these cases all 'return' rather than 'break' - this

-          * avoids an unnecessary test on whether to restore the last byte

-          * below.

-          */

-         switch (bytes_to_copy)

-         {

-            case 1:

-               for (;;)

-               {

-                  *dp = *sp;

-

-                  if (row_width <= bytes_to_jump)

-                     return;

-

-                  dp += bytes_to_jump;

-                  sp += bytes_to_jump;

-                  row_width -= bytes_to_jump;

-               }

-

-            case 2:

-               /* There is a possibility of a partial copy at the end here; this

-                * slows the code down somewhat.

-                */

-               do

-               {

-                  dp[0] = sp[0], dp[1] = sp[1];

-

-                  if (row_width <= bytes_to_jump)

-                     return;

-

-                  sp += bytes_to_jump;

-                  dp += bytes_to_jump;

-                  row_width -= bytes_to_jump;

-               }

-               while (row_width > 1);

-

-               /* And there can only be one byte left at this point: */

-               *dp = *sp;

-               return;

-

-            case 3:

-               /* This can only be the RGB case, so each copy is exactly one

-                * pixel and it is not necessary to check for a partial copy.

-                */

-               for(;;)

-               {

-                  dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2];

-

-                  if (row_width <= bytes_to_jump)

-                     return;

-

-                  sp += bytes_to_jump;

-                  dp += bytes_to_jump;

-                  row_width -= bytes_to_jump;

-               }

-

-            default:

-#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE

-               /* 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 memcpy there.

-                */

-               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)

-               {

-                  /* 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)

-                  {

-                     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

-                     {

-                        size_t c = bytes_to_copy;

-                        do

-                        {

-                           *dp32++ = *sp32++;

-                           c -= (sizeof (png_uint_32));

-                        }

-                        while (c > 0);

-

-                        if (row_width <= bytes_to_jump)

-                           return;

-

-                        dp32 += skip;

-                        sp32 += skip;

-                        row_width -= bytes_to_jump;

-                     }

-                     while (bytes_to_copy <= row_width);

-

-                     /* Get to here when the row_width truncates the final copy.

-                      * There will be 1-3 bytes left to copy, so don't try the

-                      * 16-bit loop below.

-                      */

-                     dp = (png_bytep)dp32;

-                     sp = (png_const_bytep)sp32;

-                     do

-                        *dp++ = *sp++;

-                     while (--row_width > 0);

-                     return;

-                  }

-

-                  /* Else do it in 16-bit quantities, but only if the size is

-                   * not too large.

-                   */

-                  else

-                  {

-                     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

-                     {

-                        size_t c = bytes_to_copy;

-                        do

-                        {

-                           *dp16++ = *sp16++;

-                           c -= (sizeof (png_uint_16));

-                        }

-                        while (c > 0);

-

-                        if (row_width <= bytes_to_jump)

-                           return;

-

-                        dp16 += skip;

-                        sp16 += skip;

-                        row_width -= bytes_to_jump;

-                     }

-                     while (bytes_to_copy <= row_width);

-

-                     /* End of row - 1 byte left, bytes_to_copy > row_width: */

-                     dp = (png_bytep)dp16;

-                     sp = (png_const_bytep)sp16;

-                     do

-                        *dp++ = *sp++;

-                     while (--row_width > 0);

-                     return;

-                  }

-               }

-#endif /* PNG_ALIGN_ code */

-

-               /* The true default - use a memcpy: */

-               for (;;)

-               {

-                  memcpy(dp, sp, bytes_to_copy);

-

-                  if (row_width <= bytes_to_jump)

-                     return;

-

-                  sp += bytes_to_jump;

-                  dp += bytes_to_jump;

-                  row_width -= bytes_to_jump;

-                  if (bytes_to_copy > row_width)

-                     bytes_to_copy = row_width;

-               }

-         }

-

-         /* NOT REACHED*/

-      } /* pixel_depth >= 8 */

-

-      /* Here if pixel_depth < 8 to check 'end_ptr' below. */

-   }

-   else

-#endif

-

-   /* 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.)

-    */

-   memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));

-

-   /* Restore the overwritten bits from the last byte if necessary. */

-   if (end_ptr != NULL)

-      *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask));

-}

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-void /* PRIVATE */

-png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,

-   png_uint_32 transformations /* Because these may affect the byte layout */)

-{

-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */

-   /* Offset to next interlace block */

-   static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};

-

-   png_debug(1, "in png_do_read_interlace");

-   if (row != NULL && row_info != NULL)

-   {

-      png_uint_32 final_width;

-

-      final_width = row_info->width * png_pass_inc[pass];

-

-      switch (row_info->pixel_depth)

-      {

-         case 1:

-         {

-            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);

-            png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);

-            int sshift, dshift;

-            int s_start, s_end, s_inc;

-            int jstop = png_pass_inc[pass];

-            png_byte v;

-            png_uint_32 i;

-            int j;

-

-#ifdef PNG_READ_PACKSWAP_SUPPORTED

-            if (transformations & PNG_PACKSWAP)

-            {

-                sshift = (int)((row_info->width + 7) & 0x07);

-                dshift = (int)((final_width + 7) & 0x07);

-                s_start = 7;

-                s_end = 0;

-                s_inc = -1;

-            }

-

-            else

-#endif

-            {

-                sshift = 7 - (int)((row_info->width + 7) & 0x07);

-                dshift = 7 - (int)((final_width + 7) & 0x07);

-                s_start = 0;

-                s_end = 7;

-                s_inc = 1;

-            }

-

-            for (i = 0; i < row_info->width; i++)

-            {

-               v = (png_byte)((*sp >> sshift) & 0x01);

-               for (j = 0; j < jstop; j++)

-               {

-                  unsigned int tmp = *dp & (0x7f7f >> (7 - dshift));

-                  tmp |= v << dshift;

-                  *dp = (png_byte)(tmp & 0xff);

-

-                  if (dshift == s_end)

-                  {

-                     dshift = s_start;

-                     dp--;

-                  }

-

-                  else

-                     dshift += s_inc;

-               }

-

-               if (sshift == s_end)

-               {

-                  sshift = s_start;

-                  sp--;

-               }

-

-               else

-                  sshift += s_inc;

-            }

-            break;

-         }

-

-         case 2:

-         {

-            png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);

-            png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);

-            int sshift, dshift;

-            int s_start, s_end, s_inc;

-            int jstop = png_pass_inc[pass];

-            png_uint_32 i;

-

-#ifdef PNG_READ_PACKSWAP_SUPPORTED

-            if (transformations & PNG_PACKSWAP)

-            {

-               sshift = (int)(((row_info->width + 3) & 0x03) << 1);

-               dshift = (int)(((final_width + 3) & 0x03) << 1);

-               s_start = 6;

-               s_end = 0;

-               s_inc = -2;

-            }

-

-            else

-#endif

-            {

-               sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);

-               dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);

-               s_start = 0;

-               s_end = 6;

-               s_inc = 2;

-            }

-

-            for (i = 0; i < row_info->width; i++)

-            {

-               png_byte v;

-               int j;

-

-               v = (png_byte)((*sp >> sshift) & 0x03);

-               for (j = 0; j < jstop; j++)

-               {

-                  unsigned int tmp = *dp & (0x3f3f >> (6 - dshift));

-                  tmp |= v << dshift;

-                  *dp = (png_byte)(tmp & 0xff);

-

-                  if (dshift == s_end)

-                  {

-                     dshift = s_start;

-                     dp--;

-                  }

-

-                  else

-                     dshift += s_inc;

-               }

-

-               if (sshift == s_end)

-               {

-                  sshift = s_start;

-                  sp--;

-               }

-

-               else

-                  sshift += s_inc;

-            }

-            break;

-         }

-

-         case 4:

-         {

-            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);

-            png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);

-            int sshift, dshift;

-            int s_start, s_end, s_inc;

-            png_uint_32 i;

-            int jstop = png_pass_inc[pass];

-

-#ifdef PNG_READ_PACKSWAP_SUPPORTED

-            if (transformations & PNG_PACKSWAP)

-            {

-               sshift = (int)(((row_info->width + 1) & 0x01) << 2);

-               dshift = (int)(((final_width + 1) & 0x01) << 2);

-               s_start = 4;

-               s_end = 0;

-               s_inc = -4;

-            }

-

-            else

-#endif

-            {

-               sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);

-               dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);

-               s_start = 0;

-               s_end = 4;

-               s_inc = 4;

-            }

-

-            for (i = 0; i < row_info->width; i++)

-            {

-               png_byte v = (png_byte)((*sp >> sshift) & 0x0f);

-               int j;

-

-               for (j = 0; j < jstop; j++)

-               {

-                  unsigned int tmp = *dp & (0xf0f >> (4 - dshift));

-                  tmp |= v << dshift;

-                  *dp = (png_byte)(tmp & 0xff);

-

-                  if (dshift == s_end)

-                  {

-                     dshift = s_start;

-                     dp--;

-                  }

-

-                  else

-                     dshift += s_inc;

-               }

-

-               if (sshift == s_end)

-               {

-                  sshift = s_start;

-                  sp--;

-               }

-

-               else

-                  sshift += s_inc;

-            }

-            break;

-         }

-

-         default:

-         {

-            png_size_t pixel_bytes = (row_info->pixel_depth >> 3);

-

-            png_bytep sp = row + (png_size_t)(row_info->width - 1)

-                * pixel_bytes;

-

-            png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;

-

-            int jstop = png_pass_inc[pass];

-            png_uint_32 i;

-

-            for (i = 0; i < row_info->width; i++)

-            {

-               png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */

-               int j;

-

-               memcpy(v, sp, pixel_bytes);

-

-               for (j = 0; j < jstop; j++)

-               {

-                  memcpy(dp, v, pixel_bytes);

-                  dp -= pixel_bytes;

-               }

-

-               sp -= pixel_bytes;

-            }

-            break;

-         }

-      }

-

-      row_info->width = final_width;

-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width);

-   }

-#ifndef PNG_READ_PACKSWAP_SUPPORTED

-   PNG_UNUSED(transformations)  /* Silence compiler warning */

-#endif

-}

-#endif /* PNG_READ_INTERLACING_SUPPORTED */

-

-static void

-png_read_filter_row_sub(png_row_infop row_info, png_bytep row,

-   png_const_bytep prev_row)

-{

-   png_size_t i;

-   png_size_t istop = row_info->rowbytes;

-   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;

-   png_bytep rp = row + bpp;

-

-   PNG_UNUSED(prev_row)

-

-   for (i = bpp; i < istop; i++)

-   {

-      *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);

-      rp++;

-   }

-}

-

-static void

-png_read_filter_row_up(png_row_infop row_info, png_bytep row,

-   png_const_bytep prev_row)

-{

-   png_size_t i;

-   png_size_t istop = row_info->rowbytes;

-   png_bytep rp = row;

-   png_const_bytep pp = prev_row;

-

-   for (i = 0; i < istop; i++)

-   {

-      *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);

-      rp++;

-   }

-}

-

-static void

-png_read_filter_row_avg(png_row_infop row_info, png_bytep row,

-   png_const_bytep prev_row)

-{

-   png_size_t i;

-   png_bytep rp = row;

-   png_const_bytep pp = prev_row;

-   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;

-   png_size_t istop = row_info->rowbytes - bpp;

-

-   for (i = 0; i < bpp; i++)

-   {

-      *rp = (png_byte)(((int)(*rp) +

-         ((int)(*pp++) / 2 )) & 0xff);

-

-      rp++;

-   }

-

-   for (i = 0; i < istop; i++)

-   {

-      *rp = (png_byte)(((int)(*rp) +

-         (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);

-

-      rp++;

-   }

-}

-

-static void

-png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row,

-   png_const_bytep prev_row)

-{

-   png_bytep rp_end = row + row_info->rowbytes;

-   int a, c;

-

-   /* First pixel/byte */

-   c = *prev_row++;

-   a = *row + c;

-   *row++ = (png_byte)a;

-

-   /* Remainder */

-   while (row < rp_end)

-   {

-      int b, pa, pb, pc, p;

-

-      a &= 0xff; /* From previous iteration or start */

-      b = *prev_row++;

-

-      p = b - c;

-      pc = a - c;

-

-#     ifdef PNG_USE_ABS

-         pa = abs(p);

-         pb = abs(pc);

-         pc = abs(p + pc);

-#     else

-         pa = p < 0 ? -p : p;

-         pb = pc < 0 ? -pc : pc;

-         pc = (p + pc) < 0 ? -(p + pc) : p + pc;

-#     endif

-

-      /* Find the best predictor, the least of pa, pb, pc favoring the earlier

-       * ones in the case of a tie.

-       */

-      if (pb < pa) pa = pb, a = b;

-      if (pc < pa) a = c;

-

-      /* Calculate the current pixel in a, and move the previous row pixel to c

-       * for the next time round the loop

-       */

-      c = b;

-      a += *row;

-      *row++ = (png_byte)a;

-   }

-}

-

-static void

-png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,

-   png_const_bytep prev_row)

-{

-   int bpp = (row_info->pixel_depth + 7) >> 3;

-   png_bytep rp_end = row + bpp;

-

-   /* Process the first pixel in the row completely (this is the same as 'up'

-    * because there is only one candidate predictor for the first row).

-    */

-   while (row < rp_end)

-   {

-      int a = *row + *prev_row++;

-      *row++ = (png_byte)a;

-   }

-

-   /* Remainder */

-   rp_end += row_info->rowbytes - bpp;

-

-   while (row < rp_end)

-   {

-      int a, b, c, pa, pb, pc, p;

-

-      c = *(prev_row - bpp);

-      a = *(row - bpp);

-      b = *prev_row++;

-

-      p = b - c;

-      pc = a - c;

-

-#     ifdef PNG_USE_ABS

-         pa = abs(p);

-         pb = abs(pc);

-         pc = abs(p + pc);

-#     else

-         pa = p < 0 ? -p : p;

-         pb = pc < 0 ? -pc : pc;

-         pc = (p + pc) < 0 ? -(p + pc) : p + pc;

-#     endif

-

-      if (pb < pa) pa = pb, a = b;

-      if (pc < pa) a = c;

-

-      c = b;

-      a += *row;

-      *row++ = (png_byte)a;

-   }

-}

-

-static void

-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;

-

-   pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub;

-   pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up;

-   pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg;

-   if (bpp == 1)

-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =

-         png_read_filter_row_paeth_1byte_pixel;

-   else

-      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =

-         png_read_filter_row_paeth_multibyte_pixel;

-

-#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_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)

-      pp->read_filter[filter-1](row_info, row, prev_row);

-}

-

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-void /* PRIVATE */

-png_read_IDAT_data(png_structrp png_ptr, png_bytep output,

-   png_alloc_size_t avail_out)

-{

-   /* Loop reading IDATs and decompressing the result into output[avail_out] */

-   png_ptr->zstream.next_out = output;

-   png_ptr->zstream.avail_out = 0; /* safety: set below */

-

-   if (output == NULL)

-      avail_out = 0;

-

-   do

-   {

-      int ret;

-      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];

-

-      if (png_ptr->zstream.avail_in == 0)

-      {

-         uInt avail_in;

-         png_bytep buffer;

-

-         while (png_ptr->idat_size == 0)

-         {

-            png_crc_finish(png_ptr, 0);

-

-            png_ptr->idat_size = png_read_chunk_header(png_ptr);

-            /* This is an error even in the 'check' case because the code just

-             * consumed a non-IDAT header.

-             */

-            if (png_ptr->chunk_name != png_IDAT)

-               png_error(png_ptr, "Not enough image data");

-         }

-

-         avail_in = png_ptr->IDAT_read_size;

-

-         if (avail_in > png_ptr->idat_size)

-            avail_in = (uInt)png_ptr->idat_size;

-

-         /* A PNG with a gradually increasing IDAT size will defeat this attempt

-          * to minimize memory usage by causing lots of re-allocs, but

-          * realistically doing IDAT_read_size re-allocs is not likely to be a

-          * big problem.

-          */

-         buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);

-

-         png_crc_read(png_ptr, buffer, avail_in);

-         png_ptr->idat_size -= avail_in;

-

-         png_ptr->zstream.next_in = buffer;

-         png_ptr->zstream.avail_in = avail_in;

-      }

-

-      /* And set up the output side. */

-      if (output != NULL) /* standard read */

-      {

-         uInt out = ZLIB_IO_MAX;

-

-         if (out > avail_out)

-            out = (uInt)avail_out;

-

-         avail_out -= out;

-         png_ptr->zstream.avail_out = out;

-      }

-

-      else /* 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 */

-

-   /* Start of interlace block */

-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};

-

-   /* Offset to next interlace block */

-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};

-

-   /* Start of interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};

-

-   /* Offset to next interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};

-#endif /* PNG_READ_INTERLACING_SUPPORTED */

-

-   png_debug(1, "in png_read_finish_row");

-   png_ptr->row_number++;

-   if (png_ptr->row_number < png_ptr->num_rows)

-      return;

-

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   if (png_ptr->interlaced)

-   {

-      png_ptr->row_number = 0;

-

-      /* TO DO: don't do this if prev_row isn't needed (requires

-       * read-ahead of the next row's filter byte.

-       */

-      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);

-

-      do

-      {

-         png_ptr->pass++;

-

-         if (png_ptr->pass >= 7)

-            break;

-

-         png_ptr->iwidth = (png_ptr->width +

-            png_pass_inc[png_ptr->pass] - 1 -

-            png_pass_start[png_ptr->pass]) /

-            png_pass_inc[png_ptr->pass];

-

-         if (!(png_ptr->transformations & PNG_INTERLACE))

-         {

-            png_ptr->num_rows = (png_ptr->height +

-                png_pass_yinc[png_ptr->pass] - 1 -

-                png_pass_ystart[png_ptr->pass]) /

-                png_pass_yinc[png_ptr->pass];

-         }

-

-         else  /* if (png_ptr->transformations & PNG_INTERLACE) */

-            break; /* libpng deinterlacing sees every row */

-

-      } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);

-

-      if (png_ptr->pass < 7)

-         return;

-   }

-#endif /* PNG_READ_INTERLACING_SUPPORTED */

-

-   /* 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_structrp png_ptr)

-{

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */

-

-   /* Start of interlace block */

-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};

-

-   /* Offset to next interlace block */

-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};

-

-   /* Start of interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};

-

-   /* Offset to next interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};

-#endif

-

-   int max_pixel_depth;

-   png_size_t row_bytes;

-

-   png_debug(1, "in png_read_start_row");

-

-#ifdef PNG_READ_TRANSFORMS_SUPPORTED

-   png_init_read_transformations(png_ptr);

-#endif

-#ifdef PNG_READ_INTERLACING_SUPPORTED

-   if (png_ptr->interlaced)

-   {

-      if (!(png_ptr->transformations & PNG_INTERLACE))

-         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -

-             png_pass_ystart[0]) / png_pass_yinc[0];

-

-      else

-         png_ptr->num_rows = png_ptr->height;

-

-      png_ptr->iwidth = (png_ptr->width +

-          png_pass_inc[png_ptr->pass] - 1 -

-          png_pass_start[png_ptr->pass]) /

-          png_pass_inc[png_ptr->pass];

-   }

-

-   else

-#endif /* PNG_READ_INTERLACING_SUPPORTED */

-   {

-      png_ptr->num_rows = png_ptr->height;

-      png_ptr->iwidth = png_ptr->width;

-   }

-

-   max_pixel_depth = png_ptr->pixel_depth;

-

-   /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpliar set of

-    * calculations to calculate the final pixel depth, then

-    * png_do_read_transforms actually does the transforms.  This means that the

-    * code which effectively calculates this value is actually repeated in three

-    * separate places.  They must all match.  Innocent changes to the order of

-    * transformations can and will break libpng in a way that causes memory

-    * overwrites.

-    *

-    * TODO: fix this.

-    */

-#ifdef PNG_READ_PACK_SUPPORTED

-   if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)

-      max_pixel_depth = 8;

-#endif

-

-#ifdef PNG_READ_EXPAND_SUPPORTED

-   if (png_ptr->transformations & PNG_EXPAND)

-   {

-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         if (png_ptr->num_trans)

-            max_pixel_depth = 32;

-

-         else

-            max_pixel_depth = 24;

-      }

-

-      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)

-      {

-         if (max_pixel_depth < 8)

-            max_pixel_depth = 8;

-

-         if (png_ptr->num_trans)

-            max_pixel_depth *= 2;

-      }

-

-      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)

-      {

-         if (png_ptr->num_trans)

-         {

-            max_pixel_depth *= 4;

-            max_pixel_depth /= 3;

-         }

-      }

-   }

-#endif

-

-#ifdef PNG_READ_EXPAND_16_SUPPORTED

-   if (png_ptr->transformations & PNG_EXPAND_16)

-   {

-#     ifdef PNG_READ_EXPAND_SUPPORTED

-         /* In fact it is an error if it isn't supported, but checking is

-          * the safe way.

-          */

-         if (png_ptr->transformations & PNG_EXPAND)

-         {

-            if (png_ptr->bit_depth < 16)

-               max_pixel_depth *= 2;

-         }

-         else

-#     endif

-         png_ptr->transformations &= ~PNG_EXPAND_16;

-   }

-#endif

-

-#ifdef PNG_READ_FILLER_SUPPORTED

-   if (png_ptr->transformations & (PNG_FILLER))

-   {

-      if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)

-      {

-         if (max_pixel_depth <= 8)

-            max_pixel_depth = 16;

-

-         else

-            max_pixel_depth = 32;

-      }

-

-      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB ||

-         png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         if (max_pixel_depth <= 32)

-            max_pixel_depth = 32;

-

-         else

-            max_pixel_depth = 64;

-      }

-   }

-#endif

-

-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED

-   if (png_ptr->transformations & PNG_GRAY_TO_RGB)

-   {

-      if (

-#ifdef PNG_READ_EXPAND_SUPPORTED

-          (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||

-#endif

-#ifdef PNG_READ_FILLER_SUPPORTED

-          (png_ptr->transformations & (PNG_FILLER)) ||

-#endif

-          png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-      {

-         if (max_pixel_depth <= 16)

-            max_pixel_depth = 32;

-

-         else

-            max_pixel_depth = 64;

-      }

-

-      else

-      {

-         if (max_pixel_depth <= 8)

-         {

-            if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-               max_pixel_depth = 32;

-

-            else

-               max_pixel_depth = 24;

-         }

-

-         else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-            max_pixel_depth = 64;

-

-         else

-            max_pixel_depth = 48;

-      }

-   }

-#endif

-

-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \

-defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)

-   if (png_ptr->transformations & PNG_USER_TRANSFORM)

-   {

-      int user_pixel_depth = png_ptr->user_transform_depth *

-         png_ptr->user_transform_channels;

-

-      if (user_pixel_depth > max_pixel_depth)

-         max_pixel_depth = user_pixel_depth;

-   }

-#endif

-

-   /* This value is stored in png_struct and double checked in the row read

-    * code.

-    */

-   png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth;

-   png_ptr->transformed_pixel_depth = 0; /* calculated on demand */

-

-   /* Align the width on the next larger 8 pixels.  Mainly used

-    * for interlacing

-    */

-   row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));

-   /* Calculate the maximum bytes needed, adding a byte and a pixel

-    * for safety's sake

-    */

-   row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +

-       1 + ((max_pixel_depth + 7) >> 3);

-

-#ifdef PNG_MAX_MALLOC_64K

-   if (row_bytes > (png_uint_32)65536L)

-      png_error(png_ptr, "This image requires a row greater than 64KB");

-#endif

-

-   if (row_bytes + 48 > png_ptr->old_big_row_buf_size)

-   {

-     png_free(png_ptr, png_ptr->big_row_buf);

-     png_free(png_ptr, png_ptr->big_prev_row);

-

-     if (png_ptr->interlaced)

-        png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,

-            row_bytes + 48);

-

-     else

-        png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);

-

-     png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48);

-

-#ifdef PNG_ALIGNED_MEMORY_SUPPORTED

-     /* Use 16-byte aligned memory for row_buf with at least 16 bytes

-      * of padding before and after row_buf; treat prev_row similarly.

-      * NOTE: the alignment is to the start of the pixels, one beyond the start

-      * of the buffer, because of the filter byte.  Prior to libpng 1.5.6 this

-      * was incorrect; the filter byte was aligned, which had the exact

-      * opposite effect of that intended.

-      */

-     {

-        png_bytep temp = png_ptr->big_row_buf + 32;

-        int extra = (int)((temp - (png_bytep)0) & 0x0f);

-        png_ptr->row_buf = temp - extra - 1/*filter byte*/;

-

-        temp = png_ptr->big_prev_row + 32;

-        extra = (int)((temp - (png_bytep)0) & 0x0f);

-        png_ptr->prev_row = temp - extra - 1/*filter byte*/;

-     }

-

-#else

-     /* Use 31 bytes of padding before and 17 bytes after row_buf. */

-     png_ptr->row_buf = png_ptr->big_row_buf + 31;

-     png_ptr->prev_row = png_ptr->big_prev_row + 31;

-#endif

-     png_ptr->old_big_row_buf_size = row_bytes + 48;

-   }

-

-#ifdef PNG_MAX_MALLOC_64K

-   if (png_ptr->rowbytes > 65535)

-      png_error(png_ptr, "This image requires a row greater than 64KB");

-

-#endif

-   if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1))

-      png_error(png_ptr, "Row has too many bytes to allocate in memory");

-

-   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);

-   png_debug1(3, "iwidth = %u,", png_ptr->iwidth);

-   png_debug1(3, "num_rows = %u,", png_ptr->num_rows);

-   png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes);

-   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) != Z_OK)

-      png_error(png_ptr, png_ptr->zstream.msg);

-

-   png_ptr->flags |= PNG_FLAG_ROW_INIT;

-}

-#endif /* PNG_READ_SUPPORTED */

+
+/* pngrutil.c - utilities to read a PNG file
+ *
+ * Last changed in libpng 1.6.20 [December 3, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file contains routines that are only called from within
+ * libpng itself during the course of reading an image.
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+png_uint_32 PNGAPI
+png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)
+{
+   png_uint_32 uval = png_get_uint_32(buf);
+
+   if (uval > PNG_UINT_31_MAX)
+      png_error(png_ptr, "PNG unsigned integer out of range");
+
+   return (uval);
+}
+
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)
+/* The following is a variation on the above for use with the fixed
+ * point values used for gAMA and cHRM.  Instead of png_error it
+ * issues a warning and returns (-1) - an invalid value because both
+ * gAMA and cHRM use *unsigned* integers for fixed point values.
+ */
+#define PNG_FIXED_ERROR (-1)
+
+static png_fixed_point /* PRIVATE */
+png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf)
+{
+   png_uint_32 uval = png_get_uint_32(buf);
+
+   if (uval <= PNG_UINT_31_MAX)
+      return (png_fixed_point)uval; /* known to be in range */
+
+   /* The caller can turn off the warning by passing NULL. */
+   if (png_ptr != NULL)
+      png_warning(png_ptr, "PNG fixed point integer out of range");
+
+   return PNG_FIXED_ERROR;
+}
+#endif
+
+#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
+/* NOTE: the read macros will obscure these definitions, so that if
+ * PNG_USE_READ_MACROS is set the library will not use them internally,
+ * but the APIs will still be available externally.
+ *
+ * The parentheses around "PNGAPI function_name" in the following three
+ * functions are necessary because they allow the macros to co-exist with
+ * these (unused but exported) functions.
+ */
+
+/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
+png_uint_32 (PNGAPI
+png_get_uint_32)(png_const_bytep buf)
+{
+   png_uint_32 uval =
+       ((png_uint_32)(*(buf    )) << 24) +
+       ((png_uint_32)(*(buf + 1)) << 16) +
+       ((png_uint_32)(*(buf + 2)) <<  8) +
+       ((png_uint_32)(*(buf + 3))      ) ;
+
+   return uval;
+}
+
+/* Grab a signed 32-bit integer from a buffer in big-endian format.  The
+ * data is stored in the PNG file in two's complement format and there
+ * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore
+ * the following code does a two's complement to native conversion.
+ */
+png_int_32 (PNGAPI
+png_get_int_32)(png_const_bytep buf)
+{
+   png_uint_32 uval = png_get_uint_32(buf);
+   if ((uval & 0x80000000) == 0) /* non-negative */
+      return uval;
+
+   uval = (uval ^ 0xffffffff) + 1;  /* 2's complement: -x = ~x+1 */
+   if ((uval & 0x80000000) == 0) /* no overflow */
+       return -(png_int_32)uval;
+   /* The following has to be safe; this function only gets called on PNG data
+    * and if we get here that data is invalid.  0 is the most safe value and
+    * if not then an attacker would surely just generate a PNG with 0 instead.
+    */
+   return 0;
+}
+
+/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
+png_uint_16 (PNGAPI
+png_get_uint_16)(png_const_bytep buf)
+{
+   /* ANSI-C requires an int value to accomodate at least 16 bits so this
+    * works and allows the compiler not to worry about possible narrowing
+    * on 32-bit systems.  (Pre-ANSI systems did not make integers smaller
+    * than 16 bits either.)
+    */
+   unsigned int val =
+       ((unsigned int)(*buf) << 8) +
+       ((unsigned int)(*(buf + 1)));
+
+   return (png_uint_16)val;
+}
+
+#endif /* READ_INT_FUNCTIONS */
+
+/* Read and check the PNG file signature */
+void /* PRIVATE */
+png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
+{
+   png_size_t num_checked, num_to_check;
+
+   /* Exit if the user application does not expect a signature. */
+   if (png_ptr->sig_bytes >= 8)
+      return;
+
+   num_checked = png_ptr->sig_bytes;
+   num_to_check = 8 - num_checked;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE;
+#endif
+
+   /* The signature must be serialized in a single I/O call. */
+   png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
+   png_ptr->sig_bytes = 8;
+
+   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check) != 0)
+   {
+      if (num_checked < 4 &&
+          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+         png_error(png_ptr, "Not a PNG file");
+      else
+         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+   }
+   if (num_checked < 3)
+      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+}
+
+/* Read the chunk header (length + type name).
+ * Put the type name into png_ptr->chunk_name, and return the length.
+ */
+png_uint_32 /* PRIVATE */
+png_read_chunk_header(png_structrp png_ptr)
+{
+   png_byte buf[8];
+   png_uint_32 length;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR;
+#endif
+
+   /* Read the length and the chunk name.
+    * This must be performed in a single I/O call.
+    */
+   png_read_data(png_ptr, buf, 8);
+   length = png_get_uint_31(png_ptr, buf);
+
+   /* Put the chunk name into png_ptr->chunk_name. */
+   png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4);
+
+   png_debug2(0, "Reading %lx chunk, length = %lu",
+       (unsigned long)png_ptr->chunk_name, (unsigned long)length);
+
+   /* Reset the crc and run it over the chunk name. */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, buf + 4, 4);
+
+   /* Check to see if chunk name is valid. */
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
+#endif
+
+   return length;
+}
+
+/* Read data, and (optionally) run it through the CRC. */
+void /* PRIVATE */
+png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_read_data(png_ptr, buf, length);
+   png_calculate_crc(png_ptr, buf, length);
+}
+
+/* Optionally skip data and then check the CRC.  Depending on whether we
+ * 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_structrp png_ptr, png_uint_32 skip)
+{
+   /* 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_uint_32 len;
+      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
+
+      len = (sizeof tmpbuf);
+      if (len > skip)
+         len = skip;
+      skip -= len;
+
+      png_crc_read(png_ptr, tmpbuf, len);
+   }
+
+   if (png_crc_error(png_ptr) != 0)
+   {
+      if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0 ?
+          (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0 :
+          (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE) != 0)
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+
+      else
+         png_chunk_error(png_ptr, "CRC error");
+
+      return (1);
+   }
+
+   return (0);
+}
+
+/* Compare the CRC stored in the PNG file with that calculated by libpng from
+ * the data it has read thus far.
+ */
+int /* PRIVATE */
+png_crc_error(png_structrp png_ptr)
+{
+   png_byte crc_bytes[4];
+   png_uint_32 crc;
+   int need_crc = 1;
+
+   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) != 0)
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+
+   else /* critical */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) != 0)
+         need_crc = 0;
+   }
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
+#endif
+
+   /* The chunk CRC must be serialized in a single I/O call. */
+   png_read_data(png_ptr, crc_bytes, 4);
+
+   if (need_crc != 0)
+   {
+      crc = png_get_uint_32(crc_bytes);
+      return ((int)(crc != png_ptr->crc));
+   }
+
+   else
+      return (0);
+}
+
+#if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\
+    defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_sCAL_SUPPORTED) ||\
+    defined(PNG_READ_sPLT_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) ||\
+    defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_SEQUENTIAL_READ_SUPPORTED)
+/* 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_bytep buffer = png_ptr->read_buffer;
+
+   if (buffer != NULL && new_size > png_ptr->read_buffer_size)
+   {
+      png_ptr->read_buffer = NULL;
+      png_ptr->read_buffer = NULL;
+      png_ptr->read_buffer_size = 0;
+      png_free(png_ptr, buffer);
+      buffer = NULL;
+   }
+
+   if (buffer == NULL)
+   {
+      buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size));
+
+      if (buffer != NULL)
+      {
+         png_ptr->read_buffer = buffer;
+         png_ptr->read_buffer_size = new_size;
+      }
+
+      else if (warn < 2) /* else silent */
+      {
+         if (warn != 0)
+             png_chunk_warning(png_ptr, "insufficient memory to read chunk");
+
+         else
+             png_chunk_error(png_ptr, "insufficient memory to read chunk");
+      }
+   }
+
+   return buffer;
+}
+#endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|SEQUENTIAL_READ */
+
+/* png_inflate_claim: claim the zstream for some nefarious purpose that involves
+ * decompression.  Returns Z_OK on success, else a zlib error code.  It checks
+ * the owner but, in final release builds, just issues a warning if some other
+ * chunk apparently owns the stream.  Prior to release it does a png_error.
+ */
+static int
+png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
+{
+   if (png_ptr->zowner != 0)
+   {
+      char msg[64];
+
+      PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner);
+      /* So the message that results is "<chunk> using zstream"; this is an
+       * internal error, but is very useful for debugging.  i18n requirements
+       * are minimal.
+       */
+      (void)png_safecat(msg, (sizeof msg), 4, " using zstream");
+#if PNG_RELEASE_BUILD
+      png_chunk_warning(png_ptr, msg);
+      png_ptr->zowner = 0;
+#else
+      png_chunk_error(png_ptr, msg);
+#endif
+   }
+
+   /* Implementation note: unlike 'png_deflate_claim' this internal function
+    * does not take the size of the data as an argument.  Some efficiency could
+    * be gained by using this when it is known *if* the zlib stream itself does
+    * 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 */
+#if PNG_ZLIB_VERNUM >= 0x1240
+
+# if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW)
+      int window_bits;
+
+      if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==
+          PNG_OPTION_ON)
+      {
+         window_bits = 15;
+         png_ptr->zstream_start = 0; /* fixed window size */
+      }
+
+      else
+      {
+         window_bits = 0;
+         png_ptr->zstream_start = 1;
+      }
+# else
+#   define window_bits 0
+# endif
+#endif
+
+      /* Set this for safety, just in case the previous owner left pointers to
+       * memory allocations.
+       */
+      png_ptr->zstream.next_in = NULL;
+      png_ptr->zstream.avail_in = 0;
+      png_ptr->zstream.next_out = NULL;
+      png_ptr->zstream.avail_out = 0;
+
+      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
+      {
+#if PNG_ZLIB_VERNUM < 0x1240
+         ret = inflateReset(&png_ptr->zstream);
+#else
+         ret = inflateReset2(&png_ptr->zstream, window_bits);
+#endif
+      }
+
+      else
+      {
+#if PNG_ZLIB_VERNUM < 0x1240
+         ret = inflateInit(&png_ptr->zstream);
+#else
+         ret = inflateInit2(&png_ptr->zstream, window_bits);
+#endif
+
+         if (ret == Z_OK)
+            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
+      }
+
+      if (ret == Z_OK)
+         png_ptr->zowner = owner;
+
+      else
+         png_zstream_error(png_ptr, ret);
+
+      return ret;
+   }
+
+#ifdef window_bits
+# undef window_bits
+#endif
+}
+
+#if PNG_ZLIB_VERNUM >= 0x1240
+/* Handle the start of the inflate stream if we called inflateInit2(strm,0);
+ * in this case some zlib versions skip validation of the CINFO field and, in
+ * certain circumstances, libpng may end up displaying an invalid image, in
+ * contrast to implementations that call zlib in the normal way (e.g. libpng
+ * 1.5).
+ */
+int /* PRIVATE */
+png_zlib_inflate(png_structrp png_ptr, int flush)
+{
+   if (png_ptr->zstream_start && png_ptr->zstream.avail_in > 0)
+   {
+      if ((*png_ptr->zstream.next_in >> 4) > 7)
+      {
+         png_ptr->zstream.msg = "invalid window size (libpng)";
+         return Z_DATA_ERROR;
+      }
+
+      png_ptr->zstream_start = 0;
+   }
+
+   return inflate(&png_ptr->zstream, flush);
+}
+#endif /* Zlib >= 1.2.4 */
+
+#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;
+      png_ptr->zstream.avail_out = 0;
+
+      /* Read directly into the output if it is available (this is set to
+       * a local buffer below if output is NULL).
+       */
+      if (output != NULL)
+         png_ptr->zstream.next_out = output;
+
+      do
+      {
+         uInt avail;
+         Byte local_buffer[PNG_INFLATE_BUF_SIZE];
+
+         /* zlib INPUT BUFFER */
+         /* The setting of 'avail_in' used to be outside the loop; by setting it
+          * inside it is possible to chunk the input to zlib and simply rely on
+          * zlib to advance the 'next_in' pointer.  This allows arbitrary
+          * amounts of data to be passed through zlib at the unavoidable cost of
+          * requiring a window save (memcpy of up to 32768 output bytes)
+          * every ZLIB_IO_MAX input bytes.
+          */
+         avail_in += png_ptr->zstream.avail_in; /* not consumed last time */
+
+         avail = ZLIB_IO_MAX;
+
+         if (avail_in < avail)
+            avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */
+
+         avail_in -= avail;
+         png_ptr->zstream.avail_in = avail;
+
+         /* zlib OUTPUT BUFFER */
+         avail_out += png_ptr->zstream.avail_out; /* not written last time */
+
+         avail = ZLIB_IO_MAX; /* maximum zlib can process */
+
+         if (output == NULL)
+         {
+            /* Reset the output buffer each time round if output is NULL and
+             * make available the full buffer, up to 'remaining_space'
+             */
+            png_ptr->zstream.next_out = local_buffer;
+            if ((sizeof local_buffer) < avail)
+               avail = (sizeof local_buffer);
+         }
+
+         if (avail_out < avail)
+            avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */
+
+         png_ptr->zstream.avail_out = avail;
+         avail_out -= avail;
+
+         /* zlib inflate call */
+         /* In fact 'avail_out' may be 0 at this point, that happens at the end
+          * of the read when the final LZ end code was not passed at the end of
+          * the previous chunk of input data.  Tell zlib if we have reached the
+          * end of the output buffer.
+          */
+         ret = PNG_INFLATE(png_ptr, avail_out > 0 ? Z_NO_FLUSH :
+             (finish ? Z_FINISH : Z_SYNC_FLUSH));
+      } while (ret == Z_OK);
+
+      /* For safety kill the local buffer pointer now */
+      if (output == NULL)
+         png_ptr->zstream.next_out = NULL;
+
+      /* Claw back the 'size' and 'remaining_space' byte counts. */
+      avail_in += png_ptr->zstream.avail_in;
+      avail_out += png_ptr->zstream.avail_out;
+
+      /* Update the input and output sizes; the updated values are the amount
+       * consumed or written, effectively the inverse of what zlib uses.
+       */
+      if (avail_out > 0)
+         *output_size_ptr -= avail_out;
+
+      if (avail_in > 0)
+         *input_size_ptr -= avail_in;
+
+      /* Ensure png_ptr->zstream.msg is set (even in the success case!) */
+      png_zstream_error(png_ptr, ret);
+      return ret;
+   }
+
+   else
+   {
+      /* 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 read_buffer
+ * points at an allocated area holding the contents of a chunk with a
+ * trailing compressed part.  What we get back is an allocated area
+ * holding the original prefix part and an uncompressed version of the
+ * trailing part (the malloc area passed in is freed).
+ */
+static 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*/)
+{
+   /* 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_USER_LIMITS_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
+
+   if (limit >= prefix_size + (terminate != 0))
+   {
+      int ret;
+
+      limit -= prefix_size + (terminate != 0);
+
+      if (limit < *newlength)
+         *newlength = limit;
+
+      /* Now try to claim the stream. */
+      ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);
+
+      if (ret == Z_OK)
+      {
+         png_uint_32 lzsize = chunklength - prefix_size;
+
+         ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/,
+            /* input: */ png_ptr->read_buffer + prefix_size, &lzsize,
+            /* output: */ NULL, newlength);
+
+         if (ret == Z_STREAM_END)
+         {
+            /* 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)
+            {
+               /* 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 != 0)
+                           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);
+               }
+            }
+
+            else
+            {
+               /* inflateReset failed, store the error message */
+               png_zstream_error(png_ptr, ret);
+
+               if (ret == Z_STREAM_END)
+                  ret = PNG_UNEXPECTED_ZLIB_RETURN;
+            }
+         }
+
+         else if (ret == Z_OK)
+            ret = PNG_UNEXPECTED_ZLIB_RETURN;
+
+         /* Release the claimed stream */
+         png_ptr->zowner = 0;
+      }
+
+      else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */
+         ret = PNG_UNEXPECTED_ZLIB_RETURN;
+
+      return ret;
+   }
+
+   else
+   {
+      /* Application/configuration limits exceeded */
+      png_zstream_error(png_ptr, Z_MEM_ERROR);
+      return Z_MEM_ERROR;
+   }
+}
+#endif /* READ_COMPRESSED_TEXT */
+
+#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)
+   {
+      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 */
+
+      do
+      {
+         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_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;
+         }
+
+         /* 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 = PNG_INFLATE(png_ptr,
+            *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH));
+      }
+      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;
+   }
+
+   else
+   {
+      png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed");
+      return Z_STREAM_ERROR;
+   }
+}
+#endif
+
+/* Read and check the IDHR chunk */
+
+void /* PRIVATE */
+png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_byte buf[13];
+   png_uint_32 width, height;
+   int bit_depth, color_type, compression_type, filter_type;
+   int interlace_type;
+
+   png_debug(1, "in png_handle_IHDR");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) != 0)
+      png_chunk_error(png_ptr, "out of place");
+
+   /* Check the length */
+   if (length != 13)
+      png_chunk_error(png_ptr, "invalid");
+
+   png_ptr->mode |= PNG_HAVE_IHDR;
+
+   png_crc_read(png_ptr, buf, 13);
+   png_crc_finish(png_ptr, 0);
+
+   width = png_get_uint_31(png_ptr, buf);
+   height = png_get_uint_31(png_ptr, buf + 4);
+   bit_depth = buf[8];
+   color_type = buf[9];
+   compression_type = buf[10];
+   filter_type = buf[11];
+   interlace_type = buf[12];
+
+   /* Set internal variables */
+   png_ptr->width = width;
+   png_ptr->height = height;
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->interlaced = (png_byte)interlace_type;
+   png_ptr->color_type = (png_byte)color_type;
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   png_ptr->filter_type = (png_byte)filter_type;
+#endif
+   png_ptr->compression_type = (png_byte)compression_type;
+
+   /* Find number of channels */
+   switch (png_ptr->color_type)
+   {
+      default: /* invalid, png_set_IHDR calls png_error */
+      case PNG_COLOR_TYPE_GRAY:
+      case PNG_COLOR_TYPE_PALETTE:
+         png_ptr->channels = 1;
+         break;
+
+      case PNG_COLOR_TYPE_RGB:
+         png_ptr->channels = 3;
+         break;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         png_ptr->channels = 2;
+         break;
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         png_ptr->channels = 4;
+         break;
+   }
+
+   /* Set up other useful info */
+   png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels);
+   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width);
+   png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);
+   png_debug1(3, "channels = %d", png_ptr->channels);
+   png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);
+   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+       color_type, interlace_type, compression_type, filter_type);
+}
+
+/* Read and check the palette */
+void /* PRIVATE */
+png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_color palette[PNG_MAX_PALETTE_LENGTH];
+   int max_palette_length, num, i;
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+   png_colorp pal_ptr;
+#endif
+
+   png_debug(1, "in png_handle_PLTE");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      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) != 0)
+      png_chunk_error(png_ptr, "duplicate");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      /* 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;
+   }
+
+   png_ptr->mode |= PNG_HAVE_PLTE;
+
+   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "ignored in grayscale PNG");
+      return;
+   }
+
+#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+#endif
+
+   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_chunk_benign_error(png_ptr, "invalid");
+
+      else
+         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;
+
+   /* If the palette has 256 or fewer entries but is too large for the bit
+    * depth, we don't issue an error, to preserve the behavior of previous
+    * libpng versions. We silently truncate the unused extra palette entries
+    * here.
+    */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      max_palette_length = (1 << png_ptr->bit_depth);
+   else
+      max_palette_length = PNG_MAX_PALETTE_LENGTH;
+
+   if (num > max_palette_length)
+      num = max_palette_length;
+
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+   for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      pal_ptr->red = buf[0];
+      pal_ptr->green = buf[1];
+      pal_ptr->blue = buf[2];
+   }
+#else
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      /* Don't depend upon png_color being any order */
+      palette[i].red = buf[0];
+      palette[i].green = buf[1];
+      palette[i].blue = buf[2];
+   }
+#endif
+
+   /* If we actually need the PLTE chunk (ie for a paletted image), we do
+    * whatever the normal CRC configuration tells us.  However, if we
+    * have an RGB image, the PLTE can be considered ancillary, so
+    * we will act as though it is.
+    */
+#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#endif
+   {
+      png_crc_finish(png_ptr, (int) length - num * 3);
+   }
+
+#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+   else if (png_crc_error(png_ptr) != 0)  /* Only if we have a CRC error */
+   {
+      /* If we don't want to use the data from an ancillary chunk,
+       * 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) == 0)
+      {
+         if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) != 0)
+            return;
+
+         else
+            png_chunk_error(png_ptr, "CRC error");
+      }
+
+      /* Otherwise, we (optionally) emit a warning and use the chunk. */
+      else if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) == 0)
+         png_chunk_warning(png_ptr, "CRC error");
+   }
+#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->num_trans > 0 ||
+       (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0))
+   {
+      /* 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 != 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_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) == 0 ||
+       (png_ptr->mode & PNG_HAVE_IDAT) == 0)
+      png_chunk_error(png_ptr, "out of place");
+
+   png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
+
+   png_crc_finish(png_ptr, length);
+
+   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_fixed_point igamma;
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_gAMA");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
+      return;
+   }
+
+   if (length != 4)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   igamma = png_get_fixed_point(NULL, buf);
+
+   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   unsigned int truelen, i;
+   png_byte sample_depth;
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_sBIT");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
+      return;
+   }
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      truelen = 3;
+      sample_depth = 8;
+   }
+
+   else
+   {
+      truelen = png_ptr->channels;
+      sample_depth = png_ptr->bit_depth;
+   }
+
+   if (length != truelen || length > 4)
+   {
+      png_chunk_benign_error(png_ptr, "invalid");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   buf[0] = buf[1] = buf[2] = buf[3] = sample_depth;
+   png_crc_read(png_ptr, buf, truelen);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   for (i=0; i<truelen; ++i)
+   {
+      if (buf[i] == 0 || buf[i] > sample_depth)
+      {
+         png_chunk_benign_error(png_ptr, "invalid");
+         return;
+      }
+   }
+
+   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[1];
+      png_ptr->sig_bit.blue = buf[2];
+      png_ptr->sig_bit.alpha = buf[3];
+   }
+
+   else
+   {
+      png_ptr->sig_bit.gray = buf[0];
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[0];
+      png_ptr->sig_bit.blue = buf[0];
+      png_ptr->sig_bit.alpha = buf[1];
+   }
+
+   png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
+}
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+void /* PRIVATE */
+png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_byte buf[32];
+   png_xy xy;
+
+   png_debug(1, "in png_handle_cHRM");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
+      return;
+   }
+
+   if (length != 32)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 32);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   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;
+   }
+
+   /* If a colorspace error has already been output skip this chunk */
+   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
+      return;
+
+   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0)
+   {
+      png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
+      png_colorspace_sync(png_ptr, info_ptr);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_byte intent;
+
+   png_debug(1, "in png_handle_sRGB");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
+      return;
+   }
+
+   if (length != 1)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, &intent, 1);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   /* If a colorspace error has already been output skip this chunk */
+   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
+      return;
+
+   /* 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_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID;
+      png_colorspace_sync(png_ptr, info_ptr);
+      png_chunk_benign_error(png_ptr, "too many profiles");
+      return;
+   }
+
+   (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent);
+   png_colorspace_sync(png_ptr, info_ptr);
+}
+#endif /* READ_sRGB */
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+void /* PRIVATE */
+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_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) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
+      return;
+   }
+
+   /* 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 at least one character and there is
+    * a terminator (0) byte and the compression method.
+    */
+   if (length < 9)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too short");
+      return;
+   }
+
+   /* If a colorspace error has already been output skip this chunk */
+   if ((png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   /* 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)
+   {
+      uInt read_length, keyword_length;
+      char keyword[81];
+
+      /* 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;
+
+      png_crc_read(png_ptr, (png_bytep)keyword, read_length);
+      length -= read_length;
+
+      keyword_length = 0;
+      while (keyword_length < 80 && keyword_length < read_length &&
+         keyword[keyword_length] != 0)
+         ++keyword_length;
+
+      /* 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;
+
+            if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK)
+            {
+               Byte profile_header[132];
+               Byte local_buffer[PNG_INFLATE_BUF_SIZE];
+               png_alloc_size_t size = (sizeof profile_header);
+
+               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) != 0)
+                  {
+                     /* 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) != 0)
+                     {
+                        /* 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 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) != 0)
+                              {
+                                 /* 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";
+
+#ifndef __COVERITY__
+                                 else
+                                    errmsg = png_ptr->zstream.msg;
+#endif
+                              }
+
+                              /* else png_icc_check_tag_table output an error */
+                           }
+
+                           else /* profile truncated */
+                              errmsg = png_ptr->zstream.msg;
+                        }
+
+                        else
+                           errmsg = "out of memory";
+                     }
+
+                     /* else png_icc_check_header output an error */
+                  }
+
+                  /* else png_icc_check_length output an error */
+               }
+
+               else /* profile truncated */
+                  errmsg = png_ptr->zstream.msg;
+
+               /* Release the stream */
+               png_ptr->zowner = 0;
+            }
+
+            else /* png_inflate_claim failed */
+               errmsg = png_ptr->zstream.msg;
+         }
+
+         else
+            errmsg = "bad compression method"; /* or missing */
+      }
+
+      else
+         errmsg = "bad keyword";
+   }
+
+   else
+      errmsg = "too many profiles";
+
+   /* Failure: the reason is in 'errmsg' */
+   if (finished == 0)
+      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 /* READ_iCCP */
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+void /* PRIVATE */
+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, 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_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)
+      {
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      if (--png_ptr->user_chunk_cache_max == 1)
+      {
+         png_warning(png_ptr, "No space in chunk cache for sPLT");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+   }
+#endif
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of place");
+      return;
+   }
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > 65535U)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too large to fit in memory");
+      return;
+   }
+#endif
+
+   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.
+    */
+   png_crc_read(png_ptr, buffer, length);
+
+   if (png_crc_finish(png_ptr, skip) != 0)
+      return;
+
+   buffer[length] = 0;
+
+   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 (length < 2U || entry_start > buffer + (length - 2U))
+   {
+      png_warning(png_ptr, "malformed sPLT chunk");
+      return;
+   }
+
+   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.
+    */
+   data_length = length - (png_uint_32)(entry_start - buffer);
+
+   /* Integrity-check the data length */
+   if ((data_length % entry_size) != 0)
+   {
+      png_warning(png_ptr, "sPLT chunk has bad length");
+      return;
+   }
+
+   dl = (png_int_32)(data_length / entry_size);
+   max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry));
+
+   if (dl > max_dl)
+   {
+      png_warning(png_ptr, "sPLT chunk too long");
+      return;
+   }
+
+   new_palette.nentries = (png_int_32)(data_length / entry_size);
+
+   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
+       png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry)));
+
+   if (new_palette.entries == NULL)
+   {
+      png_warning(png_ptr, "sPLT chunk requires too much memory");
+      return;
+   }
+
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+   for (i = 0; i < new_palette.nentries; i++)
+   {
+      pp = new_palette.entries + i;
+
+      if (new_palette.depth == 8)
+      {
+         pp->red = *entry_start++;
+         pp->green = *entry_start++;
+         pp->blue = *entry_start++;
+         pp->alpha = *entry_start++;
+      }
+
+      else
+      {
+         pp->red   = png_get_uint_16(entry_start); entry_start += 2;
+         pp->green = png_get_uint_16(entry_start); entry_start += 2;
+         pp->blue  = png_get_uint_16(entry_start); entry_start += 2;
+         pp->alpha = png_get_uint_16(entry_start); entry_start += 2;
+      }
+
+      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
+   }
+#else
+   pp = new_palette.entries;
+
+   for (i = 0; i < new_palette.nentries; i++)
+   {
+
+      if (new_palette.depth == 8)
+      {
+         pp[i].red   = *entry_start++;
+         pp[i].green = *entry_start++;
+         pp[i].blue  = *entry_start++;
+         pp[i].alpha = *entry_start++;
+      }
+
+      else
+      {
+         pp[i].red   = png_get_uint_16(entry_start); entry_start += 2;
+         pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
+         pp[i].blue  = png_get_uint_16(entry_start); entry_start += 2;
+         pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
+      }
+
+      pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2;
+   }
+#endif
+
+   /* Discard all chunk data except the name and stash that */
+   new_palette.name = (png_charp)buffer;
+
+   png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
+
+   png_free(png_ptr, new_palette.entries);
+}
+#endif /* READ_sPLT */
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+void /* PRIVATE */
+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) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      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) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_byte buf[2];
+
+      if (length != 2)
+      {
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "invalid");
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_color.gray = png_get_uint_16(buf);
+   }
+
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_byte buf[6];
+
+      if (length != 6)
+      {
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "invalid");
+         return;
+      }
+
+      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);
+      png_ptr->trans_color.blue = png_get_uint_16(buf + 4);
+   }
+
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if ((png_ptr->mode & PNG_HAVE_PLTE) == 0)
+      {
+         /* 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 > (unsigned int) png_ptr->num_palette ||
+         length > (unsigned int) PNG_MAX_PALETTE_LENGTH ||
+         length == 0)
+      {
+         png_crc_finish(png_ptr, length);
+         png_chunk_benign_error(png_ptr, "invalid");
+         return;
+      }
+
+      png_crc_read(png_ptr, readbuf, length);
+      png_ptr->num_trans = (png_uint_16)length;
+   }
+
+   else
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid with alpha channel");
+      return;
+   }
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+   {
+      png_ptr->num_trans = 0;
+      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));
+}
+#endif
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+void /* PRIVATE */
+png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   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) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
+       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+       (png_ptr->mode & PNG_HAVE_PLTE) == 0))
+   {
+      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) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 1;
+
+   else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      truelen = 6;
+
+   else
+      truelen = 2;
+
+   if (length != truelen)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   /* We convert the index value into RGB components so that we can allow
+    * arbitrary RGB values for background when we have transparency, and
+    * so it is easy to determine the RGB values of the background color
+    * from the info_ptr struct.
+    */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      background.index = buf[0];
+
+      if (info_ptr != NULL && info_ptr->num_palette != 0)
+      {
+         if (buf[0] >= info_ptr->num_palette)
+         {
+            png_chunk_benign_error(png_ptr, "invalid index");
+            return;
+         }
+
+         background.red = (png_uint_16)png_ptr->palette[buf[0]].red;
+         background.green = (png_uint_16)png_ptr->palette[buf[0]].green;
+         background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue;
+      }
+
+      else
+         background.red = background.green = background.blue = 0;
+
+      background.gray = 0;
+   }
+
+   else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* GRAY */
+   {
+      background.index = 0;
+      background.red =
+      background.green =
+      background.blue =
+      background.gray = png_get_uint_16(buf);
+   }
+
+   else
+   {
+      background.index = 0;
+      background.red = png_get_uint_16(buf);
+      background.green = png_get_uint_16(buf + 2);
+      background.blue = png_get_uint_16(buf + 4);
+      background.gray = 0;
+   }
+
+   png_set_bKGD(png_ptr, info_ptr, &background);
+}
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+void /* PRIVATE */
+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];
+
+   png_debug(1, "in png_handle_hIST");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0 ||
+       (png_ptr->mode & PNG_HAVE_PLTE) == 0)
+   {
+      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) != 0)
+   {
+      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)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[2];
+
+      png_crc_read(png_ptr, buf, 2);
+      readbuf[i] = png_get_uint_16(buf);
+   }
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   png_set_hIST(png_ptr, info_ptr, readbuf);
+}
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+void /* PRIVATE */
+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;
+   int unit_type;
+
+   png_debug(1, "in png_handle_pHYs");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      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) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   res_x = png_get_uint_32(buf);
+   res_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
+}
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+void /* PRIVATE */
+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;
+   int unit_type;
+
+   png_debug(1, "in png_handle_oFFs");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      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) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   offset_x = png_get_int_32(buf);
+   offset_y = png_get_int_32(buf + 4);
+   unit_type = buf[8];
+   png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
+}
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+/* Read the pCAL chunk (described in the PNG Extensions document) */
+void /* PRIVATE */
+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_bytep buffer, buf, units, endptr;
+   png_charpp params;
+   int i;
+
+   png_debug(1, "in png_handle_pCAL");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      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) != 0)
+   {
+      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);
+
+   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;
+   }
+
+   png_crc_read(png_ptr, buffer, length);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   buffer[length] = 0; /* Null terminate the last string */
+
+   png_debug(3, "Finding end of pCAL purpose string");
+   for (buf = buffer; *buf; buf++)
+      /* Empty loop */ ;
+
+   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_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_debug(3, "Reading pCAL X0, X1, type, nparams, and units");
+   X0 = png_get_int_32((png_bytep)buf+1);
+   X1 = png_get_int_32((png_bytep)buf+5);
+   type = buf[9];
+   nparams = buf[10];
+   units = buf + 11;
+
+   png_debug(3, "Checking pCAL equation type and number of parameters");
+   /* Check that we have the right number of parameters for known
+    * equation types.
+    */
+   if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
+       (type == PNG_EQUATION_BASE_E && nparams != 3) ||
+       (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
+       (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
+   {
+      png_chunk_benign_error(png_ptr, "invalid parameter count");
+      return;
+   }
+
+   else if (type >= PNG_EQUATION_LAST)
+   {
+      png_chunk_benign_error(png_ptr, "unrecognized equation type");
+   }
+
+   for (buf = units; *buf; buf++)
+      /* Empty loop to move past the units string. */ ;
+
+   png_debug(3, "Allocating pCAL parameters array");
+
+   params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
+       nparams * (sizeof (png_charp))));
+
+   if (params == NULL)
+   {
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return;
+   }
+
+   /* Get pointers to the start of each parameter string. */
+   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] = (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_free(png_ptr, params);
+         png_chunk_benign_error(png_ptr, "invalid data");
+         return;
+      }
+   }
+
+   png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams,
+      (png_charp)units, params);
+
+   png_free(png_ptr, params);
+}
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+/* Read the sCAL chunk */
+void /* PRIVATE */
+png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_bytep buffer;
+   png_size_t i;
+   int state;
+
+   png_debug(1, "in png_handle_sCAL");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+   {
+      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) != 0)
+   {
+      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_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);
+
+   buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/);
+
+   if (buffer == NULL)
+   {
+      png_chunk_benign_error(png_ptr, "out of memory");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buffer, length);
+   buffer[length] = 0; /* Null terminate the last string */
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   /* Validate the unit. */
+   if (buffer[0] != 1 && buffer[0] != 2)
+   {
+      png_chunk_benign_error(png_ptr, "invalid unit");
+      return;
+   }
+
+   /* Validate the ASCII numbers, need two ASCII numbers separated by
+    * a '\0' and they need to fit exactly in the chunk data.
+    */
+   i = 1;
+   state = 0;
+
+   if (png_check_fp_number((png_const_charp)buffer, length, &state, &i) == 0 ||
+       i >= length || buffer[i++] != 0)
+      png_chunk_benign_error(png_ptr, "bad width format");
+
+   else if (PNG_FP_IS_POSITIVE(state) == 0)
+      png_chunk_benign_error(png_ptr, "non-positive width");
+
+   else
+   {
+      png_size_t heighti = i;
+
+      state = 0;
+      if (png_check_fp_number((png_const_charp)buffer, length,
+          &state, &i) == 0 || i != length)
+         png_chunk_benign_error(png_ptr, "bad height format");
+
+      else if (PNG_FP_IS_POSITIVE(state) == 0)
+         png_chunk_benign_error(png_ptr, "non-positive height");
+
+      else
+         /* This is the (only) success case. */
+         png_set_sCAL_s(png_ptr, info_ptr, buffer[0],
+            (png_charp)buffer+1, (png_charp)buffer+heighti);
+   }
+}
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+void /* PRIVATE */
+png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_byte buf[7];
+   png_time mod_time;
+
+   png_debug(1, "in png_handle_tIME");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   if (length != 7)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "invalid");
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 7);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   mod_time.second = buf[6];
+   mod_time.minute = buf[5];
+   mod_time.hour = buf[4];
+   mod_time.day = buf[3];
+   mod_time.month = buf[2];
+   mod_time.year = png_get_uint_16(buf);
+
+   png_set_tIME(png_ptr, info_ptr, &mod_time);
+}
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_text  text_info;
+   png_bytep buffer;
+   png_charp key;
+   png_charp text;
+   png_uint_32 skip = 0;
+
+   png_debug(1, "in png_handle_tEXt");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+   if (png_ptr->user_chunk_cache_max != 0)
+   {
+      if (png_ptr->user_chunk_cache_max == 1)
+      {
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      if (--png_ptr->user_chunk_cache_max == 1)
+      {
+         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) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > 65535U)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "too large to fit in memory");
+      return;
+   }
+#endif
+
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
+
+   if (buffer == NULL)
+   {
+     png_chunk_benign_error(png_ptr, "out of memory");
+     return;
+   }
+
+   png_crc_read(png_ptr, buffer, length);
+
+   if (png_crc_finish(png_ptr, skip) != 0)
+      return;
+
+   key = (png_charp)buffer;
+   key[length] = 0;
+
+   for (text = key; *text; text++)
+      /* Empty loop to find end of key */ ;
+
+   if (text != key + length)
+      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 (png_set_text_2(png_ptr, info_ptr, &text_info, 1) != 0)
+      png_warning(png_ptr, "Insufficient memory to process text chunk");
+}
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+/* Note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_const_charp errmsg = NULL;
+   png_bytep       buffer;
+   png_uint_32     keyword_length;
+
+   png_debug(1, "in png_handle_zTXt");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+   if (png_ptr->user_chunk_cache_max != 0)
+   {
+      if (png_ptr->user_chunk_cache_max == 1)
+      {
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      if (--png_ptr->user_chunk_cache_max == 1)
+      {
+         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) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   buffer = png_read_buffer(png_ptr, length, 2/*silent*/);
+
+   if (buffer == NULL)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return;
+   }
+
+   png_crc_read(png_ptr, buffer, length);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   /* 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 */ ;
+
+   if (keyword_length > 79 || keyword_length < 1)
+      errmsg = "bad keyword";
+
+   /* 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
+   {
+      png_alloc_size_t uncompressed_length = PNG_SIZE_MAX;
+
+      /* 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;
+
+         /* 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) != 0)
+            errmsg = "insufficient memory";
+      }
+
+      else
+         errmsg = png_ptr->zstream.msg;
+   }
+
+   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_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   png_const_charp errmsg = NULL;
+   png_bytep buffer;
+   png_uint_32 prefix_length;
+
+   png_debug(1, "in png_handle_iTXt");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+   if (png_ptr->user_chunk_cache_max != 0)
+   {
+      if (png_ptr->user_chunk_cache_max == 1)
+      {
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      if (--png_ptr->user_chunk_cache_max == 1)
+      {
+         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) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   if ((png_ptr->mode & PNG_HAVE_IDAT) != 0)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/);
+
+   if (buffer == NULL)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "out of memory");
+      return;
+   }
+
+   png_crc_read(png_ptr, buffer, length);
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   /* First the keyword. */
+   for (prefix_length=0;
+      prefix_length < length && buffer[prefix_length] != 0;
+      ++prefix_length)
+      /* Empty loop */ ;
+
+   /* Perform a basic check on the keyword length here. */
+   if (prefix_length > 79 || prefix_length < 1)
+      errmsg = "bad 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";
+
+   else if (buffer[prefix_length+1] == 0 ||
+      (buffer[prefix_length+1] == 1 &&
+      buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE))
+   {
+      int compressed = buffer[prefix_length+1] != 0;
+      png_uint_32 language_offset, translated_keyword_offset;
+      png_alloc_size_t uncompressed_length = 0;
+
+      /* Now the language tag */
+      prefix_length += 3;
+      language_offset = prefix_length;
+
+      for (; prefix_length < length && buffer[prefix_length] != 0;
+         ++prefix_length)
+         /* Empty loop */ ;
+
+      /* WARNING: the length may be invalid here, this is checked below. */
+      translated_keyword_offset = ++prefix_length;
+
+      for (; prefix_length < length && buffer[prefix_length] != 0;
+         ++prefix_length)
+         /* Empty loop */ ;
+
+      /* 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 allocation may overflow.
+       */
+      ++prefix_length;
+
+      if (compressed == 0 && prefix_length <= length)
+         uncompressed_length = length - prefix_length;
+
+      else if (compressed != 0 && prefix_length < length)
+      {
+         uncompressed_length = PNG_SIZE_MAX;
+
+         /* 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;
+
+         else
+            errmsg = png_ptr->zstream.msg;
+      }
+
+      else
+         errmsg = "truncated";
+
+      if (errmsg == NULL)
+      {
+         png_text text;
+
+         buffer[uncompressed_length+prefix_length] = 0;
+
+         if (compressed == 0)
+            text.compression = PNG_ITXT_COMPRESSION_NONE;
+
+         else
+            text.compression = PNG_ITXT_COMPRESSION_zTXt;
+
+         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;
+
+         if (png_set_text_2(png_ptr, info_ptr, &text, 1) != 0)
+            errmsg = "insufficient memory";
+      }
+   }
+
+   else
+      errmsg = "bad compression info";
+
+   if (errmsg != NULL)
+      png_chunk_benign_error(png_ptr, errmsg);
+}
+#endif
+
+#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_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;
+   }
+
+#  ifdef PNG_SET_USER_LIMITS_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
+
+   if (length <= limit)
+   {
+      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 (length == 0)
+         png_ptr->unknown_chunk.data = NULL;
+
+      else
+      {
+         /* Do a 'warn' here - it is handled below. */
+         png_ptr->unknown_chunk.data = png_voidcast(png_bytep,
+            png_malloc_warn(png_ptr, length));
+      }
+   }
+
+   if (png_ptr->unknown_chunk.data == NULL && length > 0)
+   {
+      /* 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;
+   }
+
+   else
+   {
+      if (length > 0)
+         png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length);
+      png_crc_finish(png_ptr, 0);
+      return 1;
+   }
+}
+#endif /* READ_UNKNOWN_CHUNKS */
+
+/* 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
+   /* 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
+
+   /* 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.
+    */
+   if (png_ptr->read_user_chunk_fn != NULL)
+   {
+      if (png_cache_unknown_chunk(png_ptr, length) != 0)
+      {
+         /* 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 occurred; 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");
+
+         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 /* READ_USER_CHUNKS */
+
+#  ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+   {
+      /* 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) == 0)
+            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
+
+   {
+      /* 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");
+
+      png_crc_finish(png_ptr, length);
+   }
+#  endif
+
+#  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)
+      {
+         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;
+
+         default: /* not at limit */
+            --(png_ptr->user_chunk_cache_max);
+            /* FALL THROUGH */
+         case 0: /* no limit */
+#  endif /* USER_LIMITS */
+            /* 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: the chunk must be handled by the user callback */
+   PNG_UNUSED(info_ptr)
+#  endif
+
+   /* 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;
+
+#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 /* !READ_UNKNOWN_CHUNKS */
+
+   /* Check for unhandled critical chunks */
+   if (handled == 0 && 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.
+ * This function can't have the "critical chunk check" incorporated
+ * into it, since in the future we will need to be able to call user
+ * functions to handle unknown critical chunks after we check that
+ * the chunk name itself is valid.
+ */
+
+/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is:
+ *
+ * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+ */
+
+void /* PRIVATE */
+png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name)
+{
+   int i;
+
+   png_debug(1, "in png_check_chunk_name");
+
+   for (i=1; i<=4; ++i)
+   {
+      int c = chunk_name & 0xff;
+
+      if (c < 65 || c > 122 || (c > 90 && c < 97))
+         png_chunk_error(png_ptr, "invalid chunk type");
+
+      chunk_name >>= 8;
+   }
+}
+
+/* Combines the row recently read in with the existing pixels in the row.  This
+ * routine takes care of alpha and transparency if requested.  This routine also
+ * handles the two methods of progressive display of interlaced images,
+ * depending on the 'display' value; if 'display' is true then the whole row
+ * (dp) is filled from the start by replicating the available pixels.  If
+ * 'display' is false only those pixels present in the pass are filled in.
+ */
+void /* PRIVATE */
+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;
+   png_alloc_size_t row_width = png_ptr->width;
+   unsigned int pass = png_ptr->pass;
+   png_bytep end_ptr = 0;
+   png_byte end_byte = 0;
+   unsigned int end_mask;
+
+   png_debug(1, "in png_combine_row");
+
+   /* Added in 1.5.6: it should not be possible to enter this routine until at
+    * least one row has been read from the PNG data and transformed.
+    */
+   if (pixel_depth == 0)
+      png_error(png_ptr, "internal row logic error");
+
+   /* Added in 1.5.4: the pixel depth should match the information returned by
+    * any call to png_read_update_info at this point.  Do not continue if we got
+    * this wrong.
+    */
+   if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes !=
+          PNG_ROWBYTES(pixel_depth, row_width))
+      png_error(png_ptr, "internal row size calculation error");
+
+   /* Don't expect this to ever happen: */
+   if (row_width == 0)
+      png_error(png_ptr, "internal row width error");
+
+   /* Preserve the last byte in cases where only part of it will be overwritten,
+    * the multiply below may overflow, we don't care because ANSI-C guarantees
+    * we get the low bits.
+    */
+   end_mask = (pixel_depth * row_width) & 7;
+   if (end_mask != 0)
+   {
+      /* end_ptr == NULL is a flag to say do nothing */
+      end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1;
+      end_byte = *end_ptr;
+#     ifdef PNG_READ_PACKSWAP_SUPPORTED
+      if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
+         /* little-endian byte */
+         end_mask = 0xff << end_mask;
+
+      else /* big-endian byte */
+#     endif
+      end_mask = 0xff >> end_mask;
+      /* end_mask is now the bits to *keep* from the destination row */
+   }
+
+   /* 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
+    * pass.
+    */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   if (png_ptr->interlaced != 0 &&
+       (png_ptr->transformations & PNG_INTERLACE) != 0 &&
+       pass < 6 && (display == 0 ||
+       /* The following copies everything for 'display' on passes 0, 2 and 4. */
+       (display == 1 && (pass & 1) != 0)))
+   {
+      /* Narrow images may have no bits in a pass; the caller should handle
+       * this, but this test is cheap:
+       */
+      if (row_width <= PNG_PASS_START_COL(pass))
+         return;
+
+      if (pixel_depth < 8)
+      {
+         /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit
+          * into 32 bits, then a single loop over the bytes using the four byte
+          * values in the 32-bit mask can be used.  For the 'display' option the
+          * expanded mask may also not require any masking within a byte.  To
+          * make this work the PACKSWAP option must be taken into account - it
+          * simply requires the pixels to be reversed in each byte.
+          *
+          * The 'regular' case requires a mask for each of the first 6 passes,
+          * the 'display' case does a copy for the even passes in the range
+          * 0..6.  This has already been handled in the test above.
+          *
+          * The masks are arranged as four bytes with the first byte to use in
+          * the lowest bits (little-endian) regardless of the order (PACKSWAP or
+          * not) of the pixels in each byte.
+          *
+          * NOTE: the whole of this logic depends on the caller of this function
+          * only calling it on rows appropriate to the pass.  This function only
+          * understands the 'x' logic; the 'y' logic is handled by the caller.
+          *
+          * The following defines allow generation of compile time constant bit
+          * masks for each pixel depth and each possibility of swapped or not
+          * swapped bytes.  Pass 'p' is in the range 0..6; 'x', a pixel index,
+          * is in the range 0..7; and the result is 1 if the pixel is to be
+          * copied in the pass, 0 if not.  'S' is for the sparkle method, 'B'
+          * for the block method.
+          *
+          * With some compilers a compile time expression of the general form:
+          *
+          *    (shift >= 32) ? (a >> (shift-32)) : (b >> shift)
+          *
+          * Produces warnings with values of 'shift' in the range 33 to 63
+          * because the right hand side of the ?: expression is evaluated by
+          * the compiler even though it isn't used.  Microsoft Visual C (various
+          * versions) and the Intel C compiler are known to do this.  To avoid
+          * this the following macros are used in 1.5.6.  This is a temporary
+          * solution to avoid destabilizing the code during the release process.
+          */
+#        if PNG_USE_COMPILE_TIME_MASKS
+#           define PNG_LSR(x,s) ((x)>>((s) & 0x1f))
+#           define PNG_LSL(x,s) ((x)<<((s) & 0x1f))
+#        else
+#           define PNG_LSR(x,s) ((x)>>(s))
+#           define PNG_LSL(x,s) ((x)<<(s))
+#        endif
+#        define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\
+           PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1)
+#        define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\
+           PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1)
+
+         /* Return a mask for pass 'p' pixel 'x' at depth 'd'.  The mask is
+          * little endian - the first pixel is at bit 0 - however the extra
+          * parameter 's' can be set to cause the mask position to be swapped
+          * within each byte, to match the PNG format.  This is done by XOR of
+          * the shift with 7, 6 or 4 for bit depths 1, 2 and 4.
+          */
+#        define PIXEL_MASK(p,x,d,s) \
+            (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0))))
+
+         /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask.
+          */
+#        define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)
+#        define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0)
+
+         /* Combine 8 of these to get the full mask.  For the 1-bpp and 2-bpp
+          * cases the result needs replicating, for the 4-bpp case the above
+          * generates a full 32 bits.
+          */
+#        define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1)))
+
+#        define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\
+            S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\
+            S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d)
+
+#        define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\
+            B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\
+            B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d)
+
+#if PNG_USE_COMPILE_TIME_MASKS
+         /* Utility macros to construct all the masks for a depth/swap
+          * combination.  The 's' parameter says whether the format is PNG
+          * (big endian bytes) or not.  Only the three odd-numbered passes are
+          * required for the display/block algorithm.
+          */
+#        define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\
+            S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) }
+
+#        define B_MASKS(d,s) { B_MASK(1,d,s), B_MASK(3,d,s), B_MASK(5,d,s) }
+
+#        define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2))
+
+         /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and
+          * then pass:
+          */
+         static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] =
+         {
+            /* Little-endian byte masks for PACKSWAP */
+            { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) },
+            /* Normal (big-endian byte) masks - PNG format */
+            { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) }
+         };
+
+         /* display_mask has only three entries for the odd passes, so index by
+          * pass>>1.
+          */
+         static PNG_CONST png_uint_32 display_mask[2][3][3] =
+         {
+            /* Little-endian byte masks for PACKSWAP */
+            { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) },
+            /* Normal (big-endian byte) masks - PNG format */
+            { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) }
+         };
+
+#        define MASK(pass,depth,display,png)\
+            ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\
+               row_mask[png][DEPTH_INDEX(depth)][pass])
+
+#else /* !PNG_USE_COMPILE_TIME_MASKS */
+         /* This is the runtime alternative: it seems unlikely that this will
+          * ever be either smaller or faster than the compile time approach.
+          */
+#        define MASK(pass,depth,display,png)\
+            ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png))
+#endif /* !USE_COMPILE_TIME_MASKS */
+
+         /* Use the appropriate mask to copy the required bits.  In some cases
+          * the byte mask will be 0 or 0xff; optimize these cases.  row_width is
+          * the number of pixels, but the code copies bytes, so it is necessary
+          * to special case the end.
+          */
+         png_uint_32 pixels_per_byte = 8 / pixel_depth;
+         png_uint_32 mask;
+
+#        ifdef PNG_READ_PACKSWAP_SUPPORTED
+         if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
+            mask = MASK(pass, pixel_depth, display, 0);
+
+         else
+#        endif
+         mask = MASK(pass, pixel_depth, display, 1);
+
+         for (;;)
+         {
+            png_uint_32 m;
+
+            /* It doesn't matter in the following if png_uint_32 has more than
+             * 32 bits because the high bits always match those in m<<24; it is,
+             * however, essential to use OR here, not +, because of this.
+             */
+            m = mask;
+            mask = (m >> 8) | (m << 24); /* rotate right to good compilers */
+            m &= 0xff;
+
+            if (m != 0) /* something to copy */
+            {
+               if (m != 0xff)
+                  *dp = (png_byte)((*dp & ~m) | (*sp & m));
+               else
+                  *dp = *sp;
+            }
+
+            /* NOTE: this may overwrite the last byte with garbage if the image
+             * is not an exact number of bytes wide; libpng has always done
+             * this.
+             */
+            if (row_width <= pixels_per_byte)
+               break; /* May need to restore part of the last byte */
+
+            row_width -= pixels_per_byte;
+            ++dp;
+            ++sp;
+         }
+      }
+
+      else /* pixel_depth >= 8 */
+      {
+         unsigned int bytes_to_copy, bytes_to_jump;
+
+         /* Validate the depth - it must be a multiple of 8 */
+         if (pixel_depth & 7)
+            png_error(png_ptr, "invalid user transform pixel depth");
+
+         pixel_depth >>= 3; /* now in bytes */
+         row_width *= pixel_depth;
+
+         /* Regardless of pass number the Adam 7 interlace always results in a
+          * fixed number of pixels to copy then to skip.  There may be a
+          * different number of pixels to skip at the start though.
+          */
+         {
+            unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth;
+
+            row_width -= offset;
+            dp += offset;
+            sp += offset;
+         }
+
+         /* Work out the bytes to copy. */
+         if (display != 0)
+         {
+            /* When doing the 'block' algorithm the pixel in the pass gets
+             * replicated to adjacent pixels.  This is why the even (0,2,4,6)
+             * passes are skipped above - the entire expanded row is copied.
+             */
+            bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth;
+
+            /* But don't allow this number to exceed the actual row width. */
+            if (bytes_to_copy > row_width)
+               bytes_to_copy = (unsigned int)/*SAFE*/row_width;
+         }
+
+         else /* normal row; Adam7 only ever gives us one pixel to copy. */
+            bytes_to_copy = pixel_depth;
+
+         /* In Adam7 there is a constant offset between where the pixels go. */
+         bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth;
+
+         /* And simply copy these bytes.  Some optimization is possible here,
+          * depending on the value of 'bytes_to_copy'.  Special case the low
+          * byte counts, which we know to be frequent.
+          *
+          * Notice that these cases all 'return' rather than 'break' - this
+          * avoids an unnecessary test on whether to restore the last byte
+          * below.
+          */
+         switch (bytes_to_copy)
+         {
+            case 1:
+               for (;;)
+               {
+                  *dp = *sp;
+
+                  if (row_width <= bytes_to_jump)
+                     return;
+
+                  dp += bytes_to_jump;
+                  sp += bytes_to_jump;
+                  row_width -= bytes_to_jump;
+               }
+
+            case 2:
+               /* There is a possibility of a partial copy at the end here; this
+                * slows the code down somewhat.
+                */
+               do
+               {
+                  dp[0] = sp[0], dp[1] = sp[1];
+
+                  if (row_width <= bytes_to_jump)
+                     return;
+
+                  sp += bytes_to_jump;
+                  dp += bytes_to_jump;
+                  row_width -= bytes_to_jump;
+               }
+               while (row_width > 1);
+
+               /* And there can only be one byte left at this point: */
+               *dp = *sp;
+               return;
+
+            case 3:
+               /* This can only be the RGB case, so each copy is exactly one
+                * pixel and it is not necessary to check for a partial copy.
+                */
+               for (;;)
+               {
+                  dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2];
+
+                  if (row_width <= bytes_to_jump)
+                     return;
+
+                  sp += bytes_to_jump;
+                  dp += bytes_to_jump;
+                  row_width -= bytes_to_jump;
+               }
+
+            default:
+#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE
+               /* 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 memcpy there.
+                */
+               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)
+               {
+                  /* Everything is aligned for png_uint_16 copies, but try for
+                   * png_uint_32 first.
+                   */
+                  if (png_isaligned(dp, png_uint_32) != 0 &&
+                      png_isaligned(sp, 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_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
+                     {
+                        size_t c = bytes_to_copy;
+                        do
+                        {
+                           *dp32++ = *sp32++;
+                           c -= (sizeof (png_uint_32));
+                        }
+                        while (c > 0);
+
+                        if (row_width <= bytes_to_jump)
+                           return;
+
+                        dp32 += skip;
+                        sp32 += skip;
+                        row_width -= bytes_to_jump;
+                     }
+                     while (bytes_to_copy <= row_width);
+
+                     /* Get to here when the row_width truncates the final copy.
+                      * There will be 1-3 bytes left to copy, so don't try the
+                      * 16-bit loop below.
+                      */
+                     dp = (png_bytep)dp32;
+                     sp = (png_const_bytep)sp32;
+                     do
+                        *dp++ = *sp++;
+                     while (--row_width > 0);
+                     return;
+                  }
+
+                  /* Else do it in 16-bit quantities, but only if the size is
+                   * not too large.
+                   */
+                  else
+                  {
+                     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
+                     {
+                        size_t c = bytes_to_copy;
+                        do
+                        {
+                           *dp16++ = *sp16++;
+                           c -= (sizeof (png_uint_16));
+                        }
+                        while (c > 0);
+
+                        if (row_width <= bytes_to_jump)
+                           return;
+
+                        dp16 += skip;
+                        sp16 += skip;
+                        row_width -= bytes_to_jump;
+                     }
+                     while (bytes_to_copy <= row_width);
+
+                     /* End of row - 1 byte left, bytes_to_copy > row_width: */
+                     dp = (png_bytep)dp16;
+                     sp = (png_const_bytep)sp16;
+                     do
+                        *dp++ = *sp++;
+                     while (--row_width > 0);
+                     return;
+                  }
+               }
+#endif /* ALIGN_TYPE code */
+
+               /* The true default - use a memcpy: */
+               for (;;)
+               {
+                  memcpy(dp, sp, bytes_to_copy);
+
+                  if (row_width <= bytes_to_jump)
+                     return;
+
+                  sp += bytes_to_jump;
+                  dp += bytes_to_jump;
+                  row_width -= bytes_to_jump;
+                  if (bytes_to_copy > row_width)
+                     bytes_to_copy = (unsigned int)/*SAFE*/row_width;
+               }
+         }
+
+         /* NOT REACHED*/
+      } /* pixel_depth >= 8 */
+
+      /* Here if pixel_depth < 8 to check 'end_ptr' below. */
+   }
+   else
+#endif /* READ_INTERLACING */
+
+   /* 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.)
+    */
+   memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));
+
+   /* Restore the overwritten bits from the last byte if necessary. */
+   if (end_ptr != NULL)
+      *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask));
+}
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+void /* PRIVATE */
+png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
+   png_uint_32 transformations /* Because these may affect the byte layout */)
+{
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+   /* Offset to next interlace block */
+   static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   png_debug(1, "in png_do_read_interlace");
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+            if ((transformations & PNG_PACKSWAP) != 0)
+            {
+                sshift = (int)((row_info->width + 7) & 0x07);
+                dshift = (int)((final_width + 7) & 0x07);
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            else
+#endif
+            {
+                sshift = 7 - (int)((row_info->width + 7) & 0x07);
+                dshift = 7 - (int)((final_width + 7) & 0x07);
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x01);
+               for (j = 0; j < jstop; j++)
+               {
+                  unsigned int tmp = *dp & (0x7f7f >> (7 - dshift));
+                  tmp |= v << dshift;
+                  *dp = (png_byte)(tmp & 0xff);
+
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+
+                  else
+                     dshift += s_inc;
+               }
+
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
+            png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+            if ((transformations & PNG_PACKSWAP) != 0)
+            {
+               sshift = (int)(((row_info->width + 3) & 0x03) << 1);
+               dshift = (int)(((final_width + 3) & 0x03) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            else
+#endif
+            {
+               sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);
+               dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x03);
+               for (j = 0; j < jstop; j++)
+               {
+                  unsigned int tmp = *dp & (0x3f3f >> (6 - dshift));
+                  tmp |= v << dshift;
+                  *dp = (png_byte)(tmp & 0xff);
+
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+
+                  else
+                     dshift += s_inc;
+               }
+
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+            int jstop = png_pass_inc[pass];
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+            if ((transformations & PNG_PACKSWAP) != 0)
+            {
+               sshift = (int)(((row_info->width + 1) & 0x01) << 2);
+               dshift = (int)(((final_width + 1) & 0x01) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+
+            else
+#endif
+            {
+               sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);
+               dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v = (png_byte)((*sp >> sshift) & 0x0f);
+               int j;
+
+               for (j = 0; j < jstop; j++)
+               {
+                  unsigned int tmp = *dp & (0xf0f >> (4 - dshift));
+                  tmp |= v << dshift;
+                  *dp = (png_byte)(tmp & 0xff);
+
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+
+                  else
+                     dshift += s_inc;
+               }
+
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         default:
+         {
+            png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
+
+            png_bytep sp = row + (png_size_t)(row_info->width - 1)
+                * pixel_bytes;
+
+            png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
+
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v[8]; /* SAFE; pixel_depth does not exceed 64 */
+               int j;
+
+               memcpy(v, sp, pixel_bytes);
+
+               for (j = 0; j < jstop; j++)
+               {
+                  memcpy(dp, v, pixel_bytes);
+                  dp -= pixel_bytes;
+               }
+
+               sp -= pixel_bytes;
+            }
+            break;
+         }
+      }
+
+      row_info->width = final_width;
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width);
+   }
+#ifndef PNG_READ_PACKSWAP_SUPPORTED
+   PNG_UNUSED(transformations)  /* Silence compiler warning */
+#endif
+}
+#endif /* READ_INTERLACING */
+
+static void
+png_read_filter_row_sub(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev_row)
+{
+   png_size_t i;
+   png_size_t istop = row_info->rowbytes;
+   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
+   png_bytep rp = row + bpp;
+
+   PNG_UNUSED(prev_row)
+
+   for (i = bpp; i < istop; i++)
+   {
+      *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff);
+      rp++;
+   }
+}
+
+static void
+png_read_filter_row_up(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev_row)
+{
+   png_size_t i;
+   png_size_t istop = row_info->rowbytes;
+   png_bytep rp = row;
+   png_const_bytep pp = prev_row;
+
+   for (i = 0; i < istop; i++)
+   {
+      *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+      rp++;
+   }
+}
+
+static void
+png_read_filter_row_avg(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev_row)
+{
+   png_size_t i;
+   png_bytep rp = row;
+   png_const_bytep pp = prev_row;
+   unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
+   png_size_t istop = row_info->rowbytes - bpp;
+
+   for (i = 0; i < bpp; i++)
+   {
+      *rp = (png_byte)(((int)(*rp) +
+         ((int)(*pp++) / 2 )) & 0xff);
+
+      rp++;
+   }
+
+   for (i = 0; i < istop; i++)
+   {
+      *rp = (png_byte)(((int)(*rp) +
+         (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff);
+
+      rp++;
+   }
+}
+
+static void
+png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev_row)
+{
+   png_bytep rp_end = row + row_info->rowbytes;
+   int a, c;
+
+   /* First pixel/byte */
+   c = *prev_row++;
+   a = *row + c;
+   *row++ = (png_byte)a;
+
+   /* Remainder */
+   while (row < rp_end)
+   {
+      int b, pa, pb, pc, p;
+
+      a &= 0xff; /* From previous iteration or start */
+      b = *prev_row++;
+
+      p = b - c;
+      pc = a - c;
+
+#ifdef PNG_USE_ABS
+      pa = abs(p);
+      pb = abs(pc);
+      pc = abs(p + pc);
+#else
+      pa = p < 0 ? -p : p;
+      pb = pc < 0 ? -pc : pc;
+      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+      /* Find the best predictor, the least of pa, pb, pc favoring the earlier
+       * ones in the case of a tie.
+       */
+      if (pb < pa) pa = pb, a = b;
+      if (pc < pa) a = c;
+
+      /* Calculate the current pixel in a, and move the previous row pixel to c
+       * for the next time round the loop
+       */
+      c = b;
+      a += *row;
+      *row++ = (png_byte)a;
+   }
+}
+
+static void
+png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row,
+   png_const_bytep prev_row)
+{
+   int bpp = (row_info->pixel_depth + 7) >> 3;
+   png_bytep rp_end = row + bpp;
+
+   /* Process the first pixel in the row completely (this is the same as 'up'
+    * because there is only one candidate predictor for the first row).
+    */
+   while (row < rp_end)
+   {
+      int a = *row + *prev_row++;
+      *row++ = (png_byte)a;
+   }
+
+   /* Remainder */
+   rp_end += row_info->rowbytes - bpp;
+
+   while (row < rp_end)
+   {
+      int a, b, c, pa, pb, pc, p;
+
+      c = *(prev_row - bpp);
+      a = *(row - bpp);
+      b = *prev_row++;
+
+      p = b - c;
+      pc = a - c;
+
+#ifdef PNG_USE_ABS
+      pa = abs(p);
+      pb = abs(pc);
+      pc = abs(p + pc);
+#else
+      pa = p < 0 ? -p : p;
+      pb = pc < 0 ? -pc : pc;
+      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+      if (pb < pa) pa = pb, a = b;
+      if (pc < pa) a = c;
+
+      a += *row;
+      *row++ = (png_byte)a;
+   }
+}
+
+static void
+png_init_filter_functions(png_structrp pp)
+   /* This function is called once for every PNG image (except for PNG images
+    * that only use PNG_FILTER_VALUE_NONE for all rows) 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;
+
+   pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub;
+   pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up;
+   pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg;
+   if (bpp == 1)
+      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
+         png_read_filter_row_paeth_1byte_pixel;
+   else
+      pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
+         png_read_filter_row_paeth_multibyte_pixel;
+
+#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_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 (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST)
+   {
+      if (pp->read_filter[0] == NULL)
+         png_init_filter_functions(pp);
+
+      pp->read_filter[filter-1](row_info, row, prev_row);
+   }
+}
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+void /* PRIVATE */
+png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
+   png_alloc_size_t avail_out)
+{
+   /* Loop reading IDATs and decompressing the result into output[avail_out] */
+   png_ptr->zstream.next_out = output;
+   png_ptr->zstream.avail_out = 0; /* safety: set below */
+
+   if (output == NULL)
+      avail_out = 0;
+
+   do
+   {
+      int ret;
+      png_byte tmpbuf[PNG_INFLATE_BUF_SIZE];
+
+      if (png_ptr->zstream.avail_in == 0)
+      {
+         uInt avail_in;
+         png_bytep buffer;
+
+         while (png_ptr->idat_size == 0)
+         {
+            png_crc_finish(png_ptr, 0);
+
+            png_ptr->idat_size = png_read_chunk_header(png_ptr);
+            /* This is an error even in the 'check' case because the code just
+             * consumed a non-IDAT header.
+             */
+            if (png_ptr->chunk_name != png_IDAT)
+               png_error(png_ptr, "Not enough image data");
+         }
+
+         avail_in = png_ptr->IDAT_read_size;
+
+         if (avail_in > png_ptr->idat_size)
+            avail_in = (uInt)png_ptr->idat_size;
+
+         /* A PNG with a gradually increasing IDAT size will defeat this attempt
+          * to minimize memory usage by causing lots of re-allocs, but
+          * realistically doing IDAT_read_size re-allocs is not likely to be a
+          * big problem.
+          */
+         buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/);
+
+         png_crc_read(png_ptr, buffer, avail_in);
+         png_ptr->idat_size -= avail_in;
+
+         png_ptr->zstream.next_in = buffer;
+         png_ptr->zstream.avail_in = avail_in;
+      }
+
+      /* And set up the output side. */
+      if (output != NULL) /* standard read */
+      {
+         uInt out = ZLIB_IO_MAX;
+
+         if (out > avail_out)
+            out = (uInt)avail_out;
+
+         avail_out -= out;
+         png_ptr->zstream.avail_out = out;
+      }
+
+      else /* 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 = PNG_INFLATE(png_ptr, 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) == 0)
+   {
+      /* 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) == 0)
+      {
+         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)
+{
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* Start of interlace block */
+   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* Offset to next interlace block */
+   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* Start of interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* Offset to next interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+
+   png_debug(1, "in png_read_finish_row");
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced != 0)
+   {
+      png_ptr->row_number = 0;
+
+      /* TO DO: don't do this if prev_row isn't needed (requires
+       * read-ahead of the next row's filter byte.
+       */
+      memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+      do
+      {
+         png_ptr->pass++;
+
+         if (png_ptr->pass >= 7)
+            break;
+
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+
+         if ((png_ptr->transformations & PNG_INTERLACE) == 0)
+         {
+            png_ptr->num_rows = (png_ptr->height +
+                png_pass_yinc[png_ptr->pass] - 1 -
+                png_pass_ystart[png_ptr->pass]) /
+                png_pass_yinc[png_ptr->pass];
+         }
+
+         else  /* if (png_ptr->transformations & PNG_INTERLACE) */
+            break; /* libpng deinterlacing sees every row */
+
+      } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);
+
+      if (png_ptr->pass < 7)
+         return;
+   }
+
+   /* Here after at the end of the last row of the last pass. */
+   png_read_finish_IDAT(png_ptr);
+}
+#endif /* SEQUENTIAL_READ */
+
+void /* PRIVATE */
+png_read_start_row(png_structrp png_ptr)
+{
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* Start of interlace block */
+   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* Offset to next interlace block */
+   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* Start of interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* Offset to next interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+
+   int max_pixel_depth;
+   png_size_t row_bytes;
+
+   png_debug(1, "in png_read_start_row");
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+   png_init_read_transformations(png_ptr);
+#endif
+   if (png_ptr->interlaced != 0)
+   {
+      if ((png_ptr->transformations & PNG_INTERLACE) == 0)
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+             png_pass_ystart[0]) / png_pass_yinc[0];
+
+      else
+         png_ptr->num_rows = png_ptr->height;
+
+      png_ptr->iwidth = (png_ptr->width +
+          png_pass_inc[png_ptr->pass] - 1 -
+          png_pass_start[png_ptr->pass]) /
+          png_pass_inc[png_ptr->pass];
+   }
+
+   else
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->iwidth = png_ptr->width;
+   }
+
+   max_pixel_depth = png_ptr->pixel_depth;
+
+   /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpler set of
+    * calculations to calculate the final pixel depth, then
+    * png_do_read_transforms actually does the transforms.  This means that the
+    * code which effectively calculates this value is actually repeated in three
+    * separate places.  They must all match.  Innocent changes to the order of
+    * transformations can and will break libpng in a way that causes memory
+    * overwrites.
+    *
+    * TODO: fix this.
+    */
+#ifdef PNG_READ_PACK_SUPPORTED
+   if ((png_ptr->transformations & PNG_PACK) != 0 && png_ptr->bit_depth < 8)
+      max_pixel_depth = 8;
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+   if ((png_ptr->transformations & PNG_EXPAND) != 0)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans != 0)
+            max_pixel_depth = 32;
+
+         else
+            max_pixel_depth = 24;
+      }
+
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth < 8)
+            max_pixel_depth = 8;
+
+         if (png_ptr->num_trans != 0)
+            max_pixel_depth *= 2;
+      }
+
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (png_ptr->num_trans != 0)
+         {
+            max_pixel_depth *= 4;
+            max_pixel_depth /= 3;
+         }
+      }
+   }
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   if ((png_ptr->transformations & PNG_EXPAND_16) != 0)
+   {
+#  ifdef PNG_READ_EXPAND_SUPPORTED
+      /* In fact it is an error if it isn't supported, but checking is
+       * the safe way.
+       */
+      if ((png_ptr->transformations & PNG_EXPAND) != 0)
+      {
+         if (png_ptr->bit_depth < 16)
+            max_pixel_depth *= 2;
+      }
+      else
+#  endif
+      png_ptr->transformations &= ~PNG_EXPAND_16;
+   }
+#endif
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+   if ((png_ptr->transformations & (PNG_FILLER)) != 0)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth <= 8)
+            max_pixel_depth = 16;
+
+         else
+            max_pixel_depth = 32;
+      }
+
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+         png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (max_pixel_depth <= 32)
+            max_pixel_depth = 32;
+
+         else
+            max_pixel_depth = 64;
+      }
+   }
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) != 0)
+   {
+      if (
+#ifdef PNG_READ_EXPAND_SUPPORTED
+          (png_ptr->num_trans != 0 &&
+          (png_ptr->transformations & PNG_EXPAND) != 0) ||
+#endif
+#ifdef PNG_READ_FILLER_SUPPORTED
+          (png_ptr->transformations & (PNG_FILLER)) != 0 ||
+#endif
+          png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (max_pixel_depth <= 16)
+            max_pixel_depth = 32;
+
+         else
+            max_pixel_depth = 64;
+      }
+
+      else
+      {
+         if (max_pixel_depth <= 8)
+         {
+            if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+               max_pixel_depth = 32;
+
+            else
+               max_pixel_depth = 24;
+         }
+
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            max_pixel_depth = 64;
+
+         else
+            max_pixel_depth = 48;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
+defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
+   {
+      int user_pixel_depth = png_ptr->user_transform_depth *
+         png_ptr->user_transform_channels;
+
+      if (user_pixel_depth > max_pixel_depth)
+         max_pixel_depth = user_pixel_depth;
+   }
+#endif
+
+   /* This value is stored in png_struct and double checked in the row read
+    * code.
+    */
+   png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth;
+   png_ptr->transformed_pixel_depth = 0; /* calculated on demand */
+
+   /* Align the width on the next larger 8 pixels.  Mainly used
+    * for interlacing
+    */
+   row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
+   /* Calculate the maximum bytes needed, adding a byte and a pixel
+    * for safety's sake
+    */
+   row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +
+       1 + ((max_pixel_depth + 7) >> 3);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (row_bytes > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+
+   if (row_bytes + 48 > png_ptr->old_big_row_buf_size)
+   {
+     png_free(png_ptr, png_ptr->big_row_buf);
+     png_free(png_ptr, png_ptr->big_prev_row);
+
+     if (png_ptr->interlaced != 0)
+        png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
+            row_bytes + 48);
+
+     else
+        png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
+
+     png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48);
+
+#ifdef PNG_ALIGNED_MEMORY_SUPPORTED
+     /* Use 16-byte aligned memory for row_buf with at least 16 bytes
+      * of padding before and after row_buf; treat prev_row similarly.
+      * NOTE: the alignment is to the start of the pixels, one beyond the start
+      * of the buffer, because of the filter byte.  Prior to libpng 1.5.6 this
+      * was incorrect; the filter byte was aligned, which had the exact
+      * opposite effect of that intended.
+      */
+     {
+        png_bytep temp = png_ptr->big_row_buf + 32;
+        int extra = (int)((temp - (png_bytep)0) & 0x0f);
+        png_ptr->row_buf = temp - extra - 1/*filter byte*/;
+
+        temp = png_ptr->big_prev_row + 32;
+        extra = (int)((temp - (png_bytep)0) & 0x0f);
+        png_ptr->prev_row = temp - extra - 1/*filter byte*/;
+     }
+
+#else
+     /* Use 31 bytes of padding before and 17 bytes after row_buf. */
+     png_ptr->row_buf = png_ptr->big_row_buf + 31;
+     png_ptr->prev_row = png_ptr->big_prev_row + 31;
+#endif
+     png_ptr->old_big_row_buf_size = row_bytes + 48;
+   }
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (png_ptr->rowbytes > 65535)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+
+#endif
+   if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1))
+      png_error(png_ptr, "Row has too many bytes to allocate in memory");
+
+   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);
+   png_debug1(3, "iwidth = %u,", png_ptr->iwidth);
+   png_debug1(3, "num_rows = %u,", png_ptr->num_rows);
+   png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes);
+   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 != 0)
+   {
+      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) != Z_OK)
+      png_error(png_ptr, png_ptr->zstream.msg);
+
+   png_ptr->flags |= PNG_FLAG_ROW_INIT;
+}
+#endif /* READ */
diff --git a/third_party/lpng_v163/pngset.c b/third_party/libpng/pngset.c
similarity index 91%
rename from third_party/lpng_v163/pngset.c
rename to third_party/libpng/pngset.c
index b479052..8fd7965 100644
--- a/third_party/lpng_v163/pngset.c
+++ b/third_party/libpng/pngset.c
@@ -1,1596 +1,1647 @@
-/* pngset.c - storage of image information into info struct

- *

- * Last changed in libpng 1.6.3 [July 18, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * The functions here are used during reads to store data from the file

- * into the info struct, and during writes to store application data

- * into the info struct for writing into the file.  This abstracts the

- * info struct and allows us to change the structure in the future.

- */

-

-#include "pngpriv.h"

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-

-#ifdef PNG_bKGD_SUPPORTED

-void PNGAPI

-png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_color_16p background)

-{

-   png_debug1(1, "in %s storage function", "bKGD");

-

-   if (png_ptr == NULL || info_ptr == NULL || background == NULL)

-      return;

-

-   info_ptr->background = *background;

-   info_ptr->valid |= PNG_INFO_bKGD;

-}

-#endif

-

-#ifdef PNG_cHRM_SUPPORTED

-void PNGFAPI

-png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,

-    png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,

-    png_fixed_point blue_x, png_fixed_point blue_y)

-{

-   png_xy xy;

-

-   png_debug1(1, "in %s storage function", "cHRM fixed");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   xy.redx = red_x;

-   xy.redy = red_y;

-   xy.greenx = green_x;

-   xy.greeny = green_y;

-   xy.bluex = blue_x;

-   xy.bluey = blue_y;

-   xy.whitex = white_x;

-   xy.whitey = white_y;

-

-   if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy,

-      2/* override with app values*/))

-      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;

-

-   png_colorspace_sync_info(png_ptr, info_ptr);

-}

-

-void PNGFAPI

-png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_fixed_point int_red_X, png_fixed_point int_red_Y,

-    png_fixed_point int_red_Z, png_fixed_point int_green_X,

-    png_fixed_point int_green_Y, png_fixed_point int_green_Z,

-    png_fixed_point int_blue_X, png_fixed_point int_blue_Y,

-    png_fixed_point int_blue_Z)

-{

-   png_XYZ XYZ;

-

-   png_debug1(1, "in %s storage function", "cHRM XYZ fixed");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   XYZ.red_X = int_red_X;

-   XYZ.red_Y = int_red_Y;

-   XYZ.red_Z = int_red_Z;

-   XYZ.green_X = int_green_X;

-   XYZ.green_Y = int_green_Y;

-   XYZ.green_Z = int_green_Z;

-   XYZ.blue_X = int_blue_X;

-   XYZ.blue_Y = int_blue_Y;

-   XYZ.blue_Z = int_blue_Z;

-

-   if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2))

-      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;

-

-   png_colorspace_sync_info(png_ptr, info_ptr);

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,

-    double white_x, double white_y, double red_x, double red_y,

-    double green_x, double green_y, double blue_x, double blue_y)

-{

-   png_set_cHRM_fixed(png_ptr, info_ptr,

-      png_fixed(png_ptr, white_x, "cHRM White X"),

-      png_fixed(png_ptr, white_y, "cHRM White Y"),

-      png_fixed(png_ptr, red_x, "cHRM Red X"),

-      png_fixed(png_ptr, red_y, "cHRM Red Y"),

-      png_fixed(png_ptr, green_x, "cHRM Green X"),

-      png_fixed(png_ptr, green_y, "cHRM Green Y"),

-      png_fixed(png_ptr, blue_x, "cHRM Blue X"),

-      png_fixed(png_ptr, blue_y, "cHRM Blue Y"));

-}

-

-void PNGAPI

-png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X,

-    double red_Y, double red_Z, double green_X, double green_Y, double green_Z,

-    double blue_X, double blue_Y, double blue_Z)

-{

-   png_set_cHRM_XYZ_fixed(png_ptr, info_ptr,

-      png_fixed(png_ptr, red_X, "cHRM Red X"),

-      png_fixed(png_ptr, red_Y, "cHRM Red Y"),

-      png_fixed(png_ptr, red_Z, "cHRM Red Z"),

-      png_fixed(png_ptr, green_X, "cHRM Red X"),

-      png_fixed(png_ptr, green_Y, "cHRM Red Y"),

-      png_fixed(png_ptr, green_Z, "cHRM Red Z"),

-      png_fixed(png_ptr, blue_X, "cHRM Red X"),

-      png_fixed(png_ptr, blue_Y, "cHRM Red Y"),

-      png_fixed(png_ptr, blue_Z, "cHRM Red Z"));

-}

-#  endif /* PNG_FLOATING_POINT_SUPPORTED */

-

-#endif /* PNG_cHRM_SUPPORTED */

-

-#ifdef PNG_gAMA_SUPPORTED

-void PNGFAPI

-png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_fixed_point file_gamma)

-{

-   png_debug1(1, "in %s storage function", "gAMA");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma);

-   png_colorspace_sync_info(png_ptr, info_ptr);

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma)

-{

-   png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma,

-       "png_set_gAMA"));

-}

-#  endif

-#endif

-

-#ifdef PNG_hIST_SUPPORTED

-void PNGAPI

-png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_uint_16p hist)

-{

-   int i;

-

-   png_debug1(1, "in %s storage function", "hIST");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (info_ptr->num_palette == 0 || info_ptr->num_palette

-       > PNG_MAX_PALETTE_LENGTH)

-   {

-      png_warning(png_ptr,

-          "Invalid palette size, hIST allocation skipped");

-

-      return;

-   }

-

-   png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);

-

-   /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in

-    * version 1.2.1

-    */

-   info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr,

-       PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16))));

-

-   if (info_ptr->hist == NULL)

-   {

-      png_warning(png_ptr, "Insufficient memory for hIST chunk data");

-      return;

-   }

-

-   info_ptr->free_me |= PNG_FREE_HIST;

-

-   for (i = 0; i < info_ptr->num_palette; i++)

-      info_ptr->hist[i] = hist[i];

-

-   info_ptr->valid |= PNG_INFO_hIST;

-}

-#endif

-

-void PNGAPI

-png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_uint_32 width, png_uint_32 height, int bit_depth,

-    int color_type, int interlace_type, int compression_type,

-    int filter_type)

-{

-   png_debug1(1, "in %s storage function", "IHDR");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   info_ptr->width = width;

-   info_ptr->height = height;

-   info_ptr->bit_depth = (png_byte)bit_depth;

-   info_ptr->color_type = (png_byte)color_type;

-   info_ptr->compression_type = (png_byte)compression_type;

-   info_ptr->filter_type = (png_byte)filter_type;

-   info_ptr->interlace_type = (png_byte)interlace_type;

-

-   png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height,

-       info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,

-       info_ptr->compression_type, info_ptr->filter_type);

-

-   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      info_ptr->channels = 1;

-

-   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)

-      info_ptr->channels = 3;

-

-   else

-      info_ptr->channels = 1;

-

-   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)

-      info_ptr->channels++;

-

-   info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);

-

-   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);

-}

-

-#ifdef PNG_oFFs_SUPPORTED

-void PNGAPI

-png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_int_32 offset_x, png_int_32 offset_y, int unit_type)

-{

-   png_debug1(1, "in %s storage function", "oFFs");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   info_ptr->x_offset = offset_x;

-   info_ptr->y_offset = offset_y;

-   info_ptr->offset_unit_type = (png_byte)unit_type;

-   info_ptr->valid |= PNG_INFO_oFFs;

-}

-#endif

-

-#ifdef PNG_pCAL_SUPPORTED

-void PNGAPI

-png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type,

-    int nparams, png_const_charp units, png_charpp params)

-{

-   png_size_t length;

-   int i;

-

-   png_debug1(1, "in %s storage function", "pCAL");

-

-   if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL

-      || (nparams > 0 && params == NULL))

-      return;

-

-   length = strlen(purpose) + 1;

-   png_debug1(3, "allocating purpose for info (%lu bytes)",

-       (unsigned long)length);

-

-   /* TODO: validate format of calibration name and unit name */

-

-   /* Check that the type matches the specification. */

-   if (type < 0 || type > 3)

-      png_error(png_ptr, "Invalid pCAL equation type");

-

-   if (nparams < 0 || nparams > 255)

-      png_error(png_ptr, "Invalid pCAL parameter count");

-

-   /* Validate params[nparams] */

-   for (i=0; i<nparams; ++i)

-      if (params[i] == NULL ||

-         !png_check_fp_string(params[i], strlen(params[i])))

-         png_error(png_ptr, "Invalid format for pCAL parameter");

-

-   info_ptr->pcal_purpose = png_voidcast(png_charp,

-      png_malloc_warn(png_ptr, length));

-

-   if (info_ptr->pcal_purpose == NULL)

-   {

-      png_warning(png_ptr, "Insufficient memory for pCAL purpose");

-      return;

-   }

-

-   memcpy(info_ptr->pcal_purpose, purpose, length);

-

-   png_debug(3, "storing X0, X1, type, and nparams in info");

-   info_ptr->pcal_X0 = X0;

-   info_ptr->pcal_X1 = X1;

-   info_ptr->pcal_type = (png_byte)type;

-   info_ptr->pcal_nparams = (png_byte)nparams;

-

-   length = strlen(units) + 1;

-   png_debug1(3, "allocating units for info (%lu bytes)",

-     (unsigned long)length);

-

-   info_ptr->pcal_units = png_voidcast(png_charp,

-      png_malloc_warn(png_ptr, length));

-

-   if (info_ptr->pcal_units == NULL)

-   {

-      png_warning(png_ptr, "Insufficient memory for pCAL units");

-      return;

-   }

-

-   memcpy(info_ptr->pcal_units, units, length);

-

-   info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,

-       (png_size_t)((nparams + 1) * (sizeof (png_charp)))));

-

-   if (info_ptr->pcal_params == NULL)

-   {

-      png_warning(png_ptr, "Insufficient memory for pCAL params");

-      return;

-   }

-

-   memset(info_ptr->pcal_params, 0, (nparams + 1) * (sizeof (png_charp)));

-

-   for (i = 0; i < nparams; i++)

-   {

-      length = strlen(params[i]) + 1;

-      png_debug2(3, "allocating parameter %d for info (%lu bytes)", i,

-          (unsigned long)length);

-

-      info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length);

-

-      if (info_ptr->pcal_params[i] == NULL)

-      {

-         png_warning(png_ptr, "Insufficient memory for pCAL parameter");

-         return;

-      }

-

-      memcpy(info_ptr->pcal_params[i], params[i], length);

-   }

-

-   info_ptr->valid |= PNG_INFO_pCAL;

-   info_ptr->free_me |= PNG_FREE_PCAL;

-}

-#endif

-

-#ifdef PNG_sCAL_SUPPORTED

-void PNGAPI

-png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,

-    int unit, png_const_charp swidth, png_const_charp sheight)

-{

-   png_size_t lengthw = 0, lengthh = 0;

-

-   png_debug1(1, "in %s storage function", "sCAL");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   /* Double check the unit (should never get here with an invalid

-    * unit unless this is an API call.)

-    */

-   if (unit != 1 && unit != 2)

-      png_error(png_ptr, "Invalid sCAL unit");

-

-   if (swidth == NULL || (lengthw = strlen(swidth)) == 0 ||

-       swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw))

-      png_error(png_ptr, "Invalid sCAL width");

-

-   if (sheight == NULL || (lengthh = strlen(sheight)) == 0 ||

-       sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh))

-      png_error(png_ptr, "Invalid sCAL height");

-

-   info_ptr->scal_unit = (png_byte)unit;

-

-   ++lengthw;

-

-   png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw);

-

-   info_ptr->scal_s_width = png_voidcast(png_charp,

-      png_malloc_warn(png_ptr, lengthw));

-

-   if (info_ptr->scal_s_width == NULL)

-   {

-      png_warning(png_ptr, "Memory allocation failed while processing sCAL");

-      return;

-   }

-

-   memcpy(info_ptr->scal_s_width, swidth, lengthw);

-

-   ++lengthh;

-

-   png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh);

-

-   info_ptr->scal_s_height = png_voidcast(png_charp,

-      png_malloc_warn(png_ptr, lengthh));

-

-   if (info_ptr->scal_s_height == NULL)

-   {

-      png_free (png_ptr, info_ptr->scal_s_width);

-      info_ptr->scal_s_width = NULL;

-

-      png_warning(png_ptr, "Memory allocation failed while processing sCAL");

-      return;

-   }

-

-   memcpy(info_ptr->scal_s_height, sheight, lengthh);

-

-   info_ptr->valid |= PNG_INFO_sCAL;

-   info_ptr->free_me |= PNG_FREE_SCAL;

-}

-

-#  ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit,

-    double width, double height)

-{

-   png_debug1(1, "in %s storage function", "sCAL");

-

-   /* Check the arguments. */

-   if (width <= 0)

-      png_warning(png_ptr, "Invalid sCAL width ignored");

-

-   else if (height <= 0)

-      png_warning(png_ptr, "Invalid sCAL height ignored");

-

-   else

-   {

-      /* Convert 'width' and 'height' to ASCII. */

-      char swidth[PNG_sCAL_MAX_DIGITS+1];

-      char sheight[PNG_sCAL_MAX_DIGITS+1];

-

-      png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width,

-         PNG_sCAL_PRECISION);

-      png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height,

-         PNG_sCAL_PRECISION);

-

-      png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);

-   }

-}

-#  endif

-

-#  ifdef PNG_FIXED_POINT_SUPPORTED

-void PNGAPI

-png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit,

-    png_fixed_point width, png_fixed_point height)

-{

-   png_debug1(1, "in %s storage function", "sCAL");

-

-   /* Check the arguments. */

-   if (width <= 0)

-      png_warning(png_ptr, "Invalid sCAL width ignored");

-

-   else if (height <= 0)

-      png_warning(png_ptr, "Invalid sCAL height ignored");

-

-   else

-   {

-      /* Convert 'width' and 'height' to ASCII. */

-      char swidth[PNG_sCAL_MAX_DIGITS+1];

-      char sheight[PNG_sCAL_MAX_DIGITS+1];

-

-      png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width);

-      png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height);

-

-      png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);

-   }

-}

-#  endif

-#endif

-

-#ifdef PNG_pHYs_SUPPORTED

-void PNGAPI

-png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_uint_32 res_x, png_uint_32 res_y, int unit_type)

-{

-   png_debug1(1, "in %s storage function", "pHYs");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   info_ptr->x_pixels_per_unit = res_x;

-   info_ptr->y_pixels_per_unit = res_y;

-   info_ptr->phys_unit_type = (png_byte)unit_type;

-   info_ptr->valid |= PNG_INFO_pHYs;

-}

-#endif

-

-void PNGAPI

-png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,

-    png_const_colorp palette, int num_palette)

-{

-

-   png_debug1(1, "in %s storage function", "PLTE");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH)

-   {

-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-         png_error(png_ptr, "Invalid palette length");

-

-      else

-      {

-         png_warning(png_ptr, "Invalid palette length");

-         return;

-      }

-   }

-

-   if ((num_palette > 0 && palette == NULL) ||

-      (num_palette == 0

-#        ifdef PNG_MNG_FEATURES_SUPPORTED

-            && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0

-#        endif

-      ))

-   {

-      png_chunk_report(png_ptr, "Invalid palette", PNG_CHUNK_ERROR);

-      return;

-   }

-

-   /* It may not actually be necessary to set png_ptr->palette here;

-    * we do it for backward compatibility with the way the png_handle_tRNS

-    * function used to do the allocation.

-    *

-    * 1.6.0: the above statement appears to be incorrect; something has to set

-    * the palette inside png_struct on read.

-    */

-   png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);

-

-   /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead

-    * of num_palette entries, in case of an invalid PNG file that has

-    * too-large sample values.

-    */

-   png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,

-       PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));

-

-   if (num_palette > 0)

-      memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color)));

-   info_ptr->palette = png_ptr->palette;

-   info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;

-

-   info_ptr->free_me |= PNG_FREE_PLTE;

-

-   info_ptr->valid |= PNG_INFO_PLTE;

-}

-

-#ifdef PNG_sBIT_SUPPORTED

-void PNGAPI

-png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_color_8p sig_bit)

-{

-   png_debug1(1, "in %s storage function", "sBIT");

-

-   if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL)

-      return;

-

-   info_ptr->sig_bit = *sig_bit;

-   info_ptr->valid |= PNG_INFO_sBIT;

-}

-#endif

-

-#ifdef PNG_sRGB_SUPPORTED

-void PNGAPI

-png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent)

-{

-   png_debug1(1, "in %s storage function", "sRGB");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent);

-   png_colorspace_sync_info(png_ptr, info_ptr);

-}

-

-void PNGAPI

-png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,

-    int srgb_intent)

-{

-   png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent))

-   {

-      /* This causes the gAMA and cHRM to be written too */

-      info_ptr->colorspace.flags |=

-         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;

-   }

-

-   png_colorspace_sync_info(png_ptr, info_ptr);

-}

-#endif /* sRGB */

-

-

-#ifdef PNG_iCCP_SUPPORTED

-void PNGAPI

-png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_charp name, int compression_type,

-    png_const_bytep profile, png_uint_32 proflen)

-{

-   png_charp new_iccp_name;

-   png_bytep new_iccp_profile;

-   png_size_t length;

-

-   png_debug1(1, "in %s storage function", "iCCP");

-

-   if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL)

-      return;

-

-   if (compression_type != PNG_COMPRESSION_TYPE_BASE)

-      png_app_error(png_ptr, "Invalid iCCP compression method");

-

-   /* Set the colorspace first because this validates the profile; do not

-    * override previously set app cHRM or gAMA here (because likely as not the

-    * application knows better than libpng what the correct values are.)  Pass

-    * the info_ptr color_type field to png_colorspace_set_ICC because in the

-    * write case it has not yet been stored in png_ptr.

-    */

-   {

-      int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name,

-         proflen, profile, info_ptr->color_type);

-

-      png_colorspace_sync_info(png_ptr, info_ptr);

-

-      /* Don't do any of the copying if the profile was bad, or inconsistent. */

-      if (!result)

-         return;

-

-      /* But do write the gAMA and cHRM chunks from the profile. */

-      info_ptr->colorspace.flags |=

-         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;

-   }

-

-   length = strlen(name)+1;

-   new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length));

-

-   if (new_iccp_name == NULL)

-   {

-      png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk");

-      return;

-   }

-

-   memcpy(new_iccp_name, name, length);

-   new_iccp_profile = png_voidcast(png_bytep,

-      png_malloc_warn(png_ptr, proflen));

-

-   if (new_iccp_profile == NULL)

-   {

-      png_free(png_ptr, new_iccp_name);

-      png_benign_error(png_ptr,

-          "Insufficient memory to process iCCP profile");

-      return;

-   }

-

-   memcpy(new_iccp_profile, profile, proflen);

-

-   png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);

-

-   info_ptr->iccp_proflen = proflen;

-   info_ptr->iccp_name = new_iccp_name;

-   info_ptr->iccp_profile = new_iccp_profile;

-   info_ptr->free_me |= PNG_FREE_ICCP;

-   info_ptr->valid |= PNG_INFO_iCCP;

-}

-#endif

-

-#ifdef PNG_TEXT_SUPPORTED

-void PNGAPI

-png_set_text(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_textp text_ptr, int num_text)

-{

-   int ret;

-   ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text);

-

-   if (ret)

-      png_error(png_ptr, "Insufficient memory to store text");

-}

-

-int /* PRIVATE */

-png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_textp text_ptr, int num_text)

-{

-   int i;

-

-   png_debug1(1, "in %lx storage function", png_ptr == NULL ? "unexpected" :

-      (unsigned long)png_ptr->chunk_name);

-

-   if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL)

-      return(0);

-

-   /* Make sure we have enough space in the "text" array in info_struct

-    * to hold all of the incoming text_ptr objects.  This compare can't overflow

-    * because max_text >= num_text (anyway, subtract of two positive integers

-    * can't overflow in any case.)

-    */

-   if (num_text > info_ptr->max_text - info_ptr->num_text)

-   {

-      int old_num_text = info_ptr->num_text;

-      int max_text;

-      png_textp new_text = NULL;

-

-      /* Calculate an appropriate max_text, checking for overflow. */

-      max_text = old_num_text;

-      if (num_text <= INT_MAX - max_text)

-      {

-         max_text += num_text;

-

-         /* Round up to a multiple of 8 */

-         if (max_text < INT_MAX-8)

-            max_text = (max_text + 8) & ~0x7;

-

-         else

-            max_text = INT_MAX;

-

-         /* Now allocate a new array and copy the old members in, this does all

-          * the overflow checks.

-          */

-         new_text = png_voidcast(png_textp,png_realloc_array(png_ptr,

-            info_ptr->text, old_num_text, max_text-old_num_text,

-            sizeof *new_text));

-      }

-

-      if (new_text == NULL)

-      {

-         png_chunk_report(png_ptr, "too many text chunks",

-            PNG_CHUNK_WRITE_ERROR);

-         return 1;

-      }

-

-      png_free(png_ptr, info_ptr->text);

-

-      info_ptr->text = new_text;

-      info_ptr->free_me |= PNG_FREE_TEXT;

-      info_ptr->max_text = max_text;

-      /* num_text is adjusted below as the entries are copied in */

-

-      png_debug1(3, "allocated %d entries for info_ptr->text", max_text);

-   }

-

-   for (i = 0; i < num_text; i++)

-   {

-      size_t text_length, key_len;

-      size_t lang_len, lang_key_len;

-      png_textp textp = &(info_ptr->text[info_ptr->num_text]);

-

-      if (text_ptr[i].key == NULL)

-          continue;

-

-      if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE ||

-          text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST)

-      {

-         png_chunk_report(png_ptr, "text compression mode is out of range",

-            PNG_CHUNK_WRITE_ERROR);

-         continue;

-      }

-

-      key_len = strlen(text_ptr[i].key);

-

-      if (text_ptr[i].compression <= 0)

-      {

-         lang_len = 0;

-         lang_key_len = 0;

-      }

-

-      else

-#  ifdef PNG_iTXt_SUPPORTED

-      {

-         /* Set iTXt data */

-

-         if (text_ptr[i].lang != NULL)

-            lang_len = strlen(text_ptr[i].lang);

-

-         else

-            lang_len = 0;

-

-         if (text_ptr[i].lang_key != NULL)

-            lang_key_len = strlen(text_ptr[i].lang_key);

-

-         else

-            lang_key_len = 0;

-      }

-#  else /* PNG_iTXt_SUPPORTED */

-      {

-         png_chunk_report(png_ptr, "iTXt chunk not supported",

-            PNG_CHUNK_WRITE_ERROR);

-         continue;

-      }

-#  endif

-

-      if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')

-      {

-         text_length = 0;

-#  ifdef PNG_iTXt_SUPPORTED

-         if (text_ptr[i].compression > 0)

-            textp->compression = PNG_ITXT_COMPRESSION_NONE;

-

-         else

-#  endif

-            textp->compression = PNG_TEXT_COMPRESSION_NONE;

-      }

-

-      else

-      {

-         text_length = strlen(text_ptr[i].text);

-         textp->compression = text_ptr[i].compression;

-      }

-

-      textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr,

-          key_len + text_length + lang_len + lang_key_len + 4));

-

-      if (textp->key == NULL)

-      {

-         png_chunk_report(png_ptr, "text chunk: out of memory",

-               PNG_CHUNK_WRITE_ERROR);

-         return 1;

-      }

-

-      png_debug2(2, "Allocated %lu bytes at %p in png_set_text",

-          (unsigned long)(png_uint_32)

-          (key_len + lang_len + lang_key_len + text_length + 4),

-          textp->key);

-

-      memcpy(textp->key, text_ptr[i].key, key_len);

-      *(textp->key + key_len) = '\0';

-

-      if (text_ptr[i].compression > 0)

-      {

-         textp->lang = textp->key + key_len + 1;

-         memcpy(textp->lang, text_ptr[i].lang, lang_len);

-         *(textp->lang + lang_len) = '\0';

-         textp->lang_key = textp->lang + lang_len + 1;

-         memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len);

-         *(textp->lang_key + lang_key_len) = '\0';

-         textp->text = textp->lang_key + lang_key_len + 1;

-      }

-

-      else

-      {

-         textp->lang=NULL;

-         textp->lang_key=NULL;

-         textp->text = textp->key + key_len + 1;

-      }

-

-      if (text_length)

-         memcpy(textp->text, text_ptr[i].text, text_length);

-

-      *(textp->text + text_length) = '\0';

-

-#  ifdef PNG_iTXt_SUPPORTED

-      if (textp->compression > 0)

-      {

-         textp->text_length = 0;

-         textp->itxt_length = text_length;

-      }

-

-      else

-#  endif

-      {

-         textp->text_length = text_length;

-         textp->itxt_length = 0;

-      }

-

-      info_ptr->num_text++;

-      png_debug1(3, "transferred text chunk %d", info_ptr->num_text);

-   }

-

-   return(0);

-}

-#endif

-

-#ifdef PNG_tIME_SUPPORTED

-void PNGAPI

-png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_const_timep mod_time)

-{

-   png_debug1(1, "in %s storage function", "tIME");

-

-   if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL ||

-       (png_ptr->mode & PNG_WROTE_tIME))

-      return;

-

-   if (mod_time->month == 0   || mod_time->month > 12  ||

-       mod_time->day   == 0   || mod_time->day   > 31  ||

-       mod_time->hour  > 23   || mod_time->minute > 59 ||

-       mod_time->second > 60)

-   {

-      png_warning(png_ptr, "Ignoring invalid time value");

-      return;

-   }

-

-   info_ptr->mod_time = *mod_time;

-   info_ptr->valid |= PNG_INFO_tIME;

-}

-#endif

-

-#ifdef PNG_tRNS_SUPPORTED

-void PNGAPI

-png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,

-    png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color)

-{

-   png_debug1(1, "in %s storage function", "tRNS");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (trans_alpha != NULL)

-   {

-       /* It may not actually be necessary to set png_ptr->trans_alpha here;

-        * we do it for backward compatibility with the way the png_handle_tRNS

-        * function used to do the allocation.

-        *

-        * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively

-        * relies on png_set_tRNS storing the information in png_struct

-        * (otherwise it won't be there for the code in pngrtran.c).

-        */

-

-       png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);

-

-       /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */

-       png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep,

-         png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));

-

-       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)

-          memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans);

-   }

-

-   if (trans_color != NULL)

-   {

-      int sample_max = (1 << info_ptr->bit_depth);

-

-      if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&

-          trans_color->gray > sample_max) ||

-          (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&

-          (trans_color->red > sample_max ||

-          trans_color->green > sample_max ||

-          trans_color->blue > sample_max)))

-         png_warning(png_ptr,

-            "tRNS chunk has out-of-range samples for bit_depth");

-

-      info_ptr->trans_color = *trans_color;

-

-      if (num_trans == 0)

-         num_trans = 1;

-   }

-

-   info_ptr->num_trans = (png_uint_16)num_trans;

-

-   if (num_trans != 0)

-   {

-      info_ptr->valid |= PNG_INFO_tRNS;

-      info_ptr->free_me |= PNG_FREE_TRNS;

-   }

-}

-#endif

-

-#ifdef PNG_sPLT_SUPPORTED

-void PNGAPI

-png_set_sPLT(png_const_structrp png_ptr,

-    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)

-/*

- *  entries        - array of png_sPLT_t structures

- *                   to be added to the list of palettes

- *                   in the info structure.

- *

- *  nentries       - number of palette structures to be

- *                   added.

- */

-{

-   png_sPLT_tp np;

-

-   if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL)

-      return;

-

-   /* Use the internal realloc function, which checks for all the possible

-    * overflows.  Notice that the parameters are (int) and (size_t)

-    */

-   np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr,

-      info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,

-      sizeof *np));

-

-   if (np == NULL)

-   {

-      /* Out of memory or too many chunks */

-      png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR);

-      return;

-   }

-

-   png_free(png_ptr, info_ptr->splt_palettes);

-   info_ptr->splt_palettes = np;

-   info_ptr->free_me |= PNG_FREE_SPLT;

-

-   np += info_ptr->splt_palettes_num;

-

-   do

-   {

-      png_size_t length;

-

-      /* Skip invalid input entries */

-      if (entries->name == NULL || entries->entries == NULL)

-      {

-         /* png_handle_sPLT doesn't do this, so this is an app error */

-         png_app_error(png_ptr, "png_set_sPLT: invalid sPLT");

-         /* Just skip the invalid entry */

-         continue;

-      }

-

-      np->depth = entries->depth;

-

-      /* In the even of out-of-memory just return - there's no point keeping on

-       * trying to add sPLT chunks.

-       */

-      length = strlen(entries->name) + 1;

-      np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length));

-

-      if (np->name == NULL)

-         break;

-

-      memcpy(np->name, entries->name, length);

-

-      /* IMPORTANT: we have memory now that won't get freed if something else

-       * goes wrong, this code must free it.  png_malloc_array produces no

-       * warnings, use a png_chunk_report (below) if there is an error.

-       */

-      np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr,

-          entries->nentries, sizeof (png_sPLT_entry)));

-

-      if (np->entries == NULL)

-      {

-         png_free(png_ptr, np->name);

-         break;

-      }

-

-      np->nentries = entries->nentries;

-      /* This multiply can't overflow because png_malloc_array has already

-       * checked it when doing the allocation.

-       */

-      memcpy(np->entries, entries->entries,

-         entries->nentries * sizeof (png_sPLT_entry));

-

-      /* Note that 'continue' skips the advance of the out pointer and out

-       * count, so an invalid entry is not added.

-       */

-      info_ptr->valid |= PNG_INFO_sPLT;

-      ++(info_ptr->splt_palettes_num);

-      ++np;

-   }

-   while (++entries, --nentries);

-

-   if (nentries > 0)

-      png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);

-}

-#endif /* PNG_sPLT_SUPPORTED */

-

-#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED

-static png_byte

-check_location(png_const_structrp png_ptr, int location)

-{

-   location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT);

-

-   /* New in 1.6.0; copy the location and check it.  This is an API

-    * change, previously the app had to use the

-    * png_set_unknown_chunk_location API below for each chunk.

-    */

-   if (location == 0 && !(png_ptr->mode & PNG_IS_READ_STRUCT))

-   {

-      /* Write struct, so unknown chunks come from the app */

-      png_app_warning(png_ptr,

-         "png_set_unknown_chunks now expects a valid location");

-      /* Use the old behavior */

-      location = (png_byte)(png_ptr->mode &

-         (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));

-   }

-

-   /* This need not be an internal error - if the app calls

-    * png_set_unknown_chunks on a read pointer it must get the location right.

-    */

-   if (location == 0)

-      png_error(png_ptr, "invalid location in png_set_unknown_chunks");

-

-   /* Now reduce the location to the top-most set bit by removing each least

-    * significant bit in turn.

-    */

-   while (location != (location & -location))

-      location &= ~(location & -location);

-

-   /* The cast is safe because 'location' is a bit mask and only the low four

-    * bits are significant.

-    */

-   return (png_byte)location;

-}

-

-void PNGAPI

-png_set_unknown_chunks(png_const_structrp png_ptr,

-   png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)

-{

-   png_unknown_chunkp np;

-

-   if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||

-      unknowns == NULL)

-      return;

-

-   /* Check for the failure cases where support has been disabled at compile

-    * time.  This code is hardly ever compiled - it's here because

-    * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this

-    * code) but may be meaningless if the read or write handling of unknown

-    * chunks is not compiled in.

-    */

-#  if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \

-      defined(PNG_READ_SUPPORTED)

-      if (png_ptr->mode & PNG_IS_READ_STRUCT)

-      {

-         png_app_error(png_ptr, "no unknown chunk support on read");

-         return;

-      }

-#  endif

-#  if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \

-      defined(PNG_WRITE_SUPPORTED)

-      if (!(png_ptr->mode & PNG_IS_READ_STRUCT))

-      {

-         png_app_error(png_ptr, "no unknown chunk support on write");

-         return;

-      }

-#  endif

-

-   /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that

-    * unknown critical chunks could be lost with just a warning resulting in

-    * undefined behavior.  Now png_chunk_report is used to provide behavior

-    * appropriate to read or write.

-    */

-   np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr,

-         info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns,

-         sizeof *np));

-

-   if (np == NULL)

-   {

-      png_chunk_report(png_ptr, "too many unknown chunks",

-         PNG_CHUNK_WRITE_ERROR);

-      return;

-   }

-

-   png_free(png_ptr, info_ptr->unknown_chunks);

-   info_ptr->unknown_chunks = np; /* safe because it is initialized */

-   info_ptr->free_me |= PNG_FREE_UNKN;

-

-   np += info_ptr->unknown_chunks_num;

-

-   /* Increment unknown_chunks_num each time round the loop to protect the

-    * just-allocated chunk data.

-    */

-   for (; num_unknowns > 0; --num_unknowns, ++unknowns)

-   {

-      memcpy(np->name, unknowns->name, (sizeof np->name));

-      np->name[(sizeof np->name)-1] = '\0';

-      np->location = check_location(png_ptr, unknowns->location);

-

-      if (unknowns->size == 0)

-      {

-         np->data = NULL;

-         np->size = 0;

-      }

-

-      else

-      {

-         np->data = png_voidcast(png_bytep,

-            png_malloc_base(png_ptr, unknowns->size));

-

-         if (np->data == NULL)

-         {

-            png_chunk_report(png_ptr, "unknown chunk: out of memory",

-               PNG_CHUNK_WRITE_ERROR);

-            /* But just skip storing the unknown chunk */

-            continue;

-         }

-

-         memcpy(np->data, unknowns->data, unknowns->size);

-         np->size = unknowns->size;

-      }

-

-      /* These increments are skipped on out-of-memory for the data - the

-       * unknown chunk entry gets overwritten if the png_chunk_report returns.

-       * This is correct in the read case (the chunk is just dropped.)

-       */

-      ++np;

-      ++(info_ptr->unknown_chunks_num);

-   }

-}

-

-void PNGAPI

-png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr,

-    int chunk, int location)

-{

-   /* This API is pretty pointless in 1.6.0 because the location can be set

-    * before the call to png_set_unknown_chunks.

-    *

-    * TODO: add a png_app_warning in 1.7

-    */

-   if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 &&

-      chunk < info_ptr->unknown_chunks_num)

-   {

-      if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0)

-      {

-         png_app_error(png_ptr, "invalid unknown chunk location");

-         /* Fake out the pre 1.6.0 behavior: */

-         if ((location & PNG_HAVE_IDAT)) /* undocumented! */

-            location = PNG_AFTER_IDAT;

-

-         else

-            location = PNG_HAVE_IHDR; /* also undocumented */

-      }

-

-      info_ptr->unknown_chunks[chunk].location =

-         check_location(png_ptr, location);

-   }

-}

-#endif

-

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-png_uint_32 PNGAPI

-png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features)

-{

-   png_debug(1, "in png_permit_mng_features");

-

-   if (png_ptr == NULL)

-      return 0;

-

-   png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES;

-

-   return png_ptr->mng_features_permitted;

-}

-#endif

-

-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED

-static unsigned int

-add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep)

-{

-   unsigned int i;

-

-   /* Utility function: update the 'keep' state of a chunk if it is already in

-    * the list, otherwise add it to the list.

-    */

-   for (i=0; i<count; ++i, list += 5) if (memcmp(list, add, 4) == 0)

-   {

-      list[4] = (png_byte)keep;

-      return count;

-   }

-

-   if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)

-   {

-      ++count;

-      memcpy(list, add, 4);

-      list[4] = (png_byte)keep;

-   }

-

-   return count;

-}

-

-void PNGAPI

-png_set_keep_unknown_chunks(png_structrp png_ptr, int keep,

-    png_const_bytep chunk_list, int num_chunks_in)

-{

-   png_bytep new_list;

-   unsigned int num_chunks, old_num_chunks;

-

-   if (png_ptr == NULL)

-      return;

-

-   if (keep < 0 || keep >= PNG_HANDLE_CHUNK_LAST)

-   {

-      png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep");

-      return;

-   }

-

-   if (num_chunks_in <= 0)

-   {

-      png_ptr->unknown_default = keep;

-

-      /* '0' means just set the flags, so stop here */

-      if (num_chunks_in == 0)

-        return;

-   }

-

-   if (num_chunks_in < 0)

-   {

-      /* Ignore all unknown chunks and all chunks recognized by

-       * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND

-       */

-      static PNG_CONST png_byte chunks_to_ignore[] = {

-         98,  75,  71,  68, '\0',  /* bKGD */

-         99,  72,  82,  77, '\0',  /* cHRM */

-        103,  65,  77,  65, '\0',  /* gAMA */

-        104,  73,  83,  84, '\0',  /* hIST */

-        105,  67,  67,  80, '\0',  /* iCCP */

-        105,  84,  88, 116, '\0',  /* iTXt */

-        111,  70,  70, 115, '\0',  /* oFFs */

-        112,  67,  65,  76, '\0',  /* pCAL */

-        112,  72,  89, 115, '\0',  /* pHYs */

-        115,  66,  73,  84, '\0',  /* sBIT */

-        115,  67,  65,  76, '\0',  /* sCAL */

-        115,  80,  76,  84, '\0',  /* sPLT */

-        115,  84,  69,  82, '\0',  /* sTER */

-        115,  82,  71,  66, '\0',  /* sRGB */

-        116,  69,  88, 116, '\0',  /* tEXt */

-        116,  73,  77,  69, '\0',  /* tIME */

-        122,  84,  88, 116, '\0'   /* zTXt */

-      };

-

-      chunk_list = chunks_to_ignore;

-      num_chunks = (sizeof chunks_to_ignore)/5;

-   }

-

-   else /* num_chunks_in > 0 */

-   {

-      if (chunk_list == NULL)

-      {

-         /* Prior to 1.6.0 this was silently ignored, now it is an app_error

-          * which can be switched off.

-          */

-         png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list");

-         return;

-      }

-

-      num_chunks = num_chunks_in;

-   }

-

-   old_num_chunks = png_ptr->num_chunk_list;

-   if (png_ptr->chunk_list == NULL)

-      old_num_chunks = 0;

-

-   /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow.

-    */

-   if (num_chunks + old_num_chunks > UINT_MAX/5)

-   {

-      png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks");

-      return;

-   }

-

-   /* If these chunks are being reset to the default then no more memory is

-    * required because add_one_chunk above doesn't extend the list if the 'keep'

-    * parameter is the default.

-    */

-   if (keep)

-   {

-      new_list = png_voidcast(png_bytep, png_malloc(png_ptr,

-          5 * (num_chunks + old_num_chunks)));

-

-      if (old_num_chunks > 0)

-         memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks);

-   }

-

-   else if (old_num_chunks > 0)

-      new_list = png_ptr->chunk_list;

-

-   else

-      new_list = NULL;

-

-   /* Add the new chunks together with each one's handling code.  If the chunk

-    * already exists the code is updated, otherwise the chunk is added to the

-    * end.  (In libpng 1.6.0 order no longer matters because this code enforces

-    * the earlier convention that the last setting is the one that is used.)

-    */

-   if (new_list != NULL)

-   {

-      png_const_bytep inlist;

-      png_bytep outlist;

-      unsigned int i;

-

-      for (i=0; i<num_chunks; ++i)

-         old_num_chunks = add_one_chunk(new_list, old_num_chunks,

-            chunk_list+5*i, keep);

-

-      /* Now remove any spurious 'default' entries. */

-      num_chunks = 0;

-      for (i=0, inlist=outlist=new_list; i<old_num_chunks; ++i, inlist += 5)

-         if (inlist[4])

-         {

-            if (outlist != inlist)

-               memcpy(outlist, inlist, 5);

-            outlist += 5;

-            ++num_chunks;

-         }

-

-      /* This means the application has removed all the specialized handling. */

-      if (num_chunks == 0)

-      {

-         if (png_ptr->chunk_list != new_list)

-            png_free(png_ptr, new_list);

-

-         new_list = NULL;

-      }

-   }

-

-   else

-      num_chunks = 0;

-

-   png_ptr->num_chunk_list = num_chunks;

-

-   if (png_ptr->chunk_list != new_list)

-   {

-      if (png_ptr->chunk_list != NULL)

-         png_free(png_ptr, png_ptr->chunk_list);

-

-      png_ptr->chunk_list = new_list;

-   }

-}

-#endif

-

-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED

-void PNGAPI

-png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr,

-    png_user_chunk_ptr read_user_chunk_fn)

-{

-   png_debug(1, "in png_set_read_user_chunk_fn");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->read_user_chunk_fn = read_user_chunk_fn;

-   png_ptr->user_chunk_ptr = user_chunk_ptr;

-}

-#endif

-

-#ifdef PNG_INFO_IMAGE_SUPPORTED

-void PNGAPI

-png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr,

-    png_bytepp row_pointers)

-{

-   png_debug1(1, "in %s storage function", "rows");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers))

-      png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);

-

-   info_ptr->row_pointers = row_pointers;

-

-   if (row_pointers)

-      info_ptr->valid |= PNG_INFO_IDAT;

-}

-#endif

-

-void PNGAPI

-png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size)

-{

-    if (png_ptr == NULL)

-       return;

-

-    if (size == 0 || size > PNG_UINT_31_MAX)

-       png_error(png_ptr, "invalid compression buffer size");

-

-#  ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-      if (png_ptr->mode & PNG_IS_READ_STRUCT)

-      {

-         png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */

-         return;

-      }

-#  endif

-

-#  ifdef PNG_WRITE_SUPPORTED

-      if (!(png_ptr->mode & PNG_IS_READ_STRUCT))

-      {

-         if (png_ptr->zowner != 0)

-         {

-            png_warning(png_ptr,

-              "Compression buffer size cannot be changed because it is in use");

-            return;

-         }

-

-         if (size > ZLIB_IO_MAX)

-         {

-            png_warning(png_ptr,

-               "Compression buffer size limited to system maximum");

-            size = ZLIB_IO_MAX; /* must fit */

-         }

-

-         else if (size < 6)

-         {

-            /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH

-             * if this is permitted.

-             */

-            png_warning(png_ptr,

-               "Compression buffer size cannot be reduced below 6");

-            return;

-         }

-

-         if (png_ptr->zbuffer_size != size)

-         {

-            png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);

-            png_ptr->zbuffer_size = (uInt)size;

-         }

-      }

-#  endif

-}

-

-void PNGAPI

-png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask)

-{

-   if (png_ptr && info_ptr)

-      info_ptr->valid &= ~mask;

-}

-

-

-#ifdef PNG_SET_USER_LIMITS_SUPPORTED

-/* This function was added to libpng 1.2.6 */

-void PNGAPI

-png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max,

-    png_uint_32 user_height_max)

-{

-   /* Images with dimensions larger than these limits will be

-    * rejected by png_set_IHDR().  To accept any PNG datastream

-    * regardless of dimensions, set both limits to 0x7ffffffL.

-    */

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->user_width_max = user_width_max;

-   png_ptr->user_height_max = user_height_max;

-}

-

-/* This function was added to libpng 1.4.0 */

-void PNGAPI

-png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max)

-{

-    if (png_ptr)

-       png_ptr->user_chunk_cache_max = user_chunk_cache_max;

-}

-

-/* This function was added to libpng 1.4.1 */

-void PNGAPI

-png_set_chunk_malloc_max (png_structrp png_ptr,

-    png_alloc_size_t user_chunk_malloc_max)

-{

-   if (png_ptr)

-      png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;

-}

-#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */

-

-

-#ifdef PNG_BENIGN_ERRORS_SUPPORTED

-void PNGAPI

-png_set_benign_errors(png_structrp png_ptr, int allowed)

-{

-   png_debug(1, "in png_set_benign_errors");

-

-   /* If allowed is 1, png_benign_error() is treated as a warning.

-    *

-    * If allowed is 0, png_benign_error() is treated as an error (which

-    * is the default behavior if png_set_benign_errors() is not called).

-    */

-

-   if (allowed)

-      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN |

-         PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN;

-

-   else

-      png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN |

-         PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN);

-}

-#endif /* PNG_BENIGN_ERRORS_SUPPORTED */

-

-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED

-   /* Whether to report invalid palette index; added at libng-1.5.10.

-    * It is possible for an indexed (color-type==3) PNG file to contain

-    * pixels with invalid (out-of-range) indexes if the PLTE chunk has

-    * fewer entries than the image's bit-depth would allow. We recover

-    * from this gracefully by filling any incomplete palette with zeroes

-    * (opaque black).  By default, when this occurs libpng will issue

-    * a benign error.  This API can be used to override that behavior.

-    */

-void PNGAPI

-png_set_check_for_invalid_index(png_structrp png_ptr, int allowed)

-{

-   png_debug(1, "in png_set_check_for_invalid_index");

-

-   if (allowed > 0)

-      png_ptr->num_palette_max = 0;

-

-   else

-      png_ptr->num_palette_max = -1;

-}

-#endif

-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */

+
+/* pngset.c - storage of image information into info struct
+ *
+ * Last changed in libpng 1.6.19 [November 12, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * The functions here are used during reads to store data from the file
+ * into the info struct, and during writes to store application data
+ * into the info struct for writing into the file.  This abstracts the
+ * info struct and allows us to change the structure in the future.
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+#ifdef PNG_bKGD_SUPPORTED
+void PNGAPI
+png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_color_16p background)
+{
+   png_debug1(1, "in %s storage function", "bKGD");
+
+   if (png_ptr == NULL || info_ptr == NULL || background == NULL)
+      return;
+
+   info_ptr->background = *background;
+   info_ptr->valid |= PNG_INFO_bKGD;
+}
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+void PNGFAPI
+png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,
+    png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
+    png_fixed_point blue_x, png_fixed_point blue_y)
+{
+   png_xy xy;
+
+   png_debug1(1, "in %s storage function", "cHRM fixed");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   xy.redx = red_x;
+   xy.redy = red_y;
+   xy.greenx = green_x;
+   xy.greeny = green_y;
+   xy.bluex = blue_x;
+   xy.bluey = blue_y;
+   xy.whitex = white_x;
+   xy.whitey = white_y;
+
+   if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy,
+       2/* override with app values*/) != 0)
+      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
+
+   png_colorspace_sync_info(png_ptr, info_ptr);
+}
+
+void PNGFAPI
+png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_fixed_point int_red_X, png_fixed_point int_red_Y,
+    png_fixed_point int_red_Z, png_fixed_point int_green_X,
+    png_fixed_point int_green_Y, png_fixed_point int_green_Z,
+    png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
+    png_fixed_point int_blue_Z)
+{
+   png_XYZ XYZ;
+
+   png_debug1(1, "in %s storage function", "cHRM XYZ fixed");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   XYZ.red_X = int_red_X;
+   XYZ.red_Y = int_red_Y;
+   XYZ.red_Z = int_red_Z;
+   XYZ.green_X = int_green_X;
+   XYZ.green_Y = int_green_Y;
+   XYZ.green_Z = int_green_Z;
+   XYZ.blue_X = int_blue_X;
+   XYZ.blue_Y = int_blue_Y;
+   XYZ.blue_Z = int_blue_Z;
+
+   if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace,
+       &XYZ, 2) != 0)
+      info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM;
+
+   png_colorspace_sync_info(png_ptr, info_ptr);
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
+    double white_x, double white_y, double red_x, double red_y,
+    double green_x, double green_y, double blue_x, double blue_y)
+{
+   png_set_cHRM_fixed(png_ptr, info_ptr,
+      png_fixed(png_ptr, white_x, "cHRM White X"),
+      png_fixed(png_ptr, white_y, "cHRM White Y"),
+      png_fixed(png_ptr, red_x, "cHRM Red X"),
+      png_fixed(png_ptr, red_y, "cHRM Red Y"),
+      png_fixed(png_ptr, green_x, "cHRM Green X"),
+      png_fixed(png_ptr, green_y, "cHRM Green Y"),
+      png_fixed(png_ptr, blue_x, "cHRM Blue X"),
+      png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
+}
+
+void PNGAPI
+png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X,
+    double red_Y, double red_Z, double green_X, double green_Y, double green_Z,
+    double blue_X, double blue_Y, double blue_Z)
+{
+   png_set_cHRM_XYZ_fixed(png_ptr, info_ptr,
+      png_fixed(png_ptr, red_X, "cHRM Red X"),
+      png_fixed(png_ptr, red_Y, "cHRM Red Y"),
+      png_fixed(png_ptr, red_Z, "cHRM Red Z"),
+      png_fixed(png_ptr, green_X, "cHRM Green X"),
+      png_fixed(png_ptr, green_Y, "cHRM Green Y"),
+      png_fixed(png_ptr, green_Z, "cHRM Green Z"),
+      png_fixed(png_ptr, blue_X, "cHRM Blue X"),
+      png_fixed(png_ptr, blue_Y, "cHRM Blue Y"),
+      png_fixed(png_ptr, blue_Z, "cHRM Blue Z"));
+}
+#  endif /* FLOATING_POINT */
+
+#endif /* cHRM */
+
+#ifdef PNG_gAMA_SUPPORTED
+void PNGFAPI
+png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_fixed_point file_gamma)
+{
+   png_debug1(1, "in %s storage function", "gAMA");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma);
+   png_colorspace_sync_info(png_ptr, info_ptr);
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma)
+{
+   png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma,
+       "png_set_gAMA"));
+}
+#  endif
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+void PNGAPI
+png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_uint_16p hist)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function", "hIST");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (info_ptr->num_palette == 0 || info_ptr->num_palette
+       > PNG_MAX_PALETTE_LENGTH)
+   {
+      png_warning(png_ptr,
+          "Invalid palette size, hIST allocation skipped");
+
+      return;
+   }
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
+
+   /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
+    * version 1.2.1
+    */
+   info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr,
+       PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16))));
+
+   if (info_ptr->hist == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory for hIST chunk data");
+
+      return;
+   }
+
+   info_ptr->free_me |= PNG_FREE_HIST;
+
+   for (i = 0; i < info_ptr->num_palette; i++)
+      info_ptr->hist[i] = hist[i];
+
+   info_ptr->valid |= PNG_INFO_hIST;
+}
+#endif
+
+void PNGAPI
+png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 width, png_uint_32 height, int bit_depth,
+    int color_type, int interlace_type, int compression_type,
+    int filter_type)
+{
+   png_debug1(1, "in %s storage function", "IHDR");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->width = width;
+   info_ptr->height = height;
+   info_ptr->bit_depth = (png_byte)bit_depth;
+   info_ptr->color_type = (png_byte)color_type;
+   info_ptr->compression_type = (png_byte)compression_type;
+   info_ptr->filter_type = (png_byte)filter_type;
+   info_ptr->interlace_type = (png_byte)interlace_type;
+
+   png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height,
+       info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
+       info_ptr->compression_type, info_ptr->filter_type);
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+
+   else if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      info_ptr->channels = 3;
+
+   else
+      info_ptr->channels = 1;
+
+   if ((info_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      info_ptr->channels++;
+
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
+
+   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);
+}
+
+#ifdef PNG_oFFs_SUPPORTED
+void PNGAPI
+png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_int_32 offset_x, png_int_32 offset_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function", "oFFs");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_offset = offset_x;
+   info_ptr->y_offset = offset_y;
+   info_ptr->offset_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_oFFs;
+}
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+void PNGAPI
+png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type,
+    int nparams, png_const_charp units, png_charpp params)
+{
+   png_size_t length;
+   int i;
+
+   png_debug1(1, "in %s storage function", "pCAL");
+
+   if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL
+       || (nparams > 0 && params == NULL))
+      return;
+
+   length = strlen(purpose) + 1;
+   png_debug1(3, "allocating purpose for info (%lu bytes)",
+       (unsigned long)length);
+
+   /* TODO: validate format of calibration name and unit name */
+
+   /* Check that the type matches the specification. */
+   if (type < 0 || type > 3)
+      png_error(png_ptr, "Invalid pCAL equation type");
+
+   if (nparams < 0 || nparams > 255)
+      png_error(png_ptr, "Invalid pCAL parameter count");
+
+   /* Validate params[nparams] */
+   for (i=0; i<nparams; ++i)
+   {
+      if (params[i] == NULL ||
+          !png_check_fp_string(params[i], strlen(params[i])))
+         png_error(png_ptr, "Invalid format for pCAL parameter");
+   }
+
+   info_ptr->pcal_purpose = png_voidcast(png_charp,
+       png_malloc_warn(png_ptr, length));
+
+   if (info_ptr->pcal_purpose == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory for pCAL purpose");
+
+      return;
+   }
+
+   memcpy(info_ptr->pcal_purpose, purpose, length);
+
+   png_debug(3, "storing X0, X1, type, and nparams in info");
+   info_ptr->pcal_X0 = X0;
+   info_ptr->pcal_X1 = X1;
+   info_ptr->pcal_type = (png_byte)type;
+   info_ptr->pcal_nparams = (png_byte)nparams;
+
+   length = strlen(units) + 1;
+   png_debug1(3, "allocating units for info (%lu bytes)",
+     (unsigned long)length);
+
+   info_ptr->pcal_units = png_voidcast(png_charp,
+      png_malloc_warn(png_ptr, length));
+
+   if (info_ptr->pcal_units == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory for pCAL units");
+
+      return;
+   }
+
+   memcpy(info_ptr->pcal_units, units, length);
+
+   info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr,
+       (png_size_t)((nparams + 1) * (sizeof (png_charp)))));
+
+   if (info_ptr->pcal_params == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory for pCAL params");
+
+      return;
+   }
+
+   memset(info_ptr->pcal_params, 0, (nparams + 1) * (sizeof (png_charp)));
+
+   for (i = 0; i < nparams; i++)
+   {
+      length = strlen(params[i]) + 1;
+      png_debug2(3, "allocating parameter %d for info (%lu bytes)", i,
+          (unsigned long)length);
+
+      info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length);
+
+      if (info_ptr->pcal_params[i] == NULL)
+      {
+         png_warning(png_ptr, "Insufficient memory for pCAL parameter");
+
+         return;
+      }
+
+      memcpy(info_ptr->pcal_params[i], params[i], length);
+   }
+
+   info_ptr->valid |= PNG_INFO_pCAL;
+   info_ptr->free_me |= PNG_FREE_PCAL;
+}
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+void PNGAPI
+png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr,
+    int unit, png_const_charp swidth, png_const_charp sheight)
+{
+   png_size_t lengthw = 0, lengthh = 0;
+
+   png_debug1(1, "in %s storage function", "sCAL");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* Double check the unit (should never get here with an invalid
+    * unit unless this is an API call.)
+    */
+   if (unit != 1 && unit != 2)
+      png_error(png_ptr, "Invalid sCAL unit");
+
+   if (swidth == NULL || (lengthw = strlen(swidth)) == 0 ||
+       swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw))
+      png_error(png_ptr, "Invalid sCAL width");
+
+   if (sheight == NULL || (lengthh = strlen(sheight)) == 0 ||
+       sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh))
+      png_error(png_ptr, "Invalid sCAL height");
+
+   info_ptr->scal_unit = (png_byte)unit;
+
+   ++lengthw;
+
+   png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw);
+
+   info_ptr->scal_s_width = png_voidcast(png_charp,
+      png_malloc_warn(png_ptr, lengthw));
+
+   if (info_ptr->scal_s_width == NULL)
+   {
+      png_warning(png_ptr, "Memory allocation failed while processing sCAL");
+
+      return;
+   }
+
+   memcpy(info_ptr->scal_s_width, swidth, lengthw);
+
+   ++lengthh;
+
+   png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh);
+
+   info_ptr->scal_s_height = png_voidcast(png_charp,
+      png_malloc_warn(png_ptr, lengthh));
+
+   if (info_ptr->scal_s_height == NULL)
+   {
+      png_free (png_ptr, info_ptr->scal_s_width);
+      info_ptr->scal_s_width = NULL;
+
+      png_warning(png_ptr, "Memory allocation failed while processing sCAL");
+
+      return;
+   }
+
+   memcpy(info_ptr->scal_s_height, sheight, lengthh);
+
+   info_ptr->valid |= PNG_INFO_sCAL;
+   info_ptr->free_me |= PNG_FREE_SCAL;
+}
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit,
+    double width, double height)
+{
+   png_debug1(1, "in %s storage function", "sCAL");
+
+   /* Check the arguments. */
+   if (width <= 0)
+      png_warning(png_ptr, "Invalid sCAL width ignored");
+
+   else if (height <= 0)
+      png_warning(png_ptr, "Invalid sCAL height ignored");
+
+   else
+   {
+      /* Convert 'width' and 'height' to ASCII. */
+      char swidth[PNG_sCAL_MAX_DIGITS+1];
+      char sheight[PNG_sCAL_MAX_DIGITS+1];
+
+      png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width,
+         PNG_sCAL_PRECISION);
+      png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height,
+         PNG_sCAL_PRECISION);
+
+      png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
+   }
+}
+#  endif
+
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit,
+    png_fixed_point width, png_fixed_point height)
+{
+   png_debug1(1, "in %s storage function", "sCAL");
+
+   /* Check the arguments. */
+   if (width <= 0)
+      png_warning(png_ptr, "Invalid sCAL width ignored");
+
+   else if (height <= 0)
+      png_warning(png_ptr, "Invalid sCAL height ignored");
+
+   else
+   {
+      /* Convert 'width' and 'height' to ASCII. */
+      char swidth[PNG_sCAL_MAX_DIGITS+1];
+      char sheight[PNG_sCAL_MAX_DIGITS+1];
+
+      png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width);
+      png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height);
+
+      png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
+   }
+}
+#  endif
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+void PNGAPI
+png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 res_x, png_uint_32 res_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function", "pHYs");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_pixels_per_unit = res_x;
+   info_ptr->y_pixels_per_unit = res_y;
+   info_ptr->phys_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_pHYs;
+}
+#endif
+
+void PNGAPI
+png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
+    png_const_colorp palette, int num_palette)
+{
+
+   png_uint_32 max_palette_length;
+
+   png_debug1(1, "in %s storage function", "PLTE");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   max_palette_length = (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
+      (1 << info_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
+
+   if (num_palette < 0 || num_palette > (int) max_palette_length)
+   {
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         png_error(png_ptr, "Invalid palette length");
+
+      else
+      {
+         png_warning(png_ptr, "Invalid palette length");
+
+         return;
+      }
+   }
+
+   if ((num_palette > 0 && palette == NULL) ||
+      (num_palette == 0
+#        ifdef PNG_MNG_FEATURES_SUPPORTED
+            && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0
+#        endif
+      ))
+   {
+      png_error(png_ptr, "Invalid palette");
+   }
+
+   /* It may not actually be necessary to set png_ptr->palette here;
+    * we do it for backward compatibility with the way the png_handle_tRNS
+    * function used to do the allocation.
+    *
+    * 1.6.0: the above statement appears to be incorrect; something has to set
+    * the palette inside png_struct on read.
+    */
+   png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
+
+   /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
+    * of num_palette entries, in case of an invalid PNG file or incorrect
+    * call to png_set_PLTE() with too-large sample values.
+    */
+   png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
+       PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
+
+   if (num_palette > 0)
+      memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color)));
+   info_ptr->palette = png_ptr->palette;
+   info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
+
+   info_ptr->free_me |= PNG_FREE_PLTE;
+
+   info_ptr->valid |= PNG_INFO_PLTE;
+}
+
+#ifdef PNG_sBIT_SUPPORTED
+void PNGAPI
+png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_color_8p sig_bit)
+{
+   png_debug1(1, "in %s storage function", "sBIT");
+
+   if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL)
+      return;
+
+   info_ptr->sig_bit = *sig_bit;
+   info_ptr->valid |= PNG_INFO_sBIT;
+}
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+void PNGAPI
+png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent)
+{
+   png_debug1(1, "in %s storage function", "sRGB");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent);
+   png_colorspace_sync_info(png_ptr, info_ptr);
+}
+
+void PNGAPI
+png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr,
+    int srgb_intent)
+{
+   png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace,
+       srgb_intent) != 0)
+   {
+      /* This causes the gAMA and cHRM to be written too */
+      info_ptr->colorspace.flags |=
+         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
+   }
+
+   png_colorspace_sync_info(png_ptr, info_ptr);
+}
+#endif /* sRGB */
+
+
+#ifdef PNG_iCCP_SUPPORTED
+void PNGAPI
+png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_charp name, int compression_type,
+    png_const_bytep profile, png_uint_32 proflen)
+{
+   png_charp new_iccp_name;
+   png_bytep new_iccp_profile;
+   png_size_t length;
+
+   png_debug1(1, "in %s storage function", "iCCP");
+
+   if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL)
+      return;
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+      png_app_error(png_ptr, "Invalid iCCP compression method");
+
+   /* Set the colorspace first because this validates the profile; do not
+    * override previously set app cHRM or gAMA here (because likely as not the
+    * application knows better than libpng what the correct values are.)  Pass
+    * the info_ptr color_type field to png_colorspace_set_ICC because in the
+    * write case it has not yet been stored in png_ptr.
+    */
+   {
+      int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name,
+         proflen, profile, info_ptr->color_type);
+
+      png_colorspace_sync_info(png_ptr, info_ptr);
+
+      /* Don't do any of the copying if the profile was bad, or inconsistent. */
+      if (result == 0)
+         return;
+
+      /* But do write the gAMA and cHRM chunks from the profile. */
+      info_ptr->colorspace.flags |=
+         PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM;
+   }
+
+   length = strlen(name)+1;
+   new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length));
+
+   if (new_iccp_name == NULL)
+   {
+      png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk");
+
+      return;
+   }
+
+   memcpy(new_iccp_name, name, length);
+   new_iccp_profile = png_voidcast(png_bytep,
+      png_malloc_warn(png_ptr, proflen));
+
+   if (new_iccp_profile == NULL)
+   {
+      png_free(png_ptr, new_iccp_name);
+      png_benign_error(png_ptr,
+          "Insufficient memory to process iCCP profile");
+
+      return;
+   }
+
+   memcpy(new_iccp_profile, profile, proflen);
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);
+
+   info_ptr->iccp_proflen = proflen;
+   info_ptr->iccp_name = new_iccp_name;
+   info_ptr->iccp_profile = new_iccp_profile;
+   info_ptr->free_me |= PNG_FREE_ICCP;
+   info_ptr->valid |= PNG_INFO_iCCP;
+}
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+void PNGAPI
+png_set_text(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_textp text_ptr, int num_text)
+{
+   int ret;
+   ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text);
+
+   if (ret != 0)
+      png_error(png_ptr, "Insufficient memory to store text");
+}
+
+int /* PRIVATE */
+png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_textp text_ptr, int num_text)
+{
+   int i;
+
+   png_debug1(1, "in %lx storage function", png_ptr == NULL ? 0xabadca11U :
+      (unsigned long)png_ptr->chunk_name);
+
+   if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL)
+      return(0);
+
+   /* Make sure we have enough space in the "text" array in info_struct
+    * to hold all of the incoming text_ptr objects.  This compare can't overflow
+    * because max_text >= num_text (anyway, subtract of two positive integers
+    * can't overflow in any case.)
+    */
+   if (num_text > info_ptr->max_text - info_ptr->num_text)
+   {
+      int old_num_text = info_ptr->num_text;
+      int max_text;
+      png_textp new_text = NULL;
+
+      /* Calculate an appropriate max_text, checking for overflow. */
+      max_text = old_num_text;
+      if (num_text <= INT_MAX - max_text)
+      {
+         max_text += num_text;
+
+         /* Round up to a multiple of 8 */
+         if (max_text < INT_MAX-8)
+            max_text = (max_text + 8) & ~0x7;
+
+         else
+            max_text = INT_MAX;
+
+         /* Now allocate a new array and copy the old members in; this does all
+          * the overflow checks.
+          */
+         new_text = png_voidcast(png_textp,png_realloc_array(png_ptr,
+            info_ptr->text, old_num_text, max_text-old_num_text,
+            sizeof *new_text));
+      }
+
+      if (new_text == NULL)
+      {
+         png_chunk_report(png_ptr, "too many text chunks",
+            PNG_CHUNK_WRITE_ERROR);
+
+         return 1;
+      }
+
+      png_free(png_ptr, info_ptr->text);
+
+      info_ptr->text = new_text;
+      info_ptr->free_me |= PNG_FREE_TEXT;
+      info_ptr->max_text = max_text;
+      /* num_text is adjusted below as the entries are copied in */
+
+      png_debug1(3, "allocated %d entries for info_ptr->text", max_text);
+   }
+
+   for (i = 0; i < num_text; i++)
+   {
+      size_t text_length, key_len;
+      size_t lang_len, lang_key_len;
+      png_textp textp = &(info_ptr->text[info_ptr->num_text]);
+
+      if (text_ptr[i].key == NULL)
+          continue;
+
+      if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE ||
+          text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST)
+      {
+         png_chunk_report(png_ptr, "text compression mode is out of range",
+            PNG_CHUNK_WRITE_ERROR);
+         continue;
+      }
+
+      key_len = strlen(text_ptr[i].key);
+
+      if (text_ptr[i].compression <= 0)
+      {
+         lang_len = 0;
+         lang_key_len = 0;
+      }
+
+      else
+#  ifdef PNG_iTXt_SUPPORTED
+      {
+         /* Set iTXt data */
+
+         if (text_ptr[i].lang != NULL)
+            lang_len = strlen(text_ptr[i].lang);
+
+         else
+            lang_len = 0;
+
+         if (text_ptr[i].lang_key != NULL)
+            lang_key_len = strlen(text_ptr[i].lang_key);
+
+         else
+            lang_key_len = 0;
+      }
+#  else /* iTXt */
+      {
+         png_chunk_report(png_ptr, "iTXt chunk not supported",
+            PNG_CHUNK_WRITE_ERROR);
+         continue;
+      }
+#  endif
+
+      if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
+      {
+         text_length = 0;
+#  ifdef PNG_iTXt_SUPPORTED
+         if (text_ptr[i].compression > 0)
+            textp->compression = PNG_ITXT_COMPRESSION_NONE;
+
+         else
+#  endif
+            textp->compression = PNG_TEXT_COMPRESSION_NONE;
+      }
+
+      else
+      {
+         text_length = strlen(text_ptr[i].text);
+         textp->compression = text_ptr[i].compression;
+      }
+
+      textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr,
+          key_len + text_length + lang_len + lang_key_len + 4));
+
+      if (textp->key == NULL)
+      {
+         png_chunk_report(png_ptr, "text chunk: out of memory",
+               PNG_CHUNK_WRITE_ERROR);
+
+         return 1;
+      }
+
+      png_debug2(2, "Allocated %lu bytes at %p in png_set_text",
+          (unsigned long)(png_uint_32)
+          (key_len + lang_len + lang_key_len + text_length + 4),
+          textp->key);
+
+      memcpy(textp->key, text_ptr[i].key, key_len);
+      *(textp->key + key_len) = '\0';
+
+      if (text_ptr[i].compression > 0)
+      {
+         textp->lang = textp->key + key_len + 1;
+         memcpy(textp->lang, text_ptr[i].lang, lang_len);
+         *(textp->lang + lang_len) = '\0';
+         textp->lang_key = textp->lang + lang_len + 1;
+         memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len);
+         *(textp->lang_key + lang_key_len) = '\0';
+         textp->text = textp->lang_key + lang_key_len + 1;
+      }
+
+      else
+      {
+         textp->lang=NULL;
+         textp->lang_key=NULL;
+         textp->text = textp->key + key_len + 1;
+      }
+
+      if (text_length != 0)
+         memcpy(textp->text, text_ptr[i].text, text_length);
+
+      *(textp->text + text_length) = '\0';
+
+#  ifdef PNG_iTXt_SUPPORTED
+      if (textp->compression > 0)
+      {
+         textp->text_length = 0;
+         textp->itxt_length = text_length;
+      }
+
+      else
+#  endif
+      {
+         textp->text_length = text_length;
+         textp->itxt_length = 0;
+      }
+
+      info_ptr->num_text++;
+      png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
+   }
+
+   return(0);
+}
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+void PNGAPI
+png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_timep mod_time)
+{
+   png_debug1(1, "in %s storage function", "tIME");
+
+   if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL ||
+       (png_ptr->mode & PNG_WROTE_tIME) != 0)
+      return;
+
+   if (mod_time->month == 0   || mod_time->month > 12  ||
+       mod_time->day   == 0   || mod_time->day   > 31  ||
+       mod_time->hour  > 23   || mod_time->minute > 59 ||
+       mod_time->second > 60)
+   {
+      png_warning(png_ptr, "Ignoring invalid time value");
+
+      return;
+   }
+
+   info_ptr->mod_time = *mod_time;
+   info_ptr->valid |= PNG_INFO_tIME;
+}
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+void PNGAPI
+png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
+    png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color)
+{
+   png_debug1(1, "in %s storage function", "tRNS");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+
+      return;
+
+   if (trans_alpha != NULL)
+   {
+       /* It may not actually be necessary to set png_ptr->trans_alpha here;
+        * we do it for backward compatibility with the way the png_handle_tRNS
+        * function used to do the allocation.
+        *
+        * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively
+        * relies on png_set_tRNS storing the information in png_struct
+        * (otherwise it won't be there for the code in pngrtran.c).
+        */
+
+       png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
+
+       /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
+       png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep,
+         png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
+
+       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+          memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans);
+   }
+
+   if (trans_color != NULL)
+   {
+#ifdef PNG_WARNINGS_SUPPORTED
+      if (info_ptr->bit_depth < 16)
+      {
+         int sample_max = (1 << info_ptr->bit_depth) - 1;
+
+         if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
+             trans_color->gray > sample_max) ||
+             (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
+             (trans_color->red > sample_max ||
+             trans_color->green > sample_max ||
+             trans_color->blue > sample_max)))
+            png_warning(png_ptr,
+               "tRNS chunk has out-of-range samples for bit_depth");
+      }
+#endif
+
+      info_ptr->trans_color = *trans_color;
+
+      if (num_trans == 0)
+         num_trans = 1;
+   }
+
+   info_ptr->num_trans = (png_uint_16)num_trans;
+
+   if (num_trans != 0)
+   {
+      info_ptr->valid |= PNG_INFO_tRNS;
+      info_ptr->free_me |= PNG_FREE_TRNS;
+   }
+}
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+void PNGAPI
+png_set_sPLT(png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)
+/*
+ *  entries        - array of png_sPLT_t structures
+ *                   to be added to the list of palettes
+ *                   in the info structure.
+ *
+ *  nentries       - number of palette structures to be
+ *                   added.
+ */
+{
+   png_sPLT_tp np;
+
+   if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL)
+      return;
+
+   /* Use the internal realloc function, which checks for all the possible
+    * overflows.  Notice that the parameters are (int) and (size_t)
+    */
+   np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr,
+      info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries,
+      sizeof *np));
+
+   if (np == NULL)
+   {
+      /* Out of memory or too many chunks */
+      png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR);
+
+      return;
+   }
+
+   png_free(png_ptr, info_ptr->splt_palettes);
+   info_ptr->splt_palettes = np;
+   info_ptr->free_me |= PNG_FREE_SPLT;
+
+   np += info_ptr->splt_palettes_num;
+
+   do
+   {
+      png_size_t length;
+
+      /* Skip invalid input entries */
+      if (entries->name == NULL || entries->entries == NULL)
+      {
+         /* png_handle_sPLT doesn't do this, so this is an app error */
+         png_app_error(png_ptr, "png_set_sPLT: invalid sPLT");
+         /* Just skip the invalid entry */
+         continue;
+      }
+
+      np->depth = entries->depth;
+
+      /* In the event of out-of-memory just return - there's no point keeping
+       * on trying to add sPLT chunks.
+       */
+      length = strlen(entries->name) + 1;
+      np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length));
+
+      if (np->name == NULL)
+         break;
+
+      memcpy(np->name, entries->name, length);
+
+      /* IMPORTANT: we have memory now that won't get freed if something else
+       * goes wrong; this code must free it.  png_malloc_array produces no
+       * warnings; use a png_chunk_report (below) if there is an error.
+       */
+      np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr,
+          entries->nentries, sizeof (png_sPLT_entry)));
+
+      if (np->entries == NULL)
+      {
+         png_free(png_ptr, np->name);
+         np->name = NULL;
+         break;
+      }
+
+      np->nentries = entries->nentries;
+      /* This multiply can't overflow because png_malloc_array has already
+       * checked it when doing the allocation.
+       */
+      memcpy(np->entries, entries->entries,
+         entries->nentries * sizeof (png_sPLT_entry));
+
+      /* Note that 'continue' skips the advance of the out pointer and out
+       * count, so an invalid entry is not added.
+       */
+      info_ptr->valid |= PNG_INFO_sPLT;
+      ++(info_ptr->splt_palettes_num);
+      ++np;
+   }
+   while (++entries, --nentries);
+
+   if (nentries > 0)
+      png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
+}
+#endif /* sPLT */
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+static png_byte
+check_location(png_const_structrp png_ptr, int location)
+{
+   location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT);
+
+   /* New in 1.6.0; copy the location and check it.  This is an API
+    * change; previously the app had to use the
+    * png_set_unknown_chunk_location API below for each chunk.
+    */
+   if (location == 0 && (png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
+   {
+      /* Write struct, so unknown chunks come from the app */
+      png_app_warning(png_ptr,
+         "png_set_unknown_chunks now expects a valid location");
+      /* Use the old behavior */
+      location = (png_byte)(png_ptr->mode &
+         (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT));
+   }
+
+   /* This need not be an internal error - if the app calls
+    * png_set_unknown_chunks on a read pointer it must get the location right.
+    */
+   if (location == 0)
+      png_error(png_ptr, "invalid location in png_set_unknown_chunks");
+
+   /* Now reduce the location to the top-most set bit by removing each least
+    * significant bit in turn.
+    */
+   while (location != (location & -location))
+      location &= ~(location & -location);
+
+   /* The cast is safe because 'location' is a bit mask and only the low four
+    * bits are significant.
+    */
+   return (png_byte)location;
+}
+
+void PNGAPI
+png_set_unknown_chunks(png_const_structrp png_ptr,
+   png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
+{
+   png_unknown_chunkp np;
+
+   if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||
+       unknowns == NULL)
+      return;
+
+   /* Check for the failure cases where support has been disabled at compile
+    * time.  This code is hardly ever compiled - it's here because
+    * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this
+    * code) but may be meaningless if the read or write handling of unknown
+    * chunks is not compiled in.
+    */
+#  if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \
+      defined(PNG_READ_SUPPORTED)
+      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
+      {
+         png_app_error(png_ptr, "no unknown chunk support on read");
+
+         return;
+      }
+#  endif
+#  if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \
+      defined(PNG_WRITE_SUPPORTED)
+      if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
+      {
+         png_app_error(png_ptr, "no unknown chunk support on write");
+
+         return;
+      }
+#  endif
+
+   /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that
+    * unknown critical chunks could be lost with just a warning resulting in
+    * undefined behavior.  Now png_chunk_report is used to provide behavior
+    * appropriate to read or write.
+    */
+   np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr,
+         info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns,
+         sizeof *np));
+
+   if (np == NULL)
+   {
+      png_chunk_report(png_ptr, "too many unknown chunks",
+         PNG_CHUNK_WRITE_ERROR);
+
+      return;
+   }
+
+   png_free(png_ptr, info_ptr->unknown_chunks);
+   info_ptr->unknown_chunks = np; /* safe because it is initialized */
+   info_ptr->free_me |= PNG_FREE_UNKN;
+
+   np += info_ptr->unknown_chunks_num;
+
+   /* Increment unknown_chunks_num each time round the loop to protect the
+    * just-allocated chunk data.
+    */
+   for (; num_unknowns > 0; --num_unknowns, ++unknowns)
+   {
+      memcpy(np->name, unknowns->name, (sizeof np->name));
+      np->name[(sizeof np->name)-1] = '\0';
+      np->location = check_location(png_ptr, unknowns->location);
+
+      if (unknowns->size == 0)
+      {
+         np->data = NULL;
+         np->size = 0;
+      }
+
+      else
+      {
+         np->data = png_voidcast(png_bytep,
+            png_malloc_base(png_ptr, unknowns->size));
+
+         if (np->data == NULL)
+         {
+            png_chunk_report(png_ptr, "unknown chunk: out of memory",
+               PNG_CHUNK_WRITE_ERROR);
+            /* But just skip storing the unknown chunk */
+            continue;
+         }
+
+         memcpy(np->data, unknowns->data, unknowns->size);
+         np->size = unknowns->size;
+      }
+
+      /* These increments are skipped on out-of-memory for the data - the
+       * unknown chunk entry gets overwritten if the png_chunk_report returns.
+       * This is correct in the read case (the chunk is just dropped.)
+       */
+      ++np;
+      ++(info_ptr->unknown_chunks_num);
+   }
+}
+
+void PNGAPI
+png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr,
+    int chunk, int location)
+{
+   /* This API is pretty pointless in 1.6.0 because the location can be set
+    * before the call to png_set_unknown_chunks.
+    *
+    * TODO: add a png_app_warning in 1.7
+    */
+   if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 &&
+      chunk < info_ptr->unknown_chunks_num)
+   {
+      if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0)
+      {
+         png_app_error(png_ptr, "invalid unknown chunk location");
+         /* Fake out the pre 1.6.0 behavior: */
+         if ((location & PNG_HAVE_IDAT) != 0) /* undocumented! */
+            location = PNG_AFTER_IDAT;
+
+         else
+            location = PNG_HAVE_IHDR; /* also undocumented */
+      }
+
+      info_ptr->unknown_chunks[chunk].location =
+         check_location(png_ptr, location);
+   }
+}
+#endif /* STORE_UNKNOWN_CHUNKS */
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+png_uint_32 PNGAPI
+png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features)
+{
+   png_debug(1, "in png_permit_mng_features");
+
+   if (png_ptr == NULL)
+      return 0;
+
+   png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES;
+
+   return png_ptr->mng_features_permitted;
+}
+#endif
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+static unsigned int
+add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep)
+{
+   unsigned int i;
+
+   /* Utility function: update the 'keep' state of a chunk if it is already in
+    * the list, otherwise add it to the list.
+    */
+   for (i=0; i<count; ++i, list += 5)
+   {
+      if (memcmp(list, add, 4) == 0)
+      {
+         list[4] = (png_byte)keep;
+
+         return count;
+      }
+   }
+
+   if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)
+   {
+      ++count;
+      memcpy(list, add, 4);
+      list[4] = (png_byte)keep;
+   }
+
+   return count;
+}
+
+void PNGAPI
+png_set_keep_unknown_chunks(png_structrp png_ptr, int keep,
+    png_const_bytep chunk_list, int num_chunks_in)
+{
+   png_bytep new_list;
+   unsigned int num_chunks, old_num_chunks;
+
+   if (png_ptr == NULL)
+      return;
+
+   if (keep < 0 || keep >= PNG_HANDLE_CHUNK_LAST)
+   {
+      png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep");
+
+      return;
+   }
+
+   if (num_chunks_in <= 0)
+   {
+      png_ptr->unknown_default = keep;
+
+      /* '0' means just set the flags, so stop here */
+      if (num_chunks_in == 0)
+        return;
+   }
+
+   if (num_chunks_in < 0)
+   {
+      /* Ignore all unknown chunks and all chunks recognized by
+       * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND
+       */
+      static PNG_CONST png_byte chunks_to_ignore[] = {
+         98,  75,  71,  68, '\0',  /* bKGD */
+         99,  72,  82,  77, '\0',  /* cHRM */
+        103,  65,  77,  65, '\0',  /* gAMA */
+        104,  73,  83,  84, '\0',  /* hIST */
+        105,  67,  67,  80, '\0',  /* iCCP */
+        105,  84,  88, 116, '\0',  /* iTXt */
+        111,  70,  70, 115, '\0',  /* oFFs */
+        112,  67,  65,  76, '\0',  /* pCAL */
+        112,  72,  89, 115, '\0',  /* pHYs */
+        115,  66,  73,  84, '\0',  /* sBIT */
+        115,  67,  65,  76, '\0',  /* sCAL */
+        115,  80,  76,  84, '\0',  /* sPLT */
+        115,  84,  69,  82, '\0',  /* sTER */
+        115,  82,  71,  66, '\0',  /* sRGB */
+        116,  69,  88, 116, '\0',  /* tEXt */
+        116,  73,  77,  69, '\0',  /* tIME */
+        122,  84,  88, 116, '\0'   /* zTXt */
+      };
+
+      chunk_list = chunks_to_ignore;
+      num_chunks = (unsigned int)/*SAFE*/(sizeof chunks_to_ignore)/5U;
+   }
+
+   else /* num_chunks_in > 0 */
+   {
+      if (chunk_list == NULL)
+      {
+         /* Prior to 1.6.0 this was silently ignored, now it is an app_error
+          * which can be switched off.
+          */
+         png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list");
+
+         return;
+      }
+
+      num_chunks = num_chunks_in;
+   }
+
+   old_num_chunks = png_ptr->num_chunk_list;
+   if (png_ptr->chunk_list == NULL)
+      old_num_chunks = 0;
+
+   /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow.
+    */
+   if (num_chunks + old_num_chunks > UINT_MAX/5)
+   {
+      png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks");
+
+      return;
+   }
+
+   /* If these chunks are being reset to the default then no more memory is
+    * required because add_one_chunk above doesn't extend the list if the 'keep'
+    * parameter is the default.
+    */
+   if (keep != 0)
+   {
+      new_list = png_voidcast(png_bytep, png_malloc(png_ptr,
+          5 * (num_chunks + old_num_chunks)));
+
+      if (old_num_chunks > 0)
+         memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks);
+   }
+
+   else if (old_num_chunks > 0)
+      new_list = png_ptr->chunk_list;
+
+   else
+      new_list = NULL;
+
+   /* Add the new chunks together with each one's handling code.  If the chunk
+    * already exists the code is updated, otherwise the chunk is added to the
+    * end.  (In libpng 1.6.0 order no longer matters because this code enforces
+    * the earlier convention that the last setting is the one that is used.)
+    */
+   if (new_list != NULL)
+   {
+      png_const_bytep inlist;
+      png_bytep outlist;
+      unsigned int i;
+
+      for (i=0; i<num_chunks; ++i)
+      {
+         old_num_chunks = add_one_chunk(new_list, old_num_chunks,
+            chunk_list+5*i, keep);
+      }
+
+      /* Now remove any spurious 'default' entries. */
+      num_chunks = 0;
+      for (i=0, inlist=outlist=new_list; i<old_num_chunks; ++i, inlist += 5)
+      {
+         if (inlist[4])
+         {
+            if (outlist != inlist)
+               memcpy(outlist, inlist, 5);
+            outlist += 5;
+            ++num_chunks;
+         }
+      }
+
+      /* This means the application has removed all the specialized handling. */
+      if (num_chunks == 0)
+      {
+         if (png_ptr->chunk_list != new_list)
+            png_free(png_ptr, new_list);
+
+         new_list = NULL;
+      }
+   }
+
+   else
+      num_chunks = 0;
+
+   png_ptr->num_chunk_list = num_chunks;
+
+   if (png_ptr->chunk_list != new_list)
+   {
+      if (png_ptr->chunk_list != NULL)
+         png_free(png_ptr, png_ptr->chunk_list);
+
+      png_ptr->chunk_list = new_list;
+   }
+}
+#endif
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+void PNGAPI
+png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr,
+    png_user_chunk_ptr read_user_chunk_fn)
+{
+   png_debug(1, "in png_set_read_user_chunk_fn");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->read_user_chunk_fn = read_user_chunk_fn;
+   png_ptr->user_chunk_ptr = user_chunk_ptr;
+}
+#endif
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+void PNGAPI
+png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_bytepp row_pointers)
+{
+   png_debug1(1, "in %s storage function", "rows");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (info_ptr->row_pointers != NULL &&
+       (info_ptr->row_pointers != row_pointers))
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+
+   info_ptr->row_pointers = row_pointers;
+
+   if (row_pointers != NULL)
+      info_ptr->valid |= PNG_INFO_IDAT;
+}
+#endif
+
+void PNGAPI
+png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size)
+{
+    if (png_ptr == NULL)
+       return;
+
+    if (size == 0 || size > PNG_UINT_31_MAX)
+       png_error(png_ptr, "invalid compression buffer size");
+
+#  ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+      if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
+      {
+         png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */
+         return;
+      }
+#  endif
+
+#  ifdef PNG_WRITE_SUPPORTED
+      if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
+      {
+         if (png_ptr->zowner != 0)
+         {
+            png_warning(png_ptr,
+              "Compression buffer size cannot be changed because it is in use");
+
+            return;
+         }
+
+#ifndef __COVERITY__
+         /* Some compilers complain that this is always false.  However, it
+          * can be true when integer overflow happens.
+          */
+         if (size > ZLIB_IO_MAX)
+         {
+            png_warning(png_ptr,
+               "Compression buffer size limited to system maximum");
+            size = ZLIB_IO_MAX; /* must fit */
+         }
+#endif
+
+         if (size < 6)
+         {
+            /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH
+             * if this is permitted.
+             */
+            png_warning(png_ptr,
+               "Compression buffer size cannot be reduced below 6");
+
+            return;
+         }
+
+         if (png_ptr->zbuffer_size != size)
+         {
+            png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
+            png_ptr->zbuffer_size = (uInt)size;
+         }
+      }
+#  endif
+}
+
+void PNGAPI
+png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      info_ptr->valid &= ~mask;
+}
+
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* This function was added to libpng 1.2.6 */
+void PNGAPI
+png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max,
+    png_uint_32 user_height_max)
+{
+   /* Images with dimensions larger than these limits will be
+    * rejected by png_set_IHDR().  To accept any PNG datastream
+    * regardless of dimensions, set both limits to 0x7fffffff.
+    */
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->user_width_max = user_width_max;
+   png_ptr->user_height_max = user_height_max;
+}
+
+/* This function was added to libpng 1.4.0 */
+void PNGAPI
+png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max)
+{
+   if (png_ptr != NULL)
+      png_ptr->user_chunk_cache_max = user_chunk_cache_max;
+}
+
+/* This function was added to libpng 1.4.1 */
+void PNGAPI
+png_set_chunk_malloc_max (png_structrp png_ptr,
+    png_alloc_size_t user_chunk_malloc_max)
+{
+   if (png_ptr != NULL)
+      png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
+}
+#endif /* ?SET_USER_LIMITS */
+
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+void PNGAPI
+png_set_benign_errors(png_structrp png_ptr, int allowed)
+{
+   png_debug(1, "in png_set_benign_errors");
+
+   /* If allowed is 1, png_benign_error() is treated as a warning.
+    *
+    * If allowed is 0, png_benign_error() is treated as an error (which
+    * is the default behavior if png_set_benign_errors() is not called).
+    */
+
+   if (allowed != 0)
+      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN |
+         PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN;
+
+   else
+      png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN |
+         PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN);
+}
+#endif /* BENIGN_ERRORS */
+
+#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+   /* Whether to report invalid palette index; added at libng-1.5.10.
+    * It is possible for an indexed (color-type==3) PNG file to contain
+    * pixels with invalid (out-of-range) indexes if the PLTE chunk has
+    * fewer entries than the image's bit-depth would allow. We recover
+    * from this gracefully by filling any incomplete palette with zeros
+    * (opaque black).  By default, when this occurs libpng will issue
+    * a benign error.  This API can be used to override that behavior.
+    */
+void PNGAPI
+png_set_check_for_invalid_index(png_structrp png_ptr, int allowed)
+{
+   png_debug(1, "in png_set_check_for_invalid_index");
+
+   if (allowed > 0)
+      png_ptr->num_palette_max = 0;
+
+   else
+      png_ptr->num_palette_max = -1;
+}
+#endif
+#endif /* READ || WRITE */
diff --git a/third_party/lpng_v163/pngstruct.h b/third_party/libpng/pngstruct.h
similarity index 93%
rename from third_party/lpng_v163/pngstruct.h
rename to third_party/libpng/pngstruct.h
index 87adaa1..ad1e7a5 100644
--- a/third_party/lpng_v163/pngstruct.h
+++ b/third_party/libpng/pngstruct.h
@@ -1,489 +1,483 @@
-

-/* pngstruct.h - header file for PNG reference library

- *

- * 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.)

- *

- * Last changed in libpng 1.6.1 [March 28, 2013]

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

-/* The structure that holds the information to read and write PNG files.

- * The only people who need to care about what is inside of this are the

- * people who will be modifying the library for their own special needs.

- * It should NOT be accessed directly by an application.

- */

-

-#ifndef PNGSTRUCT_H

-#define PNGSTRUCT_H

-/* zlib.h defines the structure z_stream, an instance of which is included

- * in this structure and is required for decompressing the LZ compressed

- * data in PNG files.

- */

-#ifndef ZLIB_CONST

-   /* We must ensure that zlib uses 'const' in declarations. */

-#  define ZLIB_CONST

-#endif

-#include "third_party/zlib_v128/zlib.h"

-#ifdef const

-   /* zlib.h sometimes #defines const to nothing, undo this. */

-#  undef const

-#endif

-

-/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility

- * with older builds.

- */

-#if ZLIB_VERNUM < 0x1260

-#  define PNGZ_MSG_CAST(s) png_constcast(char*,s)

-#  define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)

-#else

-#  define PNGZ_MSG_CAST(s) (s)

-#  define PNGZ_INPUT_CAST(b) (b)

-#endif

-

-/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib

- * can handle at once.  This type need be no larger than 16 bits (so maximum of

- * 65535), this define allows us to discover how big it is, but limited by the

- * maximuum for png_size_t.  The value can be overriden in a library build

- * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably

- * lower value (e.g. 255 works).  A lower value may help memory usage (slightly)

- * and may even improve performance on some systems (and degrade it on others.)

- */

-#ifndef ZLIB_IO_MAX

-#  define ZLIB_IO_MAX ((uInt)-1)

-#endif

-

-#ifdef PNG_WRITE_SUPPORTED

-/* The type of a compression buffer list used by the write code. */

-typedef struct png_compression_buffer

-{

-   struct png_compression_buffer *next;

-   png_byte                       output[1]; /* actually zbuf_size */

-} png_compression_buffer, *png_compression_bufferp;

-

-#define PNG_COMPRESSION_BUFFER_SIZE(pp)\

-   (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)

-#endif

-

-/* Colorspace support; structures used in png_struct, png_info and in internal

- * functions to hold and communicate information about the color space.

- *

- * PNG_COLORSPACE_SUPPORTED is only required if the application will perform

- * colorspace corrections, otherwise all the colorspace information can be

- * skipped and the size of libpng can be reduced (significantly) by compiling

- * out the colorspace support.

- */

-#ifdef PNG_COLORSPACE_SUPPORTED

-/* The chromaticities of the red, green and blue colorants and the chromaticity

- * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)).

- */

-typedef struct png_xy

-{

-   png_fixed_point redx, redy;

-   png_fixed_point greenx, greeny;

-   png_fixed_point bluex, bluey;

-   png_fixed_point whitex, whitey;

-} png_xy;

-

-/* The same data as above but encoded as CIE XYZ values.  When this data comes

- * from chromaticities the sum of the Y values is assumed to be 1.0

- */

-typedef struct png_XYZ

-{

-   png_fixed_point red_X, red_Y, red_Z;

-   png_fixed_point green_X, green_Y, green_Z;

-   png_fixed_point blue_X, blue_Y, blue_Z;

-} png_XYZ;

-#endif /* COLORSPACE */

-

-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)

-/* A colorspace is all the above plus, potentially, profile information,

- * however at present libpng does not use the profile internally so it is only

- * stored in the png_info struct (if iCCP is supported.)  The rendering intent

- * is retained here and is checked.

- *

- * The file gamma encoding information is also stored here and gamma correction

- * is done by libpng, whereas color correction must currently be done by the

- * application.

- */

-typedef struct png_colorspace

-{

-#ifdef PNG_GAMMA_SUPPORTED

-   png_fixed_point gamma;        /* File gamma */

-#endif

-

-#ifdef PNG_COLORSPACE_SUPPORTED

-   png_xy      end_points_xy;    /* End points as chromaticities */

-   png_XYZ     end_points_XYZ;   /* End points as CIE XYZ colorant values */

-   png_uint_16 rendering_intent; /* Rendering intent of a profile */

-#endif

-

-   /* Flags are always defined to simplify the code. */

-   png_uint_16 flags;            /* As defined below */

-} png_colorspace, * PNG_RESTRICT png_colorspacerp;

-

-typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;

-

-/* General flags for the 'flags' field */

-#define PNG_COLORSPACE_HAVE_GAMMA           0x0001

-#define PNG_COLORSPACE_HAVE_ENDPOINTS       0x0002

-#define PNG_COLORSPACE_HAVE_INTENT          0x0004

-#define PNG_COLORSPACE_FROM_gAMA            0x0008

-#define PNG_COLORSPACE_FROM_cHRM            0x0010

-#define PNG_COLORSPACE_FROM_sRGB            0x0020

-#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040

-#define PNG_COLORSPACE_MATCHES_sRGB         0x0080 /* exact match on profile */

-#define PNG_COLORSPACE_INVALID              0x8000

-#define PNG_COLORSPACE_CANCEL(flags)        (0xffff ^ (flags))

-#endif /* COLORSPACE || GAMMA */

-

-struct png_struct_def

-{

-#ifdef PNG_SETJMP_SUPPORTED

-   jmp_buf jmp_buf_local;     /* New name in 1.6.0 for jmp_buf in png_struct */

-   png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */

-   jmp_buf *jmp_buf_ptr;      /* passed to longjmp_fn */

-   size_t jmp_buf_size;       /* size of the above, if allocated */

-#endif

-   png_error_ptr error_fn;    /* function for printing errors and aborting */

-#ifdef PNG_WARNINGS_SUPPORTED

-   png_error_ptr warning_fn;  /* function for printing warnings */

-#endif

-   png_voidp error_ptr;       /* user supplied struct for error functions */

-   png_rw_ptr write_data_fn;  /* function for writing output data */

-   png_rw_ptr read_data_fn;   /* function for reading input data */

-   png_voidp io_ptr;          /* ptr to application struct for I/O functions */

-

-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED

-   png_user_transform_ptr read_user_transform_fn; /* user read transform */

-#endif

-

-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED

-   png_user_transform_ptr write_user_transform_fn; /* user write transform */

-#endif

-

-/* These were added in libpng-1.0.2 */

-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED

-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \

-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)

-   png_voidp user_transform_ptr; /* user supplied struct for user transform */

-   png_byte user_transform_depth;    /* bit depth of user transformed pixels */

-   png_byte user_transform_channels; /* channels in user transformed pixels */

-#endif

-#endif

-

-   png_uint_32 mode;          /* tells us where we are in the PNG file */

-   png_uint_32 flags;         /* flags indicating various things to libpng */

-   png_uint_32 transformations; /* which transformations to perform */

-

-   png_uint_32 zowner;        /* ID (chunk type) of zstream owner, 0 if none */

-   z_stream    zstream;       /* decompression structure */

-

-#ifdef PNG_WRITE_SUPPORTED

-   png_compression_bufferp zbuffer_list; /* Created on demand during write */

-   uInt                    zbuffer_size; /* size of the actual buffer */

-

-   int zlib_level;            /* holds zlib compression level */

-   int zlib_method;           /* holds zlib compression method */

-   int zlib_window_bits;      /* holds zlib compression window bits */

-   int zlib_mem_level;        /* holds zlib compression memory level */

-   int zlib_strategy;         /* holds zlib compression strategy */

-#endif

-/* Added at libpng 1.5.4 */

-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED

-   int zlib_text_level;            /* holds zlib compression level */

-   int zlib_text_method;           /* holds zlib compression method */

-   int zlib_text_window_bits;      /* holds zlib compression window bits */

-   int zlib_text_mem_level;        /* holds zlib compression memory level */

-   int zlib_text_strategy;         /* holds zlib compression strategy */

-#endif

-/* End of material added at libpng 1.5.4 */

-/* Added at libpng 1.6.0 */

-#ifdef PNG_WRITE_SUPPORTED

-   int zlib_set_level;        /* Actual values set into the zstream on write */

-   int zlib_set_method;

-   int zlib_set_window_bits;

-   int zlib_set_mem_level;

-   int zlib_set_strategy;

-#endif

-

-   png_uint_32 width;         /* width of image in pixels */

-   png_uint_32 height;        /* height of image in pixels */

-   png_uint_32 num_rows;      /* number of rows in current pass */

-   png_uint_32 usr_width;     /* width of row at start of write */

-   png_size_t rowbytes;       /* size of row in bytes */

-   png_uint_32 iwidth;        /* width of current interlaced row in pixels */

-   png_uint_32 row_number;    /* current row in interlace pass */

-   png_uint_32 chunk_name;    /* PNG_CHUNK() id of current chunk */

-   png_bytep prev_row;        /* buffer to save previous (unfiltered) row.

-                               * This is a pointer into big_prev_row

-                               */

-   png_bytep row_buf;         /* buffer to save current (unfiltered) row.

-                               * This is a pointer into big_row_buf

-                               */

-#ifdef PNG_WRITE_SUPPORTED

-   png_bytep sub_row;         /* buffer to save "sub" row when filtering */

-   png_bytep up_row;          /* buffer to save "up" row when filtering */

-   png_bytep avg_row;         /* buffer to save "avg" row when filtering */

-   png_bytep paeth_row;       /* buffer to save "Paeth" row when filtering */

-#endif

-   png_size_t info_rowbytes;  /* Added in 1.5.4: cache of updated row bytes */

-

-   png_uint_32 idat_size;     /* current IDAT size for read */

-   png_uint_32 crc;           /* current chunk CRC value */

-   png_colorp palette;        /* palette from the input file */

-   png_uint_16 num_palette;   /* number of color entries in palette */

-

-/* Added at libpng-1.5.10 */

-#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED

-   int num_palette_max;       /* maximum palette index found in IDAT */

-#endif

-

-   png_uint_16 num_trans;     /* number of transparency values */

-   png_byte compression;      /* file compression type (always 0) */

-   png_byte filter;           /* file filter type (always 0) */

-   png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */

-   png_byte pass;             /* current interlace pass (0 - 6) */

-   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ below ) */

-   png_byte color_type;       /* color type of file */

-   png_byte bit_depth;        /* bit depth of file */

-   png_byte usr_bit_depth;    /* bit depth of users row: write only */

-   png_byte pixel_depth;      /* number of bits per pixel */

-   png_byte channels;         /* number of channels in file */

-#ifdef PNG_WRITE_SUPPORTED

-   png_byte usr_channels;     /* channels at start of write: write only */

-#endif

-   png_byte sig_bytes;        /* magic bytes read/written from start of file */

-   png_byte maximum_pixel_depth;

-                              /* pixel depth used for the row buffers */

-   png_byte transformed_pixel_depth;

-                              /* pixel depth after read/write transforms */

-#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)

-   png_uint_16 filler;           /* filler bytes for pixel expansion */

-#endif

-

-#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED)

-   png_byte background_gamma_type;

-   png_fixed_point background_gamma;

-   png_color_16 background;   /* background color in screen gamma space */

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   png_color_16 background_1; /* background normalized to gamma 1.0 */

-#endif

-#endif /* PNG_bKGD_SUPPORTED */

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-   png_flush_ptr output_flush_fn; /* Function for flushing output */

-   png_uint_32 flush_dist;    /* how many rows apart to flush, 0 - no flush */

-   png_uint_32 flush_rows;    /* number of rows written since last flush */

-#endif

-

-#ifdef PNG_READ_GAMMA_SUPPORTED

-   int gamma_shift;      /* number of "insignificant" bits in 16-bit gamma */

-   png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */

-

-   png_bytep gamma_table;     /* gamma table for 8-bit depth files */

-   png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */

-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \

-   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \

-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)

-   png_bytep gamma_from_1;    /* converts from 1.0 to screen */

-   png_bytep gamma_to_1;      /* converts from file to 1.0 */

-   png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */

-   png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */

-#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */

-#endif

-

-#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)

-   png_color_8 sig_bit;       /* significant bits in each available channel */

-#endif

-

-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)

-   png_color_8 shift;         /* shift for significant bit tranformation */

-#endif

-

-#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \

- || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)

-   png_bytep trans_alpha;           /* alpha values for paletted files */

-   png_color_16 trans_color;  /* transparent color for non-paletted files */

-#endif

-

-   png_read_status_ptr read_row_fn;   /* called after each row is decoded */

-   png_write_status_ptr write_row_fn; /* called after each row is encoded */

-#ifdef PNG_PROGRESSIVE_READ_SUPPORTED

-   png_progressive_info_ptr info_fn; /* called after header data fully read */

-   png_progressive_row_ptr row_fn;   /* called after a prog. row is decoded */

-   png_progressive_end_ptr end_fn;   /* called after image is complete */

-   png_bytep save_buffer_ptr;        /* current location in save_buffer */

-   png_bytep save_buffer;            /* buffer for previously read data */

-   png_bytep current_buffer_ptr;     /* current location in current_buffer */

-   png_bytep current_buffer;         /* buffer for recently used data */

-   png_uint_32 push_length;          /* size of current input chunk */

-   png_uint_32 skip_length;          /* bytes to skip in input data */

-   png_size_t save_buffer_size;      /* amount of data now in save_buffer */

-   png_size_t save_buffer_max;       /* total size of save_buffer */

-   png_size_t buffer_size;           /* total amount of available input data */

-   png_size_t current_buffer_size;   /* amount of data now in current_buffer */

-   int process_mode;                 /* what push library is currently doing */

-   int cur_palette;                  /* current push library palette index */

-

-#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */

-

-#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)

-/* For the Borland special 64K segment handler */

-   png_bytepp offset_table_ptr;

-   png_bytep offset_table;

-   png_uint_16 offset_table_number;

-   png_uint_16 offset_table_count;

-   png_uint_16 offset_table_count_free;

-#endif

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-   png_bytep palette_lookup; /* lookup table for quantizing */

-   png_bytep quantize_index; /* index translation for palette files */

-#endif

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-   png_byte heuristic_method;        /* heuristic for row filter selection */

-   png_byte num_prev_filters;        /* number of weights for previous rows */

-   png_bytep prev_filters;           /* filter type(s) of previous row(s) */

-   png_uint_16p filter_weights;      /* weight(s) for previous line(s) */

-   png_uint_16p inv_filter_weights;  /* 1/weight(s) for previous line(s) */

-   png_uint_16p filter_costs;        /* relative filter calculation cost */

-   png_uint_16p inv_filter_costs;    /* 1/relative filter calculation cost */

-#endif

-

-   /* Options */

-#ifdef PNG_SET_OPTION_SUPPORTED

-   png_byte options;           /* On/off state (up to 4 options) */

-#endif

-

-#if PNG_LIBPNG_VER < 10700

-/* To do: remove this from libpng-1.7 */

-#ifdef PNG_TIME_RFC1123_SUPPORTED

-   char time_buffer[29]; /* String to hold RFC 1123 time text */

-#endif

-#endif

-

-/* New members added in libpng-1.0.6 */

-

-   png_uint_32 free_me;    /* flags items libpng is responsible for freeing */

-

-#ifdef PNG_USER_CHUNKS_SUPPORTED

-   png_voidp user_chunk_ptr;

-#ifdef PNG_READ_USER_CHUNKS_SUPPORTED

-   png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */

-#endif

-#endif

-

-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-   int          unknown_default; /* As PNG_HANDLE_* */

-   unsigned int num_chunk_list;  /* Number of entries in the list */

-   png_bytep    chunk_list;      /* List of png_byte[5]; the textual chunk name

-                                  * followed by a PNG_HANDLE_* byte */

-#endif

-

-/* New members added in libpng-1.0.3 */

-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED

-   png_byte rgb_to_gray_status;

-   /* Added in libpng 1.5.5 to record setting of coefficients: */

-   png_byte rgb_to_gray_coefficients_set;

-   /* These were changed from png_byte in libpng-1.0.6 */

-   png_uint_16 rgb_to_gray_red_coeff;

-   png_uint_16 rgb_to_gray_green_coeff;

-   /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */

-#endif

-

-/* New member added in libpng-1.0.4 (renamed in 1.0.9) */

-#if defined(PNG_MNG_FEATURES_SUPPORTED)

-/* Changed from png_byte to png_uint_32 at version 1.2.0 */

-   png_uint_32 mng_features_permitted;

-#endif

-

-/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   png_byte filter_type;

-#endif

-

-/* New members added in libpng-1.2.0 */

-

-/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */

-#ifdef PNG_USER_MEM_SUPPORTED

-   png_voidp mem_ptr;             /* user supplied struct for mem functions */

-   png_malloc_ptr malloc_fn;      /* function for allocating memory */

-   png_free_ptr free_fn;          /* function for freeing memory */

-#endif

-

-/* New member added in libpng-1.0.13 and 1.2.0 */

-   png_bytep big_row_buf;         /* buffer to save current (unfiltered) row */

-

-#ifdef PNG_READ_QUANTIZE_SUPPORTED

-/* The following three members were added at version 1.0.14 and 1.2.4 */

-   png_bytep quantize_sort;          /* working sort array */

-   png_bytep index_to_palette;       /* where the original index currently is

-                                        in the palette */

-   png_bytep palette_to_index;       /* which original index points to this

-                                         palette color */

-#endif

-

-/* New members added in libpng-1.0.16 and 1.2.6 */

-   png_byte compression_type;

-

-#ifdef PNG_USER_LIMITS_SUPPORTED

-   png_uint_32 user_width_max;

-   png_uint_32 user_height_max;

-

-   /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown

-    * chunks that can be stored (0 means unlimited).

-    */

-   png_uint_32 user_chunk_cache_max;

-

-   /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk

-    * can occupy when decompressed.  0 means unlimited.

-    */

-   png_alloc_size_t user_chunk_malloc_max;

-#endif

-

-/* New member added in libpng-1.0.25 and 1.2.17 */

-#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED

-   /* Temporary storage for unknown chunk that the library doesn't recognize,

-    * used while reading the chunk.

-    */

-   png_unknown_chunk unknown_chunk;

-#endif

-

-/* New member added in libpng-1.2.26 */

-  png_size_t old_big_row_buf_size;

-

-#ifdef PNG_READ_SUPPORTED

-/* New member added in libpng-1.2.30 */

-  png_bytep        read_buffer;      /* buffer for reading chunk data */

-  png_alloc_size_t read_buffer_size; /* current size of the buffer */

-#endif

-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED

-  uInt             IDAT_read_size;   /* limit on read buffer size for IDAT */

-#endif

-

-#ifdef PNG_IO_STATE_SUPPORTED

-/* New member added in libpng-1.4.0 */

-   png_uint_32 io_state;

-#endif

-

-/* New member added in libpng-1.5.6 */

-   png_bytep big_prev_row;

-

-/* New member added in libpng-1.5.7 */

-   void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,

-      png_bytep row, png_const_bytep prev_row);

-

-#ifdef PNG_READ_SUPPORTED

-#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)

-   png_colorspace   colorspace;

-#endif

-#endif

-};

-#endif /* PNGSTRUCT_H */

+
+/* pngstruct.h - header file for PNG reference library
+ *
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* The structure that holds the information to read and write PNG files.
+ * The only people who need to care about what is inside of this are the
+ * people who will be modifying the library for their own special needs.
+ * It should NOT be accessed directly by an application.
+ */
+
+#ifndef PNGSTRUCT_H
+#define PNGSTRUCT_H
+/* zlib.h defines the structure z_stream, an instance of which is included
+ * in this structure and is required for decompressing the LZ compressed
+ * data in PNG files.
+ */
+#ifndef ZLIB_CONST
+   /* We must ensure that zlib uses 'const' in declarations. */
+#  define ZLIB_CONST
+#endif
+#include "third_party/zlib_v128/zlib.h"
+#ifdef const
+   /* zlib.h sometimes #defines const to nothing, undo this. */
+#  undef const
+#endif
+
+/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
+ * with older builds.
+ */
+#if ZLIB_VERNUM < 0x1260
+#  define PNGZ_MSG_CAST(s) png_constcast(char*,s)
+#  define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
+#else
+#  define PNGZ_MSG_CAST(s) (s)
+#  define PNGZ_INPUT_CAST(b) (b)
+#endif
+
+/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
+ * can handle at once.  This type need be no larger than 16 bits (so maximum of
+ * 65535), this define allows us to discover how big it is, but limited by the
+ * maximuum for png_size_t.  The value can be overriden in a library build
+ * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
+ * lower value (e.g. 255 works).  A lower value may help memory usage (slightly)
+ * and may even improve performance on some systems (and degrade it on others.)
+ */
+#ifndef ZLIB_IO_MAX
+#  define ZLIB_IO_MAX ((uInt)-1)
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+/* The type of a compression buffer list used by the write code. */
+typedef struct png_compression_buffer
+{
+   struct png_compression_buffer *next;
+   png_byte                       output[1]; /* actually zbuf_size */
+} png_compression_buffer, *png_compression_bufferp;
+
+#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
+   (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
+#endif
+
+/* Colorspace support; structures used in png_struct, png_info and in internal
+ * functions to hold and communicate information about the color space.
+ *
+ * PNG_COLORSPACE_SUPPORTED is only required if the application will perform
+ * colorspace corrections, otherwise all the colorspace information can be
+ * skipped and the size of libpng can be reduced (significantly) by compiling
+ * out the colorspace support.
+ */
+#ifdef PNG_COLORSPACE_SUPPORTED
+/* The chromaticities of the red, green and blue colorants and the chromaticity
+ * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)).
+ */
+typedef struct png_xy
+{
+   png_fixed_point redx, redy;
+   png_fixed_point greenx, greeny;
+   png_fixed_point bluex, bluey;
+   png_fixed_point whitex, whitey;
+} png_xy;
+
+/* The same data as above but encoded as CIE XYZ values.  When this data comes
+ * from chromaticities the sum of the Y values is assumed to be 1.0
+ */
+typedef struct png_XYZ
+{
+   png_fixed_point red_X, red_Y, red_Z;
+   png_fixed_point green_X, green_Y, green_Z;
+   png_fixed_point blue_X, blue_Y, blue_Z;
+} png_XYZ;
+#endif /* COLORSPACE */
+
+#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
+/* A colorspace is all the above plus, potentially, profile information;
+ * however at present libpng does not use the profile internally so it is only
+ * stored in the png_info struct (if iCCP is supported.)  The rendering intent
+ * is retained here and is checked.
+ *
+ * The file gamma encoding information is also stored here and gamma correction
+ * is done by libpng, whereas color correction must currently be done by the
+ * application.
+ */
+typedef struct png_colorspace
+{
+#ifdef PNG_GAMMA_SUPPORTED
+   png_fixed_point gamma;        /* File gamma */
+#endif
+
+#ifdef PNG_COLORSPACE_SUPPORTED
+   png_xy      end_points_xy;    /* End points as chromaticities */
+   png_XYZ     end_points_XYZ;   /* End points as CIE XYZ colorant values */
+   png_uint_16 rendering_intent; /* Rendering intent of a profile */
+#endif
+
+   /* Flags are always defined to simplify the code. */
+   png_uint_16 flags;            /* As defined below */
+} png_colorspace, * PNG_RESTRICT png_colorspacerp;
+
+typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;
+
+/* General flags for the 'flags' field */
+#define PNG_COLORSPACE_HAVE_GAMMA           0x0001
+#define PNG_COLORSPACE_HAVE_ENDPOINTS       0x0002
+#define PNG_COLORSPACE_HAVE_INTENT          0x0004
+#define PNG_COLORSPACE_FROM_gAMA            0x0008
+#define PNG_COLORSPACE_FROM_cHRM            0x0010
+#define PNG_COLORSPACE_FROM_sRGB            0x0020
+#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040
+#define PNG_COLORSPACE_MATCHES_sRGB         0x0080 /* exact match on profile */
+#define PNG_COLORSPACE_INVALID              0x8000
+#define PNG_COLORSPACE_CANCEL(flags)        (0xffff ^ (flags))
+#endif /* COLORSPACE || GAMMA */
+
+struct png_struct_def
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf jmp_buf_local;     /* New name in 1.6.0 for jmp_buf in png_struct */
+   png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
+   jmp_buf *jmp_buf_ptr;      /* passed to longjmp_fn */
+   size_t jmp_buf_size;       /* size of the above, if allocated */
+#endif
+   png_error_ptr error_fn;    /* function for printing errors and aborting */
+#ifdef PNG_WARNINGS_SUPPORTED
+   png_error_ptr warning_fn;  /* function for printing warnings */
+#endif
+   png_voidp error_ptr;       /* user supplied struct for error functions */
+   png_rw_ptr write_data_fn;  /* function for writing output data */
+   png_rw_ptr read_data_fn;   /* function for reading input data */
+   png_voidp io_ptr;          /* ptr to application struct for I/O functions */
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+   png_user_transform_ptr read_user_transform_fn; /* user read transform */
+#endif
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+   png_user_transform_ptr write_user_transform_fn; /* user write transform */
+#endif
+
+/* These were added in libpng-1.0.2 */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   png_voidp user_transform_ptr; /* user supplied struct for user transform */
+   png_byte user_transform_depth;    /* bit depth of user transformed pixels */
+   png_byte user_transform_channels; /* channels in user transformed pixels */
+#endif
+#endif
+
+   png_uint_32 mode;          /* tells us where we are in the PNG file */
+   png_uint_32 flags;         /* flags indicating various things to libpng */
+   png_uint_32 transformations; /* which transformations to perform */
+
+   png_uint_32 zowner;        /* ID (chunk type) of zstream owner, 0 if none */
+   z_stream    zstream;       /* decompression structure */
+
+#ifdef PNG_WRITE_SUPPORTED
+   png_compression_bufferp zbuffer_list; /* Created on demand during write */
+   uInt                    zbuffer_size; /* size of the actual buffer */
+
+   int zlib_level;            /* holds zlib compression level */
+   int zlib_method;           /* holds zlib compression method */
+   int zlib_window_bits;      /* holds zlib compression window bits */
+   int zlib_mem_level;        /* holds zlib compression memory level */
+   int zlib_strategy;         /* holds zlib compression strategy */
+#endif
+/* Added at libpng 1.5.4 */
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+   int zlib_text_level;            /* holds zlib compression level */
+   int zlib_text_method;           /* holds zlib compression method */
+   int zlib_text_window_bits;      /* holds zlib compression window bits */
+   int zlib_text_mem_level;        /* holds zlib compression memory level */
+   int zlib_text_strategy;         /* holds zlib compression strategy */
+#endif
+/* End of material added at libpng 1.5.4 */
+/* Added at libpng 1.6.0 */
+#ifdef PNG_WRITE_SUPPORTED
+   int zlib_set_level;        /* Actual values set into the zstream on write */
+   int zlib_set_method;
+   int zlib_set_window_bits;
+   int zlib_set_mem_level;
+   int zlib_set_strategy;
+#endif
+
+   png_uint_32 width;         /* width of image in pixels */
+   png_uint_32 height;        /* height of image in pixels */
+   png_uint_32 num_rows;      /* number of rows in current pass */
+   png_uint_32 usr_width;     /* width of row at start of write */
+   png_size_t rowbytes;       /* size of row in bytes */
+   png_uint_32 iwidth;        /* width of current interlaced row in pixels */
+   png_uint_32 row_number;    /* current row in interlace pass */
+   png_uint_32 chunk_name;    /* PNG_CHUNK() id of current chunk */
+   png_bytep prev_row;        /* buffer to save previous (unfiltered) row.
+                               * While reading this is a pointer into
+                               * big_prev_row; while writing it is separately
+                               * allocated if needed.
+                               */
+   png_bytep row_buf;         /* buffer to save current (unfiltered) row.
+                               * While reading, this is a pointer into
+                               * big_row_buf; while writing it is separately
+                               * allocated.
+                               */
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+   png_bytep try_row;    /* buffer to save trial row when filtering */
+   png_bytep tst_row;    /* buffer to save best trial row when filtering */
+#endif
+   png_size_t info_rowbytes;  /* Added in 1.5.4: cache of updated row bytes */
+
+   png_uint_32 idat_size;     /* current IDAT size for read */
+   png_uint_32 crc;           /* current chunk CRC value */
+   png_colorp palette;        /* palette from the input file */
+   png_uint_16 num_palette;   /* number of color entries in palette */
+
+/* Added at libpng-1.5.10 */
+#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+   int num_palette_max;       /* maximum palette index found in IDAT */
+#endif
+
+   png_uint_16 num_trans;     /* number of transparency values */
+   png_byte compression;      /* file compression type (always 0) */
+   png_byte filter;           /* file filter type (always 0) */
+   png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+   png_byte pass;             /* current interlace pass (0 - 6) */
+   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ below ) */
+   png_byte color_type;       /* color type of file */
+   png_byte bit_depth;        /* bit depth of file */
+   png_byte usr_bit_depth;    /* bit depth of users row: write only */
+   png_byte pixel_depth;      /* number of bits per pixel */
+   png_byte channels;         /* number of channels in file */
+#ifdef PNG_WRITE_SUPPORTED
+   png_byte usr_channels;     /* channels at start of write: write only */
+#endif
+   png_byte sig_bytes;        /* magic bytes read/written from start of file */
+   png_byte maximum_pixel_depth;
+                              /* pixel depth used for the row buffers */
+   png_byte transformed_pixel_depth;
+                              /* pixel depth after read/write transforms */
+#if PNG_ZLIB_VERNUM >= 0x1240
+   png_byte zstream_start;    /* at start of an input zlib stream */
+#endif /* Zlib >= 1.2.4 */
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+   png_uint_16 filler;           /* filler bytes for pixel expansion */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+   png_byte background_gamma_type;
+   png_fixed_point background_gamma;
+   png_color_16 background;   /* background color in screen gamma space */
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   png_color_16 background_1; /* background normalized to gamma 1.0 */
+#endif
+#endif /* bKGD */
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+   png_flush_ptr output_flush_fn; /* Function for flushing output */
+   png_uint_32 flush_dist;    /* how many rows apart to flush, 0 - no flush */
+   png_uint_32 flush_rows;    /* number of rows written since last flush */
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+   int gamma_shift;      /* number of "insignificant" bits in 16-bit gamma */
+   png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
+
+   png_bytep gamma_table;     /* gamma table for 8-bit depth files */
+   png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   png_bytep gamma_from_1;    /* converts from 1.0 to screen */
+   png_bytep gamma_to_1;      /* converts from file to 1.0 */
+   png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
+   png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
+   png_color_8 sig_bit;       /* significant bits in each available channel */
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+   png_color_8 shift;         /* shift for significant bit tranformation */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
+ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep trans_alpha;           /* alpha values for paletted files */
+   png_color_16 trans_color;  /* transparent color for non-paletted files */
+#endif
+
+   png_read_status_ptr read_row_fn;   /* called after each row is decoded */
+   png_write_status_ptr write_row_fn; /* called after each row is encoded */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_progressive_info_ptr info_fn; /* called after header data fully read */
+   png_progressive_row_ptr row_fn;   /* called after a prog. row is decoded */
+   png_progressive_end_ptr end_fn;   /* called after image is complete */
+   png_bytep save_buffer_ptr;        /* current location in save_buffer */
+   png_bytep save_buffer;            /* buffer for previously read data */
+   png_bytep current_buffer_ptr;     /* current location in current_buffer */
+   png_bytep current_buffer;         /* buffer for recently used data */
+   png_uint_32 push_length;          /* size of current input chunk */
+   png_uint_32 skip_length;          /* bytes to skip in input data */
+   png_size_t save_buffer_size;      /* amount of data now in save_buffer */
+   png_size_t save_buffer_max;       /* total size of save_buffer */
+   png_size_t buffer_size;           /* total amount of available input data */
+   png_size_t current_buffer_size;   /* amount of data now in current_buffer */
+   int process_mode;                 /* what push library is currently doing */
+   int cur_palette;                  /* current push library palette index */
+
+#endif /* PROGRESSIVE_READ */
+
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* For the Borland special 64K segment handler */
+   png_bytepp offset_table_ptr;
+   png_bytep offset_table;
+   png_uint_16 offset_table_number;
+   png_uint_16 offset_table_count;
+   png_uint_16 offset_table_count_free;
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+   png_bytep palette_lookup; /* lookup table for quantizing */
+   png_bytep quantize_index; /* index translation for palette files */
+#endif
+
+/* Options */
+#ifdef PNG_SET_OPTION_SUPPORTED
+   png_byte options;           /* On/off state (up to 4 options) */
+#endif
+
+#if PNG_LIBPNG_VER < 10700
+/* To do: remove this from libpng-1.7 */
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+   char time_buffer[29]; /* String to hold RFC 1123 time text */
+#endif
+#endif
+
+/* New members added in libpng-1.0.6 */
+
+   png_uint_32 free_me;    /* flags items libpng is responsible for freeing */
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+   png_voidp user_chunk_ptr;
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+   png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
+#endif
+#endif
+
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+   int          unknown_default; /* As PNG_HANDLE_* */
+   unsigned int num_chunk_list;  /* Number of entries in the list */
+   png_bytep    chunk_list;      /* List of png_byte[5]; the textual chunk name
+                                  * followed by a PNG_HANDLE_* byte */
+#endif
+
+/* New members added in libpng-1.0.3 */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+   png_byte rgb_to_gray_status;
+   /* Added in libpng 1.5.5 to record setting of coefficients: */
+   png_byte rgb_to_gray_coefficients_set;
+   /* These were changed from png_byte in libpng-1.0.6 */
+   png_uint_16 rgb_to_gray_red_coeff;
+   png_uint_16 rgb_to_gray_green_coeff;
+   /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */
+#endif
+
+/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+/* Changed from png_byte to png_uint_32 at version 1.2.0 */
+   png_uint_32 mng_features_permitted;
+#endif
+
+/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   png_byte filter_type;
+#endif
+
+/* New members added in libpng-1.2.0 */
+
+/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_voidp mem_ptr;             /* user supplied struct for mem functions */
+   png_malloc_ptr malloc_fn;      /* function for allocating memory */
+   png_free_ptr free_fn;          /* function for freeing memory */
+#endif
+
+/* New member added in libpng-1.0.13 and 1.2.0 */
+   png_bytep big_row_buf;         /* buffer to save current (unfiltered) row */
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* The following three members were added at version 1.0.14 and 1.2.4 */
+   png_bytep quantize_sort;          /* working sort array */
+   png_bytep index_to_palette;       /* where the original index currently is
+                                        in the palette */
+   png_bytep palette_to_index;       /* which original index points to this
+                                         palette color */
+#endif
+
+/* New members added in libpng-1.0.16 and 1.2.6 */
+   png_byte compression_type;
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+   png_uint_32 user_width_max;
+   png_uint_32 user_height_max;
+
+   /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown
+    * chunks that can be stored (0 means unlimited).
+    */
+   png_uint_32 user_chunk_cache_max;
+
+   /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk
+    * can occupy when decompressed.  0 means unlimited.
+    */
+   png_alloc_size_t user_chunk_malloc_max;
+#endif
+
+/* New member added in libpng-1.0.25 and 1.2.17 */
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+   /* Temporary storage for unknown chunk that the library doesn't recognize,
+    * used while reading the chunk.
+    */
+   png_unknown_chunk unknown_chunk;
+#endif
+
+/* New member added in libpng-1.2.26 */
+  png_size_t old_big_row_buf_size;
+
+#ifdef PNG_READ_SUPPORTED
+/* New member added in libpng-1.2.30 */
+  png_bytep        read_buffer;      /* buffer for reading chunk data */
+  png_alloc_size_t read_buffer_size; /* current size of the buffer */
+#endif
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+  uInt             IDAT_read_size;   /* limit on read buffer size for IDAT */
+#endif
+
+#ifdef PNG_IO_STATE_SUPPORTED
+/* New member added in libpng-1.4.0 */
+   png_uint_32 io_state;
+#endif
+
+/* New member added in libpng-1.5.6 */
+   png_bytep big_prev_row;
+
+/* New member added in libpng-1.5.7 */
+   void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,
+      png_bytep row, png_const_bytep prev_row);
+
+#ifdef PNG_READ_SUPPORTED
+#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
+   png_colorspace   colorspace;
+#endif
+#endif
+};
+#endif /* PNGSTRUCT_H */
diff --git a/third_party/lpng_v163/pngtrans.c b/third_party/libpng/pngtrans.c
similarity index 94%
rename from third_party/lpng_v163/pngtrans.c
rename to third_party/libpng/pngtrans.c
index 0de6a46..0c0d92d 100644
--- a/third_party/lpng_v163/pngtrans.c
+++ b/third_party/libpng/pngtrans.c
@@ -1,840 +1,849 @@
-/* pngtrans.c - transforms the data in a row (used by both readers and writers)

- *

- * 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-

-#include "pngpriv.h"

-

-#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)

-

-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)

-/* Turn on BGR-to-RGB mapping */

-void PNGAPI

-png_set_bgr(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_bgr");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->transformations |= PNG_BGR;

-}

-#endif

-

-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)

-/* Turn on 16 bit byte swapping */

-void PNGAPI

-png_set_swap(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_swap");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (png_ptr->bit_depth == 16)

-      png_ptr->transformations |= PNG_SWAP_BYTES;

-}

-#endif

-

-#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)

-/* Turn on pixel packing */

-void PNGAPI

-png_set_packing(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_packing");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (png_ptr->bit_depth < 8)

-   {

-      png_ptr->transformations |= PNG_PACK;

-      png_ptr->usr_bit_depth = 8;

-   }

-}

-#endif

-

-#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)

-/* Turn on packed pixel swapping */

-void PNGAPI

-png_set_packswap(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_packswap");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (png_ptr->bit_depth < 8)

-      png_ptr->transformations |= PNG_PACKSWAP;

-}

-#endif

-

-#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)

-void PNGAPI

-png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)

-{

-   png_debug(1, "in png_set_shift");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->transformations |= PNG_SHIFT;

-   png_ptr->shift = *true_bits;

-}

-#endif

-

-#if defined(PNG_READ_INTERLACING_SUPPORTED) || \

-    defined(PNG_WRITE_INTERLACING_SUPPORTED)

-int PNGAPI

-png_set_interlace_handling(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_interlace handling");

-

-   if (png_ptr && png_ptr->interlaced)

-   {

-      png_ptr->transformations |= PNG_INTERLACE;

-      return (7);

-   }

-

-   return (1);

-}

-#endif

-

-#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)

-/* Add a filler byte on read, or remove a filler or alpha byte on write.

- * The filler type has changed in v0.95 to allow future 2-byte fillers

- * for 48-bit input data, as well as to avoid problems with some compilers

- * that don't like bytes as parameters.

- */

-void PNGAPI

-png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)

-{

-   png_debug(1, "in png_set_filler");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* In libpng 1.6 it is possible to determine whether this is a read or write

-    * operation and therefore to do more checking here for a valid call.

-    */

-   if (png_ptr->mode & PNG_IS_READ_STRUCT)

-   {

-#     ifdef PNG_READ_FILLER_SUPPORTED

-         /* On read png_set_filler is always valid, regardless of the base PNG

-          * format, because other transformations can give a format where the

-          * filler code can execute (basically an 8 or 16-bit component RGB or G

-          * format.)

-          *

-          * NOTE: usr_channels is not used by the read code!  (This has led to

-          * confusion in the past.)  The filler is only used in the read code.

-          */

-         png_ptr->filler = (png_uint_16)filler;

-#     else

-         png_app_error(png_ptr, "png_set_filler not supported on read");

-         PNG_UNUSED(filler) /* not used in the write case */

-         return;

-#     endif

-   }

-

-   else /* write */

-   {

-#     ifdef PNG_WRITE_FILLER_SUPPORTED

-         /* On write the usr_channels parameter must be set correctly at the

-          * start to record the number of channels in the app-supplied data.

-          */

-         switch (png_ptr->color_type)

-         {

-            case PNG_COLOR_TYPE_RGB:

-               png_ptr->usr_channels = 4;

-               break;

-

-            case PNG_COLOR_TYPE_GRAY:

-               if (png_ptr->bit_depth >= 8)

-               {

-                  png_ptr->usr_channels = 2;

-                  break;

-               }

-

-               else

-               {

-                  /* There simply isn't any code in libpng to strip out bits

-                   * from bytes when the components are less than a byte in

-                   * size!

-                   */

-                  png_app_error(png_ptr,

-                     "png_set_filler is invalid for low bit depth gray output");

-                  return;

-               }

-

-            default:

-               png_app_error(png_ptr,

-                  "png_set_filler: inappropriate color type");

-               return;

-         }

-#     else

-         png_app_error(png_ptr, "png_set_filler not supported on write");

-         return;

-#     endif

-   }

-

-   /* Here on success - libpng supports the operation, set the transformation

-    * and the flag to say where the filler channel is.

-    */

-   png_ptr->transformations |= PNG_FILLER;

-

-   if (filler_loc == PNG_FILLER_AFTER)

-      png_ptr->flags |= PNG_FLAG_FILLER_AFTER;

-

-   else

-      png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;

-}

-

-/* Added to libpng-1.2.7 */

-void PNGAPI

-png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)

-{

-   png_debug(1, "in png_set_add_alpha");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_set_filler(png_ptr, filler, filler_loc);

-   /* The above may fail to do anything. */

-   if (png_ptr->transformations & PNG_FILLER)

-      png_ptr->transformations |= PNG_ADD_ALPHA;

-}

-

-#endif

-

-#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \

-    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)

-void PNGAPI

-png_set_swap_alpha(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_swap_alpha");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->transformations |= PNG_SWAP_ALPHA;

-}

-#endif

-

-#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \

-    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)

-void PNGAPI

-png_set_invert_alpha(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_invert_alpha");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->transformations |= PNG_INVERT_ALPHA;

-}

-#endif

-

-#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)

-void PNGAPI

-png_set_invert_mono(png_structrp png_ptr)

-{

-   png_debug(1, "in png_set_invert_mono");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->transformations |= PNG_INVERT_MONO;

-}

-

-/* Invert monochrome grayscale data */

-void /* PRIVATE */

-png_do_invert(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_invert");

-

-  /* This test removed from libpng version 1.0.13 and 1.2.0:

-   *   if (row_info->bit_depth == 1 &&

-   */

-   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)

-   {

-      png_bytep rp = row;

-      png_size_t i;

-      png_size_t istop = row_info->rowbytes;

-

-      for (i = 0; i < istop; i++)

-      {

-         *rp = (png_byte)(~(*rp));

-         rp++;

-      }

-   }

-

-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&

-      row_info->bit_depth == 8)

-   {

-      png_bytep rp = row;

-      png_size_t i;

-      png_size_t istop = row_info->rowbytes;

-

-      for (i = 0; i < istop; i += 2)

-      {

-         *rp = (png_byte)(~(*rp));

-         rp += 2;

-      }

-   }

-

-#ifdef PNG_16BIT_SUPPORTED

-   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&

-      row_info->bit_depth == 16)

-   {

-      png_bytep rp = row;

-      png_size_t i;

-      png_size_t istop = row_info->rowbytes;

-

-      for (i = 0; i < istop; i += 4)

-      {

-         *rp = (png_byte)(~(*rp));

-         *(rp + 1) = (png_byte)(~(*(rp + 1)));

-         rp += 4;

-      }

-   }

-#endif

-}

-#endif

-

-#ifdef PNG_16BIT_SUPPORTED

-#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)

-/* Swaps byte order on 16 bit depth images */

-void /* PRIVATE */

-png_do_swap(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_swap");

-

-   if (row_info->bit_depth == 16)

-   {

-      png_bytep rp = row;

-      png_uint_32 i;

-      png_uint_32 istop= row_info->width * row_info->channels;

-

-      for (i = 0; i < istop; i++, rp += 2)

-      {

-         png_byte t = *rp;

-         *rp = *(rp + 1);

-         *(rp + 1) = t;

-      }

-   }

-}

-#endif

-#endif

-

-#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)

-static PNG_CONST png_byte onebppswaptable[256] = {

-   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,

-   0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,

-   0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,

-   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,

-   0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,

-   0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,

-   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,

-   0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,

-   0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,

-   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,

-   0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,

-   0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,

-   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,

-   0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,

-   0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,

-   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,

-   0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,

-   0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,

-   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,

-   0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,

-   0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,

-   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,

-   0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,

-   0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,

-   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,

-   0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,

-   0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,

-   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,

-   0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,

-   0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,

-   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,

-   0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF

-};

-

-static PNG_CONST png_byte twobppswaptable[256] = {

-   0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,

-   0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,

-   0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,

-   0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,

-   0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,

-   0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,

-   0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,

-   0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,

-   0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,

-   0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,

-   0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,

-   0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,

-   0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,

-   0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,

-   0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,

-   0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,

-   0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,

-   0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,

-   0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,

-   0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,

-   0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,

-   0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,

-   0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,

-   0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,

-   0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,

-   0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,

-   0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,

-   0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,

-   0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,

-   0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,

-   0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,

-   0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF

-};

-

-static PNG_CONST png_byte fourbppswaptable[256] = {

-   0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,

-   0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,

-   0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,

-   0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,

-   0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,

-   0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,

-   0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,

-   0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,

-   0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,

-   0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,

-   0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,

-   0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,

-   0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,

-   0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,

-   0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,

-   0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,

-   0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,

-   0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,

-   0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,

-   0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,

-   0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,

-   0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,

-   0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,

-   0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,

-   0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,

-   0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,

-   0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,

-   0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,

-   0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,

-   0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,

-   0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,

-   0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF

-};

-

-/* Swaps pixel packing order within bytes */

-void /* PRIVATE */

-png_do_packswap(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_packswap");

-

-   if (row_info->bit_depth < 8)

-   {

-      png_bytep rp;

-      png_const_bytep end, table;

-

-      end = row + row_info->rowbytes;

-

-      if (row_info->bit_depth == 1)

-         table = onebppswaptable;

-

-      else if (row_info->bit_depth == 2)

-         table = twobppswaptable;

-

-      else if (row_info->bit_depth == 4)

-         table = fourbppswaptable;

-

-      else

-         return;

-

-      for (rp = row; rp < end; rp++)

-         *rp = table[*rp];

-   }

-}

-#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */

-

-#if defined(PNG_WRITE_FILLER_SUPPORTED) || \

-    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)

-/* Remove a channel - this used to be 'png_do_strip_filler' but it used a

- * somewhat weird combination of flags to determine what to do.  All the calls

- * to png_do_strip_filler are changed in 1.5.2 to call this instead with the

- * correct arguments.

- *

- * The routine isn't general - the channel must be the channel at the start or

- * end (not in the middle) of each pixel.

- */

-void /* PRIVATE */

-png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)

-{

-   png_bytep sp = row; /* source pointer */

-   png_bytep dp = row; /* destination pointer */

-   png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */

-

-   /* At the start sp will point to the first byte to copy and dp to where

-    * it is copied to.  ep always points just beyond the end of the row, so

-    * the loop simply copies (channels-1) channels until sp reaches ep.

-    *

-    * at_start:        0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.

-    *            nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.

-    */

-

-   /* GA, GX, XG cases */

-   if (row_info->channels == 2)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         if (at_start) /* Skip initial filler */

-            ++sp;

-         else          /* Skip initial channel and, for sp, the filler */

-            sp += 2, ++dp;

-

-         /* For a 1 pixel wide image there is nothing to do */

-         while (sp < ep)

-            *dp++ = *sp, sp += 2;

-

-         row_info->pixel_depth = 8;

-      }

-

-      else if (row_info->bit_depth == 16)

-      {

-         if (at_start) /* Skip initial filler */

-            sp += 2;

-         else          /* Skip initial channel and, for sp, the filler */

-            sp += 4, dp += 2;

-

-         while (sp < ep)

-            *dp++ = *sp++, *dp++ = *sp, sp += 3;

-

-         row_info->pixel_depth = 16;

-      }

-

-      else

-         return; /* bad bit depth */

-

-      row_info->channels = 1;

-

-      /* Finally fix the color type if it records an alpha channel */

-      if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-         row_info->color_type = PNG_COLOR_TYPE_GRAY;

-   }

-

-   /* RGBA, RGBX, XRGB cases */

-   else if (row_info->channels == 4)

-   {

-      if (row_info->bit_depth == 8)

-      {

-         if (at_start) /* Skip initial filler */

-            ++sp;

-         else          /* Skip initial channels and, for sp, the filler */

-            sp += 4, dp += 3;

-

-         /* Note that the loop adds 3 to dp and 4 to sp each time. */

-         while (sp < ep)

-            *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2;

-

-         row_info->pixel_depth = 24;

-      }

-

-      else if (row_info->bit_depth == 16)

-      {

-         if (at_start) /* Skip initial filler */

-            sp += 2;

-         else          /* Skip initial channels and, for sp, the filler */

-            sp += 8, dp += 6;

-

-         while (sp < ep)

-         {

-            /* Copy 6 bytes, skip 2 */

-            *dp++ = *sp++, *dp++ = *sp++;

-            *dp++ = *sp++, *dp++ = *sp++;

-            *dp++ = *sp++, *dp++ = *sp, sp += 3;

-         }

-

-         row_info->pixel_depth = 48;

-      }

-

-      else

-         return; /* bad bit depth */

-

-      row_info->channels = 3;

-

-      /* Finally fix the color type if it records an alpha channel */

-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-         row_info->color_type = PNG_COLOR_TYPE_RGB;

-   }

-

-   else

-      return; /* The filler channel has gone already */

-

-   /* Fix the rowbytes value. */

-   row_info->rowbytes = dp-row;

-}

-#endif

-

-#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)

-/* Swaps red and blue bytes within a pixel */

-void /* PRIVATE */

-png_do_bgr(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_bgr");

-

-   if ((row_info->color_type & PNG_COLOR_MASK_COLOR))

-   {

-      png_uint_32 row_width = row_info->width;

-      if (row_info->bit_depth == 8)

-      {

-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-         {

-            png_bytep rp;

-            png_uint_32 i;

-

-            for (i = 0, rp = row; i < row_width; i++, rp += 3)

-            {

-               png_byte save = *rp;

-               *rp = *(rp + 2);

-               *(rp + 2) = save;

-            }

-         }

-

-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-         {

-            png_bytep rp;

-            png_uint_32 i;

-

-            for (i = 0, rp = row; i < row_width; i++, rp += 4)

-            {

-               png_byte save = *rp;

-               *rp = *(rp + 2);

-               *(rp + 2) = save;

-            }

-         }

-      }

-

-#ifdef PNG_16BIT_SUPPORTED

-      else if (row_info->bit_depth == 16)

-      {

-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-         {

-            png_bytep rp;

-            png_uint_32 i;

-

-            for (i = 0, rp = row; i < row_width; i++, rp += 6)

-            {

-               png_byte save = *rp;

-               *rp = *(rp + 4);

-               *(rp + 4) = save;

-               save = *(rp + 1);

-               *(rp + 1) = *(rp + 5);

-               *(rp + 5) = save;

-            }

-         }

-

-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-         {

-            png_bytep rp;

-            png_uint_32 i;

-

-            for (i = 0, rp = row; i < row_width; i++, rp += 8)

-            {

-               png_byte save = *rp;

-               *rp = *(rp + 4);

-               *(rp + 4) = save;

-               save = *(rp + 1);

-               *(rp + 1) = *(rp + 5);

-               *(rp + 5) = save;

-            }

-         }

-      }

-#endif

-   }

-}

-#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */

-

-#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \

-    defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)

-/* Added at libpng-1.5.10 */

-void /* PRIVATE */

-png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)

-{

-   if (png_ptr->num_palette < (1 << row_info->bit_depth) &&

-      png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */

-   {

-      /* Calculations moved outside switch in an attempt to stop different

-       * compiler warnings.  'padding' is in *bits* within the last byte, it is

-       * an 'int' because pixel_depth becomes an 'int' in the expression below,

-       * and this calculation is used because it avoids warnings that other

-       * forms produced on either GCC or MSVC.

-       */

-      int padding = (-row_info->pixel_depth * row_info->width) & 7;

-      png_bytep rp = png_ptr->row_buf + row_info->rowbytes;

-

-      switch (row_info->bit_depth)

-      {

-         case 1:

-         {

-            /* in this case, all bytes must be 0 so we don't need

-             * to unpack the pixels except for the rightmost one.

-             */

-            for (; rp > png_ptr->row_buf; rp--)

-            {

-              if (*rp >> padding != 0)

-                 png_ptr->num_palette_max = 1;

-              padding = 0;

-            }

-

-            break;

-         }

-

-         case 2:

-         {

-            for (; rp > png_ptr->row_buf; rp--)

-            {

-              int i = ((*rp >> padding) & 0x03);

-

-              if (i > png_ptr->num_palette_max)

-                 png_ptr->num_palette_max = i;

-

-              i = (((*rp >> padding) >> 2) & 0x03);

-

-              if (i > png_ptr->num_palette_max)

-                 png_ptr->num_palette_max = i;

-

-              i = (((*rp >> padding) >> 4) & 0x03);

-

-              if (i > png_ptr->num_palette_max)

-                 png_ptr->num_palette_max = i;

-

-              i = (((*rp >> padding) >> 6) & 0x03);

-

-              if (i > png_ptr->num_palette_max)

-                 png_ptr->num_palette_max = i;

-

-              padding = 0;

-            }

-

-            break;

-         }

-

-         case 4:

-         {

-            for (; rp > png_ptr->row_buf; rp--)

-            {

-              int i = ((*rp >> padding) & 0x0f);

-

-              if (i > png_ptr->num_palette_max)

-                 png_ptr->num_palette_max = i;

-

-              i = (((*rp >> padding) >> 4) & 0x0f);

-

-              if (i > png_ptr->num_palette_max)

-                 png_ptr->num_palette_max = i;

-

-              padding = 0;

-            }

-

-            break;

-         }

-

-         case 8:

-         {

-            for (; rp > png_ptr->row_buf; rp--)

-            {

-               if (*rp > png_ptr->num_palette_max)

-                  png_ptr->num_palette_max = (int) *rp;

-            }

-

-            break;

-         }

-

-         default:

-            break;

-      }

-   }

-}

-#endif /* PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED */

-

-#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \

-    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)

-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED

-void PNGAPI

-png_set_user_transform_info(png_structrp png_ptr, png_voidp

-   user_transform_ptr, int user_transform_depth, int user_transform_channels)

-{

-   png_debug(1, "in png_set_user_transform_info");

-

-   if (png_ptr == NULL)

-      return;

-

-#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED

-   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&

-      (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)

-   {

-      png_app_error(png_ptr,

-            "info change after png_start_read_image or png_read_update_info");

-      return;

-   }

-#endif

-

-   png_ptr->user_transform_ptr = user_transform_ptr;

-   png_ptr->user_transform_depth = (png_byte)user_transform_depth;

-   png_ptr->user_transform_channels = (png_byte)user_transform_channels;

-}

-#endif

-

-/* This function returns a pointer to the user_transform_ptr associated with

- * the user transform functions.  The application should free any memory

- * associated with this pointer before png_write_destroy and png_read_destroy

- * are called.

- */

-#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED

-png_voidp PNGAPI

-png_get_user_transform_ptr(png_const_structrp png_ptr)

-{

-   if (png_ptr == NULL)

-      return (NULL);

-

-   return png_ptr->user_transform_ptr;

-}

-#endif

-

-#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED

-png_uint_32 PNGAPI

-png_get_current_row_number(png_const_structrp png_ptr)

-{

-   /* See the comments in png.h - this is the sub-image row when reading and

-    * interlaced image.

-    */

-   if (png_ptr != NULL)

-      return png_ptr->row_number;

-

-   return PNG_UINT_32_MAX; /* help the app not to fail silently */

-}

-

-png_byte PNGAPI

-png_get_current_pass_number(png_const_structrp png_ptr)

-{

-   if (png_ptr != NULL)

-      return png_ptr->pass;

-   return 8; /* invalid */

-}

-#endif /* PNG_USER_TRANSFORM_INFO_SUPPORTED */

-#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED ||

-          PNG_WRITE_USER_TRANSFORM_SUPPORTED */

-#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */

+
+/* pngtrans.c - transforms the data in a row (used by both readers and writers)
+ *
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Turn on BGR-to-RGB mapping */
+void PNGAPI
+png_set_bgr(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_bgr");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= PNG_BGR;
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Turn on 16-bit byte swapping */
+void PNGAPI
+png_set_swap(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_swap");
+
+   if (png_ptr == NULL)
+      return;
+
+   if (png_ptr->bit_depth == 16)
+      png_ptr->transformations |= PNG_SWAP_BYTES;
+}
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Turn on pixel packing */
+void PNGAPI
+png_set_packing(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_packing");
+
+   if (png_ptr == NULL)
+      return;
+
+   if (png_ptr->bit_depth < 8)
+   {
+      png_ptr->transformations |= PNG_PACK;
+#     ifdef PNG_WRITE_SUPPORTED
+         png_ptr->usr_bit_depth = 8;
+#     endif
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Turn on packed pixel swapping */
+void PNGAPI
+png_set_packswap(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_packswap");
+
+   if (png_ptr == NULL)
+      return;
+
+   if (png_ptr->bit_depth < 8)
+      png_ptr->transformations |= PNG_PACKSWAP;
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+void PNGAPI
+png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
+{
+   png_debug(1, "in png_set_shift");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= PNG_SHIFT;
+   png_ptr->shift = *true_bits;
+}
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+int PNGAPI
+png_set_interlace_handling(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_interlace handling");
+
+   if (png_ptr != 0 && png_ptr->interlaced != 0)
+   {
+      png_ptr->transformations |= PNG_INTERLACE;
+      return (7);
+   }
+
+   return (1);
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte on read, or remove a filler or alpha byte on write.
+ * The filler type has changed in v0.95 to allow future 2-byte fillers
+ * for 48-bit input data, as well as to avoid problems with some compilers
+ * that don't like bytes as parameters.
+ */
+void PNGAPI
+png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_filler");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* In libpng 1.6 it is possible to determine whether this is a read or write
+    * operation and therefore to do more checking here for a valid call.
+    */
+   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
+   {
+#     ifdef PNG_READ_FILLER_SUPPORTED
+         /* On read png_set_filler is always valid, regardless of the base PNG
+          * format, because other transformations can give a format where the
+          * filler code can execute (basically an 8 or 16-bit component RGB or G
+          * format.)
+          *
+          * NOTE: usr_channels is not used by the read code!  (This has led to
+          * confusion in the past.)  The filler is only used in the read code.
+          */
+         png_ptr->filler = (png_uint_16)filler;
+#     else
+         png_app_error(png_ptr, "png_set_filler not supported on read");
+         PNG_UNUSED(filler) /* not used in the write case */
+         return;
+#     endif
+   }
+
+   else /* write */
+   {
+#     ifdef PNG_WRITE_FILLER_SUPPORTED
+         /* On write the usr_channels parameter must be set correctly at the
+          * start to record the number of channels in the app-supplied data.
+          */
+         switch (png_ptr->color_type)
+         {
+            case PNG_COLOR_TYPE_RGB:
+               png_ptr->usr_channels = 4;
+               break;
+
+            case PNG_COLOR_TYPE_GRAY:
+               if (png_ptr->bit_depth >= 8)
+               {
+                  png_ptr->usr_channels = 2;
+                  break;
+               }
+
+               else
+               {
+                  /* There simply isn't any code in libpng to strip out bits
+                   * from bytes when the components are less than a byte in
+                   * size!
+                   */
+                  png_app_error(png_ptr,
+                     "png_set_filler is invalid for low bit depth gray output");
+                  return;
+               }
+
+            default:
+               png_app_error(png_ptr,
+                  "png_set_filler: inappropriate color type");
+               return;
+         }
+#     else
+         png_app_error(png_ptr, "png_set_filler not supported on write");
+         return;
+#     endif
+   }
+
+   /* Here on success - libpng supports the operation, set the transformation
+    * and the flag to say where the filler channel is.
+    */
+   png_ptr->transformations |= PNG_FILLER;
+
+   if (filler_loc == PNG_FILLER_AFTER)
+      png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
+
+   else
+      png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
+}
+
+/* Added to libpng-1.2.7 */
+void PNGAPI
+png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_add_alpha");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_set_filler(png_ptr, filler, filler_loc);
+   /* The above may fail to do anything. */
+   if ((png_ptr->transformations & PNG_FILLER) != 0)
+      png_ptr->transformations |= PNG_ADD_ALPHA;
+}
+
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_swap_alpha(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_swap_alpha");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= PNG_SWAP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_invert_alpha(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_invert_alpha");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= PNG_INVERT_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+void PNGAPI
+png_set_invert_mono(png_structrp png_ptr)
+{
+   png_debug(1, "in png_set_invert_mono");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= PNG_INVERT_MONO;
+}
+
+/* Invert monochrome grayscale data */
+void /* PRIVATE */
+png_do_invert(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_invert");
+
+  /* This test removed from libpng version 1.0.13 and 1.2.0:
+   *   if (row_info->bit_depth == 1 &&
+   */
+   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_bytep rp = row;
+      png_size_t i;
+      png_size_t istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i++)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp++;
+      }
+   }
+
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+      row_info->bit_depth == 8)
+   {
+      png_bytep rp = row;
+      png_size_t i;
+      png_size_t istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i += 2)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp += 2;
+      }
+   }
+
+#ifdef PNG_16BIT_SUPPORTED
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+      row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_size_t i;
+      png_size_t istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i += 4)
+      {
+         *rp = (png_byte)(~(*rp));
+         *(rp + 1) = (png_byte)(~(*(rp + 1)));
+         rp += 4;
+      }
+   }
+#endif
+}
+#endif
+
+#ifdef PNG_16BIT_SUPPORTED
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swaps byte order on 16-bit depth images */
+void /* PRIVATE */
+png_do_swap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_swap");
+
+   if (row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop= row_info->width * row_info->channels;
+
+      for (i = 0; i < istop; i++, rp += 2)
+      {
+#ifdef PNG_BUILTIN_BSWAP16_SUPPORTED
+         /* Feature added to libpng-1.6.11 for testing purposes, not
+          * enabled by default.
+          */
+         *(png_uint_16*)rp = __builtin_bswap16(*(png_uint_16*)rp);
+#else
+         png_byte t = *rp;
+         *rp = *(rp + 1);
+         *(rp + 1) = t;
+#endif
+      }
+   }
+}
+#endif
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+static PNG_CONST png_byte onebppswaptable[256] = {
+   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+   0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+   0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+   0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+   0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+   0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+   0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+   0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+   0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+   0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+   0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+   0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+   0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+   0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+   0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+   0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+   0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+   0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+   0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+   0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+   0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+   0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+static PNG_CONST png_byte twobppswaptable[256] = {
+   0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
+   0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
+   0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
+   0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
+   0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
+   0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
+   0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
+   0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
+   0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
+   0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
+   0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
+   0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
+   0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
+   0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
+   0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
+   0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
+   0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
+   0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
+   0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
+   0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
+   0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
+   0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
+   0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
+   0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
+   0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
+   0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
+   0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
+   0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
+   0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
+   0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
+   0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
+   0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
+};
+
+static PNG_CONST png_byte fourbppswaptable[256] = {
+   0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
+   0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
+   0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
+   0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
+   0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
+   0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
+   0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
+   0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
+   0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
+   0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
+   0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
+   0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
+   0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
+   0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
+   0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
+   0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
+   0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
+   0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
+   0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
+   0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
+   0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
+   0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
+   0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
+   0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
+   0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
+   0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
+   0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
+   0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
+   0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
+   0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
+   0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
+   0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
+};
+
+/* Swaps pixel packing order within bytes */
+void /* PRIVATE */
+png_do_packswap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_packswap");
+
+   if (row_info->bit_depth < 8)
+   {
+      png_bytep rp;
+      png_const_bytep end, table;
+
+      end = row + row_info->rowbytes;
+
+      if (row_info->bit_depth == 1)
+         table = onebppswaptable;
+
+      else if (row_info->bit_depth == 2)
+         table = twobppswaptable;
+
+      else if (row_info->bit_depth == 4)
+         table = fourbppswaptable;
+
+      else
+         return;
+
+      for (rp = row; rp < end; rp++)
+         *rp = table[*rp];
+   }
+}
+#endif /* PACKSWAP || WRITE_PACKSWAP */
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+/* Remove a channel - this used to be 'png_do_strip_filler' but it used a
+ * somewhat weird combination of flags to determine what to do.  All the calls
+ * to png_do_strip_filler are changed in 1.5.2 to call this instead with the
+ * correct arguments.
+ *
+ * The routine isn't general - the channel must be the channel at the start or
+ * end (not in the middle) of each pixel.
+ */
+void /* PRIVATE */
+png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
+{
+   png_bytep sp = row; /* source pointer */
+   png_bytep dp = row; /* destination pointer */
+   png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
+
+   /* At the start sp will point to the first byte to copy and dp to where
+    * it is copied to.  ep always points just beyond the end of the row, so
+    * the loop simply copies (channels-1) channels until sp reaches ep.
+    *
+    * at_start:        0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.
+    *            nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.
+    */
+
+   /* GA, GX, XG cases */
+   if (row_info->channels == 2)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         if (at_start != 0) /* Skip initial filler */
+            ++sp;
+         else          /* Skip initial channel and, for sp, the filler */
+            sp += 2, ++dp;
+
+         /* For a 1 pixel wide image there is nothing to do */
+         while (sp < ep)
+            *dp++ = *sp, sp += 2;
+
+         row_info->pixel_depth = 8;
+      }
+
+      else if (row_info->bit_depth == 16)
+      {
+         if (at_start != 0) /* Skip initial filler */
+            sp += 2;
+         else          /* Skip initial channel and, for sp, the filler */
+            sp += 4, dp += 2;
+
+         while (sp < ep)
+            *dp++ = *sp++, *dp++ = *sp, sp += 3;
+
+         row_info->pixel_depth = 16;
+      }
+
+      else
+         return; /* bad bit depth */
+
+      row_info->channels = 1;
+
+      /* Finally fix the color type if it records an alpha channel */
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+         row_info->color_type = PNG_COLOR_TYPE_GRAY;
+   }
+
+   /* RGBA, RGBX, XRGB cases */
+   else if (row_info->channels == 4)
+   {
+      if (row_info->bit_depth == 8)
+      {
+         if (at_start != 0) /* Skip initial filler */
+            ++sp;
+         else          /* Skip initial channels and, for sp, the filler */
+            sp += 4, dp += 3;
+
+         /* Note that the loop adds 3 to dp and 4 to sp each time. */
+         while (sp < ep)
+            *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2;
+
+         row_info->pixel_depth = 24;
+      }
+
+      else if (row_info->bit_depth == 16)
+      {
+         if (at_start != 0) /* Skip initial filler */
+            sp += 2;
+         else          /* Skip initial channels and, for sp, the filler */
+            sp += 8, dp += 6;
+
+         while (sp < ep)
+         {
+            /* Copy 6 bytes, skip 2 */
+            *dp++ = *sp++, *dp++ = *sp++;
+            *dp++ = *sp++, *dp++ = *sp++;
+            *dp++ = *sp++, *dp++ = *sp, sp += 3;
+         }
+
+         row_info->pixel_depth = 48;
+      }
+
+      else
+         return; /* bad bit depth */
+
+      row_info->channels = 3;
+
+      /* Finally fix the color type if it records an alpha channel */
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         row_info->color_type = PNG_COLOR_TYPE_RGB;
+   }
+
+   else
+      return; /* The filler channel has gone already */
+
+   /* Fix the rowbytes value. */
+   row_info->rowbytes = dp-row;
+}
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Swaps red and blue bytes within a pixel */
+void /* PRIVATE */
+png_do_bgr(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_bgr");
+
+   if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 3)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 4)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+      }
+
+#ifdef PNG_16BIT_SUPPORTED
+      else if (row_info->bit_depth == 16)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 6)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 8)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+      }
+#endif
+   }
+}
+#endif /* READ_BGR || WRITE_BGR */
+
+#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
+    defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
+/* Added at libpng-1.5.10 */
+void /* PRIVATE */
+png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
+{
+   if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
+      png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
+   {
+      /* Calculations moved outside switch in an attempt to stop different
+       * compiler warnings.  'padding' is in *bits* within the last byte, it is
+       * an 'int' because pixel_depth becomes an 'int' in the expression below,
+       * and this calculation is used because it avoids warnings that other
+       * forms produced on either GCC or MSVC.
+       */
+      int padding = (-row_info->pixel_depth * row_info->width) & 7;
+      png_bytep rp = png_ptr->row_buf + row_info->rowbytes;
+
+      switch (row_info->bit_depth)
+      {
+         case 1:
+         {
+            /* in this case, all bytes must be 0 so we don't need
+             * to unpack the pixels except for the rightmost one.
+             */
+            for (; rp > png_ptr->row_buf; rp--)
+            {
+              if ((*rp >> padding) != 0)
+                 png_ptr->num_palette_max = 1;
+              padding = 0;
+            }
+
+            break;
+         }
+
+         case 2:
+         {
+            for (; rp > png_ptr->row_buf; rp--)
+            {
+              int i = ((*rp >> padding) & 0x03);
+
+              if (i > png_ptr->num_palette_max)
+                 png_ptr->num_palette_max = i;
+
+              i = (((*rp >> padding) >> 2) & 0x03);
+
+              if (i > png_ptr->num_palette_max)
+                 png_ptr->num_palette_max = i;
+
+              i = (((*rp >> padding) >> 4) & 0x03);
+
+              if (i > png_ptr->num_palette_max)
+                 png_ptr->num_palette_max = i;
+
+              i = (((*rp >> padding) >> 6) & 0x03);
+
+              if (i > png_ptr->num_palette_max)
+                 png_ptr->num_palette_max = i;
+
+              padding = 0;
+            }
+
+            break;
+         }
+
+         case 4:
+         {
+            for (; rp > png_ptr->row_buf; rp--)
+            {
+              int i = ((*rp >> padding) & 0x0f);
+
+              if (i > png_ptr->num_palette_max)
+                 png_ptr->num_palette_max = i;
+
+              i = (((*rp >> padding) >> 4) & 0x0f);
+
+              if (i > png_ptr->num_palette_max)
+                 png_ptr->num_palette_max = i;
+
+              padding = 0;
+            }
+
+            break;
+         }
+
+         case 8:
+         {
+            for (; rp > png_ptr->row_buf; rp--)
+            {
+               if (*rp > png_ptr->num_palette_max)
+                  png_ptr->num_palette_max = (int) *rp;
+            }
+
+            break;
+         }
+
+         default:
+            break;
+      }
+   }
+}
+#endif /* CHECK_FOR_INVALID_INDEX */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+void PNGAPI
+png_set_user_transform_info(png_structrp png_ptr, png_voidp
+   user_transform_ptr, int user_transform_depth, int user_transform_channels)
+{
+   png_debug(1, "in png_set_user_transform_info");
+
+   if (png_ptr == NULL)
+      return;
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+   if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
+      (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
+   {
+      png_app_error(png_ptr,
+            "info change after png_start_read_image or png_read_update_info");
+      return;
+   }
+#endif
+
+   png_ptr->user_transform_ptr = user_transform_ptr;
+   png_ptr->user_transform_depth = (png_byte)user_transform_depth;
+   png_ptr->user_transform_channels = (png_byte)user_transform_channels;
+}
+#endif
+
+/* This function returns a pointer to the user_transform_ptr associated with
+ * the user transform functions.  The application should free any memory
+ * associated with this pointer before png_write_destroy and png_read_destroy
+ * are called.
+ */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+png_voidp PNGAPI
+png_get_user_transform_ptr(png_const_structrp png_ptr)
+{
+   if (png_ptr == NULL)
+      return (NULL);
+
+   return png_ptr->user_transform_ptr;
+}
+#endif
+
+#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
+png_uint_32 PNGAPI
+png_get_current_row_number(png_const_structrp png_ptr)
+{
+   /* See the comments in png.h - this is the sub-image row when reading an
+    * interlaced image.
+    */
+   if (png_ptr != NULL)
+      return png_ptr->row_number;
+
+   return PNG_UINT_32_MAX; /* help the app not to fail silently */
+}
+
+png_byte PNGAPI
+png_get_current_pass_number(png_const_structrp png_ptr)
+{
+   if (png_ptr != NULL)
+      return png_ptr->pass;
+   return 8; /* invalid */
+}
+#endif /* USER_TRANSFORM_INFO */
+#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
+#endif /* READ || WRITE */
diff --git a/third_party/lpng_v163/pngwio.c b/third_party/libpng/pngwio.c
similarity index 95%
rename from third_party/lpng_v163/pngwio.c
rename to third_party/libpng/pngwio.c
index 9268c16..db76e6b 100644
--- a/third_party/lpng_v163/pngwio.c
+++ b/third_party/libpng/pngwio.c
@@ -1,163 +1,168 @@
-

-/* pngwio.c - functions for data output

- *

- * Last changed in libpng 1.6.0 [February 14, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- *

- * This file provides a location for all output.  Users who need

- * special handling are expected to write functions that have the same

- * arguments as these and perform similar functions, but that possibly

- * use different output methods.  Note that you shouldn't change these

- * functions, but rather write replacement functions and then change

- * them at run time with png_set_write_fn(...).

- */

-#include "pngpriv.h"

-

-#ifdef PNG_WRITE_SUPPORTED

-

-/* Write the data to whatever output you are using.  The default routine

- * writes to a file pointer.  Note that this routine sometimes gets called

- * with very small lengths, so you should implement some kind of simple

- * buffering if you are using unbuffered writes.  This should never be asked

- * to write more than 64K on a 16 bit machine.

- */

-

-void /* PRIVATE */

-png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length)

-{

-   /* NOTE: write_data_fn must not change the buffer! */

-   if (png_ptr->write_data_fn != NULL )

-      (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),

-         length);

-

-   else

-      png_error(png_ptr, "Call to NULL write function");

-}

-

-#ifdef PNG_STDIO_SUPPORTED

-/* This is the function that does the actual writing of data.  If you are

- * not writing to a standard C stream, you should create a replacement

- * write_data function and use it at run time with png_set_write_fn(), rather

- * than changing the library.

- */

-void PNGCBAPI

-png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)

-{

-   png_size_t check;

-

-   if (png_ptr == NULL)

-      return;

-

-   check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));

-

-   if (check != length)

-      png_error(png_ptr, "Write Error");

-}

-#endif

-

-/* This function is called to output any data pending writing (normally

- * to disk).  After png_flush is called, there should be no data pending

- * writing in any buffers.

- */

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-void /* PRIVATE */

-png_flush(png_structrp png_ptr)

-{

-   if (png_ptr->output_flush_fn != NULL)

-      (*(png_ptr->output_flush_fn))(png_ptr);

-}

-

-#  ifdef PNG_STDIO_SUPPORTED

-void PNGCBAPI

-png_default_flush(png_structp png_ptr)

-{

-   png_FILE_p io_ptr;

-

-   if (png_ptr == NULL)

-      return;

-

-   io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr));

-   fflush(io_ptr);

-}

-#  endif

-#endif

-

-/* This function allows the application to supply new output functions for

- * libpng if standard C streams aren't being used.

- *

- * This function takes as its arguments:

- * png_ptr       - pointer to a png output data structure

- * io_ptr        - pointer to user supplied structure containing info about

- *                 the output functions.  May be NULL.

- * write_data_fn - pointer to a new output function that takes as its

- *                 arguments a pointer to a png_struct, a pointer to

- *                 data to be written, and a 32-bit unsigned int that is

- *                 the number of bytes to be written.  The new write

- *                 function should call png_error(png_ptr, "Error msg")

- *                 to exit and output any fatal error messages.  May be

- *                 NULL, in which case libpng's default function will

- *                 be used.

- * flush_data_fn - pointer to a new flush function that takes as its

- *                 arguments a pointer to a png_struct.  After a call to

- *                 the flush function, there should be no data in any buffers

- *                 or pending transmission.  If the output method doesn't do

- *                 any buffering of output, a function prototype must still be

- *                 supplied although it doesn't have to do anything.  If

- *                 PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile

- *                 time, output_flush_fn will be ignored, although it must be

- *                 supplied for compatibility.  May be NULL, in which case

- *                 libpng's default function will be used, if

- *                 PNG_WRITE_FLUSH_SUPPORTED is defined.  This is not

- *                 a good idea if io_ptr does not point to a standard

- *                 *FILE structure.

- */

-void PNGAPI

-png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr,

-    png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->io_ptr = io_ptr;

-

-#ifdef PNG_STDIO_SUPPORTED

-   if (write_data_fn != NULL)

-      png_ptr->write_data_fn = write_data_fn;

-

-   else

-      png_ptr->write_data_fn = png_default_write_data;

-#else

-   png_ptr->write_data_fn = write_data_fn;

-#endif

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-#  ifdef PNG_STDIO_SUPPORTED

-

-   if (output_flush_fn != NULL)

-      png_ptr->output_flush_fn = output_flush_fn;

-

-   else

-      png_ptr->output_flush_fn = png_default_flush;

-

-#  else

-   png_ptr->output_flush_fn = output_flush_fn;

-#  endif

-#endif /* PNG_WRITE_FLUSH_SUPPORTED */

-

-   /* It is an error to read while writing a png file */

-   if (png_ptr->read_data_fn != NULL)

-   {

-      png_ptr->read_data_fn = NULL;

-

-      png_warning(png_ptr,

-          "Can't set both read_data_fn and write_data_fn in the"

-          " same structure");

-   }

-}

-#endif /* PNG_WRITE_SUPPORTED */

+
+/* pngwio.c - functions for data output
+ *
+ * Last changed in libpng 1.6.15 [November 20, 2014]
+ * Copyright (c) 1998-2014 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all output.  Users who need
+ * special handling are expected to write functions that have the same
+ * arguments as these and perform similar functions, but that possibly
+ * use different output methods.  Note that you shouldn't change these
+ * functions, but rather write replacement functions and then change
+ * them at run time with png_set_write_fn(...).
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Write the data to whatever output you are using.  The default routine
+ * writes to a file pointer.  Note that this routine sometimes gets called
+ * with very small lengths, so you should implement some kind of simple
+ * buffering if you are using unbuffered writes.  This should never be asked
+ * to write more than 64K on a 16-bit machine.
+ */
+
+void /* PRIVATE */
+png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length)
+{
+   /* NOTE: write_data_fn must not change the buffer! */
+   if (png_ptr->write_data_fn != NULL )
+      (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),
+         length);
+
+   else
+      png_error(png_ptr, "Call to NULL write function");
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+/* This is the function that does the actual writing of data.  If you are
+ * not writing to a standard C stream, you should create a replacement
+ * write_data function and use it at run time with png_set_write_fn(), rather
+ * than changing the library.
+ */
+void PNGCBAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_size_t check;
+
+   if (png_ptr == NULL)
+      return;
+
+   check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
+
+   if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+#endif
+
+/* This function is called to output any data pending writing (normally
+ * to disk).  After png_flush is called, there should be no data pending
+ * writing in any buffers.
+ */
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+void /* PRIVATE */
+png_flush(png_structrp png_ptr)
+{
+   if (png_ptr->output_flush_fn != NULL)
+      (*(png_ptr->output_flush_fn))(png_ptr);
+}
+
+#  ifdef PNG_STDIO_SUPPORTED
+void PNGCBAPI
+png_default_flush(png_structp png_ptr)
+{
+   png_FILE_p io_ptr;
+
+   if (png_ptr == NULL)
+      return;
+
+   io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr));
+   fflush(io_ptr);
+}
+#  endif
+#endif
+
+/* This function allows the application to supply new output functions for
+ * libpng if standard C streams aren't being used.
+ *
+ * This function takes as its arguments:
+ * png_ptr       - pointer to a png output data structure
+ * io_ptr        - pointer to user supplied structure containing info about
+ *                 the output functions.  May be NULL.
+ * write_data_fn - pointer to a new output function that takes as its
+ *                 arguments a pointer to a png_struct, a pointer to
+ *                 data to be written, and a 32-bit unsigned int that is
+ *                 the number of bytes to be written.  The new write
+ *                 function should call png_error(png_ptr, "Error msg")
+ *                 to exit and output any fatal error messages.  May be
+ *                 NULL, in which case libpng's default function will
+ *                 be used.
+ * flush_data_fn - pointer to a new flush function that takes as its
+ *                 arguments a pointer to a png_struct.  After a call to
+ *                 the flush function, there should be no data in any buffers
+ *                 or pending transmission.  If the output method doesn't do
+ *                 any buffering of output, a function prototype must still be
+ *                 supplied although it doesn't have to do anything.  If
+ *                 PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
+ *                 time, output_flush_fn will be ignored, although it must be
+ *                 supplied for compatibility.  May be NULL, in which case
+ *                 libpng's default function will be used, if
+ *                 PNG_WRITE_FLUSH_SUPPORTED is defined.  This is not
+ *                 a good idea if io_ptr does not point to a standard
+ *                 *FILE structure.
+ */
+void PNGAPI
+png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr,
+    png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->io_ptr = io_ptr;
+
+#ifdef PNG_STDIO_SUPPORTED
+   if (write_data_fn != NULL)
+      png_ptr->write_data_fn = write_data_fn;
+
+   else
+      png_ptr->write_data_fn = png_default_write_data;
+#else
+   png_ptr->write_data_fn = write_data_fn;
+#endif
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#  ifdef PNG_STDIO_SUPPORTED
+
+   if (output_flush_fn != NULL)
+      png_ptr->output_flush_fn = output_flush_fn;
+
+   else
+      png_ptr->output_flush_fn = png_default_flush;
+
+#  else
+   png_ptr->output_flush_fn = output_flush_fn;
+#  endif
+#else
+   PNG_UNUSED(output_flush_fn)
+#endif /* WRITE_FLUSH */
+
+#ifdef PNG_READ_SUPPORTED
+   /* It is an error to read while writing a png file */
+   if (png_ptr->read_data_fn != NULL)
+   {
+      png_ptr->read_data_fn = NULL;
+
+      png_warning(png_ptr,
+          "Can't set both read_data_fn and write_data_fn in the"
+          " same structure");
+   }
+#endif
+}
+#endif /* WRITE */
diff --git a/third_party/lpng_v163/pngwrite.c b/third_party/libpng/pngwrite.c
similarity index 74%
rename from third_party/lpng_v163/pngwrite.c
rename to third_party/libpng/pngwrite.c
index f48ed97..0d4ee9f 100644
--- a/third_party/lpng_v163/pngwrite.c
+++ b/third_party/libpng/pngwrite.c
@@ -1,2329 +1,2230 @@
-

-/* pngwrite.c - general routines to write a PNG file

- *

- * 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-#include "pngpriv.h"

-#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)

-#  include <errno.h>

-#endif

-

-#ifdef PNG_WRITE_SUPPORTED

-

-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED

-/* Write out all the unknown chunks for the current given location */

-static void

-write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,

-   unsigned int where)

-{

-   if (info_ptr->unknown_chunks_num)

-   {

-      png_const_unknown_chunkp up;

-

-      png_debug(5, "writing extra chunks");

-

-      for (up = info_ptr->unknown_chunks;

-           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;

-           ++up)

-         if (up->location & where)

-      {

-         /* If per-chunk unknown chunk handling is enabled use it, otherwise

-          * just write the chunks the application has set.

-          */

-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-         int keep = png_handle_as_unknown(png_ptr, up->name);

-

-         /* NOTE: this code is radically different from the read side in the

-          * matter of handling an ancillary unknown chunk.  In the read side

-          * the default behavior is to discard it, in the code below the default

-          * behavior is to write it.  Critical chunks are, however, only

-          * written if explicitly listed or if the default is set to write all

-          * unknown chunks.

-          *

-          * The default handling is also slightly weird - it is not possible to

-          * stop the writing of all unsafe-to-copy chunks!

-          *

-          * TODO: REVIEW: this would seem to be a bug.

-          */

-         if (keep != PNG_HANDLE_CHUNK_NEVER &&

-             ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ ||

-              keep == PNG_HANDLE_CHUNK_ALWAYS ||

-              (keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&

-               png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS)))

-#endif

-         {

-            /* TODO: review, what is wrong with a zero length unknown chunk? */

-            if (up->size == 0)

-               png_warning(png_ptr, "Writing zero-length unknown chunk");

-

-            png_write_chunk(png_ptr, up->name, up->data, up->size);

-         }

-      }

-   }

-}

-#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */

-

-/* Writes all the PNG information.  This is the suggested way to use the

- * library.  If you have a new chunk to add, make a function to write it,

- * and put it in the correct location here.  If you want the chunk written

- * after the image data, put it in png_write_end().  I strongly encourage

- * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing

- * the chunk, as that will keep the code from breaking if you want to just

- * write a plain PNG file.  If you have long comments, I suggest writing

- * them in png_write_end(), and compressing them.

- */

-void PNGAPI

-png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)

-{

-   png_debug(1, "in png_write_info_before_PLTE");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))

-   {

-   /* Write PNG signature */

-   png_write_sig(png_ptr);

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \

-       (png_ptr->mng_features_permitted))

-   {

-      png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");

-      png_ptr->mng_features_permitted = 0;

-   }

-#endif

-

-   /* Write IHDR information. */

-   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,

-       info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,

-       info_ptr->filter_type,

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-       info_ptr->interlace_type

-#else

-       0

-#endif

-      );

-

-   /* The rest of these check to see if the valid field has the appropriate

-    * flag set, and if it does, writes the chunk.

-    *

-    * 1.6.0: COLORSPACE support controls the writing of these chunks too, and

-    * the chunks will be written if the WRITE routine is there and information

-    * is available in the COLORSPACE.  (See png_colorspace_sync_info in png.c

-    * for where the valid flags get set.)

-    *

-    * Under certain circumstances the colorspace can be invalidated without

-    * syncing the info_struct 'valid' flags; this happens if libpng detects and

-    * error and calls png_error while the color space is being set, yet the

-    * application continues writing the PNG.  So check the 'invalid' flag here

-    * too.

-    */

-#ifdef PNG_GAMMA_SUPPORTED

-#  ifdef PNG_WRITE_gAMA_SUPPORTED

-      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&

-         (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) &&

-         (info_ptr->valid & PNG_INFO_gAMA))

-         png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);

-#  endif

-#endif

-

-#ifdef PNG_COLORSPACE_SUPPORTED

-   /* Write only one of sRGB or an ICC profile.  If a profile was supplied

-    * and it matches one of the known sRGB ones issue a warning.

-    */

-#  ifdef PNG_WRITE_iCCP_SUPPORTED

-      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&

-         (info_ptr->valid & PNG_INFO_iCCP))

-      {

-#        ifdef PNG_WRITE_sRGB_SUPPORTED

-            if (info_ptr->valid & PNG_INFO_sRGB)

-               png_app_warning(png_ptr,

-                  "profile matches sRGB but writing iCCP instead");

-#        endif

-

-         png_write_iCCP(png_ptr, info_ptr->iccp_name,

-            info_ptr->iccp_profile);

-      }

-#     ifdef PNG_WRITE_sRGB_SUPPORTED

-         else

-#     endif

-#  endif

-

-#  ifdef PNG_WRITE_sRGB_SUPPORTED

-      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&

-         (info_ptr->valid & PNG_INFO_sRGB))

-         png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);

-#  endif /* WRITE_sRGB */

-#endif /* COLORSPACE */

-

-#ifdef PNG_WRITE_sBIT_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_sBIT)

-      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);

-#endif

-

-#ifdef PNG_COLORSPACE_SUPPORTED

-#  ifdef PNG_WRITE_cHRM_SUPPORTED

-      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&

-         (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) &&

-         (info_ptr->valid & PNG_INFO_cHRM))

-         png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);

-#  endif

-#endif

-

-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED

-      write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);

-#endif

-

-      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;

-   }

-}

-

-void PNGAPI

-png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)

-{

-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)

-   int i;

-#endif

-

-   png_debug(1, "in png_write_info");

-

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   png_write_info_before_PLTE(png_ptr, info_ptr);

-

-   if (info_ptr->valid & PNG_INFO_PLTE)

-      png_write_PLTE(png_ptr, info_ptr->palette,

-          (png_uint_32)info_ptr->num_palette);

-

-   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      png_error(png_ptr, "Valid palette required for paletted images");

-

-#ifdef PNG_WRITE_tRNS_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_tRNS)

-   {

-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED

-      /* Invert the alpha channel (in tRNS) */

-      if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&

-          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         int j;

-         for (j = 0; j<(int)info_ptr->num_trans; j++)

-            info_ptr->trans_alpha[j] =

-               (png_byte)(255 - info_ptr->trans_alpha[j]);

-      }

-#endif

-      png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),

-          info_ptr->num_trans, info_ptr->color_type);

-   }

-#endif

-#ifdef PNG_WRITE_bKGD_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_bKGD)

-      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);

-#endif

-

-#ifdef PNG_WRITE_hIST_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_hIST)

-      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);

-#endif

-

-#ifdef PNG_WRITE_oFFs_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_oFFs)

-      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,

-          info_ptr->offset_unit_type);

-#endif

-

-#ifdef PNG_WRITE_pCAL_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_pCAL)

-      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,

-          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,

-          info_ptr->pcal_units, info_ptr->pcal_params);

-#endif

-

-#ifdef PNG_WRITE_sCAL_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_sCAL)

-      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,

-          info_ptr->scal_s_width, info_ptr->scal_s_height);

-#endif /* sCAL */

-

-#ifdef PNG_WRITE_pHYs_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_pHYs)

-      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,

-          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);

-#endif /* pHYs */

-

-#ifdef PNG_WRITE_tIME_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_tIME)

-   {

-      png_write_tIME(png_ptr, &(info_ptr->mod_time));

-      png_ptr->mode |= PNG_WROTE_tIME;

-   }

-#endif /* tIME */

-

-#ifdef PNG_WRITE_sPLT_SUPPORTED

-   if (info_ptr->valid & PNG_INFO_sPLT)

-      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)

-         png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);

-#endif /* sPLT */

-

-#ifdef PNG_WRITE_TEXT_SUPPORTED

-   /* Check to see if we need to write text chunks */

-   for (i = 0; i < info_ptr->num_text; i++)

-   {

-      png_debug2(2, "Writing header text chunk %d, type %d", i,

-          info_ptr->text[i].compression);

-      /* An internationalized chunk? */

-      if (info_ptr->text[i].compression > 0)

-      {

-#ifdef PNG_WRITE_iTXt_SUPPORTED

-         /* Write international chunk */

-         png_write_iTXt(png_ptr,

-             info_ptr->text[i].compression,

-             info_ptr->text[i].key,

-             info_ptr->text[i].lang,

-             info_ptr->text[i].lang_key,

-             info_ptr->text[i].text);

-#else

-          png_warning(png_ptr, "Unable to write international text");

-#endif

-          /* Mark this chunk as written */

-          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;

-      }

-

-      /* If we want a compressed text chunk */

-      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)

-      {

-#ifdef PNG_WRITE_zTXt_SUPPORTED

-         /* Write compressed chunk */

-         png_write_zTXt(png_ptr, info_ptr->text[i].key,

-             info_ptr->text[i].text, 0,

-             info_ptr->text[i].compression);

-#else

-         png_warning(png_ptr, "Unable to write compressed text");

-#endif

-         /* Mark this chunk as written */

-         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;

-      }

-

-      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)

-      {

-#ifdef PNG_WRITE_tEXt_SUPPORTED

-         /* Write uncompressed chunk */

-         png_write_tEXt(png_ptr, info_ptr->text[i].key,

-             info_ptr->text[i].text,

-             0);

-         /* Mark this chunk as written */

-         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;

-#else

-         /* Can't get here */

-         png_warning(png_ptr, "Unable to write uncompressed text");

-#endif

-      }

-   }

-#endif /* tEXt */

-

-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED

-   write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);

-#endif

-}

-

-/* Writes the end of the PNG file.  If you don't want to write comments or

- * time information, you can pass NULL for info.  If you already wrote these

- * in png_write_info(), do not write them again here.  If you have long

- * comments, I suggest writing them here, and compressing them.

- */

-void PNGAPI

-png_write_end(png_structrp png_ptr, png_inforp info_ptr)

-{

-   png_debug(1, "in png_write_end");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (!(png_ptr->mode & PNG_HAVE_IDAT))

-      png_error(png_ptr, "No IDATs written into file");

-

-#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED

-   if (png_ptr->num_palette_max > png_ptr->num_palette)

-      png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");

-#endif

-

-   /* See if user wants us to write information chunks */

-   if (info_ptr != NULL)

-   {

-#ifdef PNG_WRITE_TEXT_SUPPORTED

-      int i; /* local index variable */

-#endif

-#ifdef PNG_WRITE_tIME_SUPPORTED

-      /* Check to see if user has supplied a time chunk */

-      if ((info_ptr->valid & PNG_INFO_tIME) &&

-          !(png_ptr->mode & PNG_WROTE_tIME))

-         png_write_tIME(png_ptr, &(info_ptr->mod_time));

-

-#endif

-#ifdef PNG_WRITE_TEXT_SUPPORTED

-      /* Loop through comment chunks */

-      for (i = 0; i < info_ptr->num_text; i++)

-      {

-         png_debug2(2, "Writing trailer text chunk %d, type %d", i,

-            info_ptr->text[i].compression);

-         /* An internationalized chunk? */

-         if (info_ptr->text[i].compression > 0)

-         {

-#ifdef PNG_WRITE_iTXt_SUPPORTED

-            /* Write international chunk */

-            png_write_iTXt(png_ptr,

-                info_ptr->text[i].compression,

-                info_ptr->text[i].key,

-                info_ptr->text[i].lang,

-                info_ptr->text[i].lang_key,

-                info_ptr->text[i].text);

-#else

-            png_warning(png_ptr, "Unable to write international text");

-#endif

-            /* Mark this chunk as written */

-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;

-         }

-

-         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)

-         {

-#ifdef PNG_WRITE_zTXt_SUPPORTED

-            /* Write compressed chunk */

-            png_write_zTXt(png_ptr, info_ptr->text[i].key,

-                info_ptr->text[i].text, 0,

-                info_ptr->text[i].compression);

-#else

-            png_warning(png_ptr, "Unable to write compressed text");

-#endif

-            /* Mark this chunk as written */

-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;

-         }

-

-         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)

-         {

-#ifdef PNG_WRITE_tEXt_SUPPORTED

-            /* Write uncompressed chunk */

-            png_write_tEXt(png_ptr, info_ptr->text[i].key,

-                info_ptr->text[i].text, 0);

-#else

-            png_warning(png_ptr, "Unable to write uncompressed text");

-#endif

-

-            /* Mark this chunk as written */

-            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;

-         }

-      }

-#endif

-#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED

-      write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);

-#endif

-   }

-

-   png_ptr->mode |= PNG_AFTER_IDAT;

-

-   /* Write end of PNG file */

-   png_write_IEND(png_ptr);

-   /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,

-    * and restored again in libpng-1.2.30, may cause some applications that

-    * do not set png_ptr->output_flush_fn to crash.  If your application

-    * experiences a problem, please try building libpng with

-    * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to

-    * png-mng-implement at lists.sf.net .

-    */

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-#  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED

-   png_flush(png_ptr);

-#  endif

-#endif

-}

-

-#ifdef PNG_CONVERT_tIME_SUPPORTED

-void PNGAPI

-png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)

-{

-   png_debug(1, "in png_convert_from_struct_tm");

-

-   ptime->year = (png_uint_16)(1900 + ttime->tm_year);

-   ptime->month = (png_byte)(ttime->tm_mon + 1);

-   ptime->day = (png_byte)ttime->tm_mday;

-   ptime->hour = (png_byte)ttime->tm_hour;

-   ptime->minute = (png_byte)ttime->tm_min;

-   ptime->second = (png_byte)ttime->tm_sec;

-}

-

-void PNGAPI

-png_convert_from_time_t(png_timep ptime, time_t ttime)

-{

-   struct tm *tbuf;

-

-   png_debug(1, "in png_convert_from_time_t");

-

-   tbuf = gmtime(&ttime);

-   png_convert_from_struct_tm(ptime, tbuf);

-}

-#endif

-

-/* Initialize png_ptr structure, and allocate any memory needed */

-PNG_FUNCTION(png_structp,PNGAPI

-png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,

-    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)

-{

-#ifndef PNG_USER_MEM_SUPPORTED

-   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,

-      error_fn, warn_fn, NULL, NULL, NULL);

-#else

-   return png_create_write_struct_2(user_png_ver, error_ptr, error_fn,

-       warn_fn, NULL, NULL, NULL);

-}

-

-/* Alternate initialize png_ptr structure, and allocate any memory needed */

-PNG_FUNCTION(png_structp,PNGAPI

-png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,

-    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,

-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)

-{

-   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,

-      error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);

-#endif /* PNG_USER_MEM_SUPPORTED */

-   if (png_ptr != NULL)

-   {

-      /* Set the zlib control values to defaults; they can be overridden by the

-       * application after the struct has been created.

-       */

-      png_ptr->zbuffer_size = PNG_ZBUF_SIZE;

-

-      /* The 'zlib_strategy' setting is irrelevant because png_default_claim in

-       * pngwutil.c defaults it according to whether or not filters will be

-       * used, and ignores this setting.

-       */

-      png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY;

-      png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION;

-      png_ptr->zlib_mem_level = 8;

-      png_ptr->zlib_window_bits = 15;

-      png_ptr->zlib_method = 8;

-

-#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED

-      png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;

-      png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;

-      png_ptr->zlib_text_mem_level = 8;

-      png_ptr->zlib_text_window_bits = 15;

-      png_ptr->zlib_text_method = 8;

-#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */

-

-      /* This is a highly dubious configuration option; by default it is off,

-       * but it may be appropriate for private builds that are testing

-       * extensions not conformant to the current specification, or of

-       * applications that must not fail to write at all costs!

-       */

-#ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED

-      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;

-      /* In stable builds only warn if an application error can be completely

-       * handled.

-       */

-#endif

-

-      /* App warnings are warnings in release (or release candidate) builds but

-       * are errors during development.

-       */

-#if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC

-      png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;

-#endif

-

-      /* TODO: delay this, it can be done in png_init_io() (if the app doesn't

-       * do it itself) avoiding setting the default function if it is not

-       * required.

-       */

-      png_set_write_fn(png_ptr, NULL, NULL, NULL);

-   }

-

-   return png_ptr;

-}

-

-

-/* Write a few rows of image data.  If the image is interlaced,

- * either you will have to write the 7 sub images, or, if you

- * have called png_set_interlace_handling(), you will have to

- * "write" the image seven times.

- */

-void PNGAPI

-png_write_rows(png_structrp png_ptr, png_bytepp row,

-    png_uint_32 num_rows)

-{

-   png_uint_32 i; /* row counter */

-   png_bytepp rp; /* row pointer */

-

-   png_debug(1, "in png_write_rows");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* Loop through the rows */

-   for (i = 0, rp = row; i < num_rows; i++, rp++)

-   {

-      png_write_row(png_ptr, *rp);

-   }

-}

-

-/* Write the image.  You only need to call this function once, even

- * if you are writing an interlaced image.

- */

-void PNGAPI

-png_write_image(png_structrp png_ptr, png_bytepp image)

-{

-   png_uint_32 i; /* row index */

-   int pass, num_pass; /* pass variables */

-   png_bytepp rp; /* points to current row */

-

-   if (png_ptr == NULL)

-      return;

-

-   png_debug(1, "in png_write_image");

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* Initialize interlace handling.  If image is not interlaced,

-    * this will set pass to 1

-    */

-   num_pass = png_set_interlace_handling(png_ptr);

-#else

-   num_pass = 1;

-#endif

-   /* Loop through passes */

-   for (pass = 0; pass < num_pass; pass++)

-   {

-      /* Loop through image */

-      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)

-      {

-         png_write_row(png_ptr, *rp);

-      }

-   }

-}

-

-/* Called by user to write a row of image data */

-void PNGAPI

-png_write_row(png_structrp png_ptr, png_const_bytep row)

-{

-   /* 1.5.6: moved from png_struct to be a local structure: */

-   png_row_info row_info;

-

-   if (png_ptr == NULL)

-      return;

-

-   png_debug2(1, "in png_write_row (row %u, pass %d)",

-      png_ptr->row_number, png_ptr->pass);

-

-   /* Initialize transformations and other stuff if first time */

-   if (png_ptr->row_number == 0 && png_ptr->pass == 0)

-   {

-      /* Make sure we wrote the header info */

-      if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))

-         png_error(png_ptr,

-             "png_write_info was never called before png_write_row");

-

-      /* Check for transforms that have been set but were defined out */

-#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)

-      if (png_ptr->transformations & PNG_INVERT_MONO)

-         png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");

-#endif

-

-#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)

-      if (png_ptr->transformations & PNG_FILLER)

-         png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");

-#endif

-#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \

-    defined(PNG_READ_PACKSWAP_SUPPORTED)

-      if (png_ptr->transformations & PNG_PACKSWAP)

-         png_warning(png_ptr,

-             "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");

-#endif

-

-#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)

-      if (png_ptr->transformations & PNG_PACK)

-         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");

-#endif

-

-#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)

-      if (png_ptr->transformations & PNG_SHIFT)

-         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");

-#endif

-

-#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)

-      if (png_ptr->transformations & PNG_BGR)

-         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");

-#endif

-

-#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)

-      if (png_ptr->transformations & PNG_SWAP_BYTES)

-         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");

-#endif

-

-      png_write_start_row(png_ptr);

-   }

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* If interlaced and not interested in row, return */

-   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))

-   {

-      switch (png_ptr->pass)

-      {

-         case 0:

-            if (png_ptr->row_number & 0x07)

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 1:

-            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 2:

-            if ((png_ptr->row_number & 0x07) != 4)

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 3:

-            if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 4:

-            if ((png_ptr->row_number & 0x03) != 2)

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 5:

-            if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         case 6:

-            if (!(png_ptr->row_number & 0x01))

-            {

-               png_write_finish_row(png_ptr);

-               return;

-            }

-            break;

-

-         default: /* error: ignore it */

-            break;

-      }

-   }

-#endif

-

-   /* Set up row info for transformations */

-   row_info.color_type = png_ptr->color_type;

-   row_info.width = png_ptr->usr_width;

-   row_info.channels = png_ptr->usr_channels;

-   row_info.bit_depth = png_ptr->usr_bit_depth;

-   row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);

-   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);

-

-   png_debug1(3, "row_info->color_type = %d", row_info.color_type);

-   png_debug1(3, "row_info->width = %u", row_info.width);

-   png_debug1(3, "row_info->channels = %d", row_info.channels);

-   png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);

-   png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);

-   png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);

-

-   /* Copy user's row into buffer, leaving room for filter byte. */

-   memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* Handle interlacing */

-   if (png_ptr->interlaced && png_ptr->pass < 6 &&

-       (png_ptr->transformations & PNG_INTERLACE))

-   {

-      png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);

-      /* This should always get caught above, but still ... */

-      if (!(row_info.width))

-      {

-         png_write_finish_row(png_ptr);

-         return;

-      }

-   }

-#endif

-

-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED

-   /* Handle other transformations */

-   if (png_ptr->transformations)

-      png_do_write_transformations(png_ptr, &row_info);

-#endif

-

-   /* At this point the row_info pixel depth must match the 'transformed' depth,

-    * which is also the output depth.

-    */

-   if (row_info.pixel_depth != png_ptr->pixel_depth ||

-      row_info.pixel_depth != png_ptr->transformed_pixel_depth)

-      png_error(png_ptr, "internal write transform logic error");

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   /* Write filter_method 64 (intrapixel differencing) only if

-    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and

-    * 2. Libpng did not write a PNG signature (this filter_method is only

-    *    used in PNG datastreams that are embedded in MNG datastreams) and

-    * 3. The application called png_permit_mng_features with a mask that

-    *    included PNG_FLAG_MNG_FILTER_64 and

-    * 4. The filter_method is 64 and

-    * 5. The color_type is RGB or RGBA

-    */

-   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&

-       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))

-   {

-      /* Intrapixel differencing */

-      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);

-   }

-#endif

-

-/* Added at libpng-1.5.10 */

-#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED

-   /* Check for out-of-range palette index */

-   if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&

-       png_ptr->num_palette_max >= 0)

-      png_do_check_palette_indexes(png_ptr, &row_info);

-#endif

-

-   /* Find a filter if necessary, filter the row and write it out. */

-   png_write_find_filter(png_ptr, &row_info);

-

-   if (png_ptr->write_row_fn != NULL)

-      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);

-}

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-/* Set the automatic flush interval or 0 to turn flushing off */

-void PNGAPI

-png_set_flush(png_structrp png_ptr, int nrows)

-{

-   png_debug(1, "in png_set_flush");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);

-}

-

-/* Flush the current output buffers now */

-void PNGAPI

-png_write_flush(png_structrp png_ptr)

-{

-   png_debug(1, "in png_write_flush");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* We have already written out all of the data */

-   if (png_ptr->row_number >= png_ptr->num_rows)

-      return;

-

-   png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);

-   png_ptr->flush_rows = 0;

-   png_flush(png_ptr);

-}

-#endif /* PNG_WRITE_FLUSH_SUPPORTED */

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */

-#endif

-

-/* Free any memory used in png_ptr struct without freeing the struct itself. */

-static void

-png_write_destroy(png_structrp png_ptr)

-{

-   png_debug(1, "in png_write_destroy");

-

-   /* Free any memory zlib uses */

-   if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)

-      deflateEnd(&png_ptr->zstream);

-

-   /* Free our memory.  png_free checks NULL for us. */

-   png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);

-   png_free(png_ptr, png_ptr->row_buf);

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-   png_free(png_ptr, png_ptr->prev_row);

-   png_free(png_ptr, png_ptr->sub_row);

-   png_free(png_ptr, png_ptr->up_row);

-   png_free(png_ptr, png_ptr->avg_row);

-   png_free(png_ptr, png_ptr->paeth_row);

-#endif

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-   /* Use this to save a little code space, it doesn't free the filter_costs */

-   png_reset_filter_heuristics(png_ptr);

-   png_free(png_ptr, png_ptr->filter_costs);

-   png_free(png_ptr, png_ptr->inv_filter_costs);

-#endif

-

-#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED

-   png_free(png_ptr, png_ptr->chunk_list);

-#endif

-

-   /* The error handling and memory handling information is left intact at this

-    * point: the jmp_buf may still have to be freed.  See png_destroy_png_struct

-    * for how this happens.

-    */

-}

-

-/* Free all memory used by the write.

- * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for

- * *png_ptr_ptr.  Prior to 1.6.0 it would accept such a value and it would free

- * the passed in info_structs but it would quietly fail to free any of the data

- * inside them.  In 1.6.0 it quietly does nothing (it has to be quiet because it

- * has no png_ptr.)

- */

-void PNGAPI

-png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)

-{

-   png_debug(1, "in png_destroy_write_struct");

-

-   if (png_ptr_ptr != NULL)

-   {

-      png_structrp png_ptr = *png_ptr_ptr;

-

-      if (png_ptr != NULL) /* added in libpng 1.6.0 */

-      {

-         png_destroy_info_struct(png_ptr, info_ptr_ptr);

-

-         *png_ptr_ptr = NULL;

-         png_write_destroy(png_ptr);

-         png_destroy_png_struct(png_ptr);

-      }

-   }

-}

-

-/* Allow the application to select one or more row filters to use. */

-void PNGAPI

-png_set_filter(png_structrp png_ptr, int method, int filters)

-{

-   png_debug(1, "in png_set_filter");

-

-   if (png_ptr == NULL)

-      return;

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&

-       (method == PNG_INTRAPIXEL_DIFFERENCING))

-      method = PNG_FILTER_TYPE_BASE;

-

-#endif

-   if (method == PNG_FILTER_TYPE_BASE)

-   {

-      switch (filters & (PNG_ALL_FILTERS | 0x07))

-      {

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-         case 5:

-         case 6:

-         case 7: png_app_error(png_ptr, "Unknown row filter for method 0");

-            /* FALL THROUGH */

-#endif /* PNG_WRITE_FILTER_SUPPORTED */

-         case PNG_FILTER_VALUE_NONE:

-            png_ptr->do_filter = PNG_FILTER_NONE; break;

-

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-         case PNG_FILTER_VALUE_SUB:

-            png_ptr->do_filter = PNG_FILTER_SUB; break;

-

-         case PNG_FILTER_VALUE_UP:

-            png_ptr->do_filter = PNG_FILTER_UP; break;

-

-         case PNG_FILTER_VALUE_AVG:

-            png_ptr->do_filter = PNG_FILTER_AVG; break;

-

-         case PNG_FILTER_VALUE_PAETH:

-            png_ptr->do_filter = PNG_FILTER_PAETH; break;

-

-         default:

-            png_ptr->do_filter = (png_byte)filters; break;

-#else

-         default:

-            png_app_error(png_ptr, "Unknown row filter for method 0");

-#endif /* PNG_WRITE_FILTER_SUPPORTED */

-      }

-

-      /* If we have allocated the row_buf, this means we have already started

-       * with the image and we should have allocated all of the filter buffers

-       * that have been selected.  If prev_row isn't already allocated, then

-       * it is too late to start using the filters that need it, since we

-       * will be missing the data in the previous row.  If an application

-       * wants to start and stop using particular filters during compression,

-       * it should start out with all of the filters, and then add and

-       * remove them after the start of compression.

-       */

-      if (png_ptr->row_buf != NULL)

-      {

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)

-         {

-            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,

-                (png_ptr->rowbytes + 1));

-            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;

-         }

-

-         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)

-         {

-            if (png_ptr->prev_row == NULL)

-            {

-               png_warning(png_ptr, "Can't add Up filter after starting");

-               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &

-                   ~PNG_FILTER_UP);

-            }

-

-            else

-            {

-               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,

-                   (png_ptr->rowbytes + 1));

-               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;

-            }

-         }

-

-         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)

-         {

-            if (png_ptr->prev_row == NULL)

-            {

-               png_warning(png_ptr, "Can't add Average filter after starting");

-               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &

-                   ~PNG_FILTER_AVG);

-            }

-

-            else

-            {

-               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,

-                   (png_ptr->rowbytes + 1));

-               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;

-            }

-         }

-

-         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&

-             png_ptr->paeth_row == NULL)

-         {

-            if (png_ptr->prev_row == NULL)

-            {

-               png_warning(png_ptr, "Can't add Paeth filter after starting");

-               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);

-            }

-

-            else

-            {

-               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,

-                   (png_ptr->rowbytes + 1));

-               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;

-            }

-         }

-

-         if (png_ptr->do_filter == PNG_NO_FILTERS)

-#endif /* PNG_WRITE_FILTER_SUPPORTED */

-            png_ptr->do_filter = PNG_FILTER_NONE;

-      }

-   }

-   else

-      png_error(png_ptr, "Unknown custom filter method");

-}

-

-/* This allows us to influence the way in which libpng chooses the "best"

- * filter for the current scanline.  While the "minimum-sum-of-absolute-

- * differences metric is relatively fast and effective, there is some

- * question as to whether it can be improved upon by trying to keep the

- * filtered data going to zlib more consistent, hopefully resulting in

- * better compression.

- */

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */

-/* Convenience reset API. */

-static void

-png_reset_filter_heuristics(png_structrp png_ptr)

-{

-   /* Clear out any old values in the 'weights' - this must be done because if

-    * the app calls set_filter_heuristics multiple times with different

-    * 'num_weights' values we would otherwise potentially have wrong sized

-    * arrays.

-    */

-   png_ptr->num_prev_filters = 0;

-   png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;

-   if (png_ptr->prev_filters != NULL)

-   {

-      png_bytep old = png_ptr->prev_filters;

-      png_ptr->prev_filters = NULL;

-      png_free(png_ptr, old);

-   }

-   if (png_ptr->filter_weights != NULL)

-   {

-      png_uint_16p old = png_ptr->filter_weights;

-      png_ptr->filter_weights = NULL;

-      png_free(png_ptr, old);

-   }

-

-   if (png_ptr->inv_filter_weights != NULL)

-   {

-      png_uint_16p old = png_ptr->inv_filter_weights;

-      png_ptr->inv_filter_weights = NULL;

-      png_free(png_ptr, old);

-   }

-

-   /* Leave the filter_costs - this array is fixed size. */

-}

-

-static int

-png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,

-   int num_weights)

-{

-   if (png_ptr == NULL)

-      return 0;

-

-   /* Clear out the arrays */

-   png_reset_filter_heuristics(png_ptr);

-

-   /* Check arguments; the 'reset' function makes the correct settings for the

-    * unweighted case, but we must handle the weight case by initializing the

-    * arrays for the caller.

-    */

-   if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-   {

-      int i;

-

-      if (num_weights > 0)

-      {

-         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,

-             (png_uint_32)((sizeof (png_byte)) * num_weights));

-

-         /* To make sure that the weighting starts out fairly */

-         for (i = 0; i < num_weights; i++)

-         {

-            png_ptr->prev_filters[i] = 255;

-         }

-

-         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,

-             (png_uint_32)((sizeof (png_uint_16)) * num_weights));

-

-         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,

-             (png_uint_32)((sizeof (png_uint_16)) * num_weights));

-

-         for (i = 0; i < num_weights; i++)

-         {

-            png_ptr->inv_filter_weights[i] =

-            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;

-         }

-

-         /* Safe to set this now */

-         png_ptr->num_prev_filters = (png_byte)num_weights;

-      }

-

-      /* If, in the future, there are other filter methods, this would

-       * need to be based on png_ptr->filter.

-       */

-      if (png_ptr->filter_costs == NULL)

-      {

-         png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,

-             (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));

-

-         png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,

-             (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));

-      }

-

-      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)

-      {

-         png_ptr->inv_filter_costs[i] =

-         png_ptr->filter_costs[i] = PNG_COST_FACTOR;

-      }

-

-      /* All the arrays are inited, safe to set this: */

-      png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED;

-

-      /* Return the 'ok' code. */

-      return 1;

-   }

-   else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT ||

-      heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)

-   {

-      return 1;

-   }

-   else

-   {

-      png_warning(png_ptr, "Unknown filter heuristic method");

-      return 0;

-   }

-}

-

-/* Provide floating and fixed point APIs */

-#ifdef PNG_FLOATING_POINT_SUPPORTED

-void PNGAPI

-png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,

-    int num_weights, png_const_doublep filter_weights,

-    png_const_doublep filter_costs)

-{

-   png_debug(1, "in png_set_filter_heuristics");

-

-   /* The internal API allocates all the arrays and ensures that the elements of

-    * those arrays are set to the default value.

-    */

-   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))

-      return;

-

-   /* If using the weighted method copy in the weights. */

-   if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-   {

-      int i;

-      for (i = 0; i < num_weights; i++)

-      {

-         if (filter_weights[i] <= 0.0)

-         {

-            png_ptr->inv_filter_weights[i] =

-            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;

-         }

-

-         else

-         {

-            png_ptr->inv_filter_weights[i] =

-                (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5);

-

-            png_ptr->filter_weights[i] =

-                (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5);

-         }

-      }

-

-      /* Here is where we set the relative costs of the different filters.  We

-       * should take the desired compression level into account when setting

-       * the costs, so that Paeth, for instance, has a high relative cost at low

-       * compression levels, while it has a lower relative cost at higher

-       * compression settings.  The filter types are in order of increasing

-       * relative cost, so it would be possible to do this with an algorithm.

-       */

-      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0)

-      {

-         png_ptr->inv_filter_costs[i] =

-             (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);

-

-         png_ptr->filter_costs[i] =

-             (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);

-      }

-   }

-}

-#endif /* FLOATING_POINT */

-

-#ifdef PNG_FIXED_POINT_SUPPORTED

-void PNGAPI

-png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,

-    int num_weights, png_const_fixed_point_p filter_weights,

-    png_const_fixed_point_p filter_costs)

-{

-   png_debug(1, "in png_set_filter_heuristics_fixed");

-

-   /* The internal API allocates all the arrays and ensures that the elements of

-    * those arrays are set to the default value.

-    */

-   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))

-      return;

-

-   /* If using the weighted method copy in the weights. */

-   if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-   {

-      int i;

-      for (i = 0; i < num_weights; i++)

-      {

-         if (filter_weights[i] <= 0)

-         {

-            png_ptr->inv_filter_weights[i] =

-            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;

-         }

-

-         else

-         {

-            png_ptr->inv_filter_weights[i] = (png_uint_16)

-               ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1);

-

-            png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR*

-               PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]);

-         }

-      }

-

-      /* Here is where we set the relative costs of the different filters.  We

-       * should take the desired compression level into account when setting

-       * the costs, so that Paeth, for instance, has a high relative cost at low

-       * compression levels, while it has a lower relative cost at higher

-       * compression settings.  The filter types are in order of increasing

-       * relative cost, so it would be possible to do this with an algorithm.

-       */

-      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)

-         if (filter_costs[i] >= PNG_FP_1)

-      {

-         png_uint_32 tmp;

-

-         /* Use a 32 bit unsigned temporary here because otherwise the

-          * intermediate value will be a 32 bit *signed* integer (ANSI rules)

-          * and this will get the wrong answer on division.

-          */

-         tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2);

-         tmp /= filter_costs[i];

-

-         png_ptr->inv_filter_costs[i] = (png_uint_16)tmp;

-

-         tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF;

-         tmp /= PNG_FP_1;

-

-         png_ptr->filter_costs[i] = (png_uint_16)tmp;

-      }

-   }

-}

-#endif /* FIXED_POINT */

-#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */

-

-void PNGAPI

-png_set_compression_level(png_structrp png_ptr, int level)

-{

-   png_debug(1, "in png_set_compression_level");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->zlib_level = level;

-}

-

-void PNGAPI

-png_set_compression_mem_level(png_structrp png_ptr, int mem_level)

-{

-   png_debug(1, "in png_set_compression_mem_level");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->zlib_mem_level = mem_level;

-}

-

-void PNGAPI

-png_set_compression_strategy(png_structrp png_ptr, int strategy)

-{

-   png_debug(1, "in png_set_compression_strategy");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* The flag setting here prevents the libpng dynamic selection of strategy.

-    */

-   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;

-   png_ptr->zlib_strategy = strategy;

-}

-

-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a

- * smaller value of window_bits if it can do so safely.

- */

-void PNGAPI

-png_set_compression_window_bits(png_structrp png_ptr, int window_bits)

-{

-   if (png_ptr == NULL)

-      return;

-

-   /* Prior to 1.6.0 this would warn but then set the window_bits value, this

-    * meant that negative window bits values could be selected which would cause

-    * libpng to write a non-standard PNG file with raw deflate or gzip

-    * compressed IDAT or ancillary chunks.  Such files can be read and there is

-    * no warning on read, so this seems like a very bad idea.

-    */

-   if (window_bits > 15)

-   {

-      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");

-      window_bits = 15;

-   }

-

-   else if (window_bits < 8)

-   {

-      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");

-      window_bits = 8;

-   }

-

-   png_ptr->zlib_window_bits = window_bits;

-}

-

-void PNGAPI

-png_set_compression_method(png_structrp png_ptr, int method)

-{

-   png_debug(1, "in png_set_compression_method");

-

-   if (png_ptr == NULL)

-      return;

-

-   /* This would produce an invalid PNG file if it worked, but it doesn't and

-    * deflate will fault it, so it is harmless to just warn here.

-    */

-   if (method != 8)

-      png_warning(png_ptr, "Only compression method 8 is supported by PNG");

-

-   png_ptr->zlib_method = method;

-}

-

-/* The following were added to libpng-1.5.4 */

-#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED

-void PNGAPI

-png_set_text_compression_level(png_structrp png_ptr, int level)

-{

-   png_debug(1, "in png_set_text_compression_level");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->zlib_text_level = level;

-}

-

-void PNGAPI

-png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)

-{

-   png_debug(1, "in png_set_text_compression_mem_level");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->zlib_text_mem_level = mem_level;

-}

-

-void PNGAPI

-png_set_text_compression_strategy(png_structrp png_ptr, int strategy)

-{

-   png_debug(1, "in png_set_text_compression_strategy");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->zlib_text_strategy = strategy;

-}

-

-/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a

- * smaller value of window_bits if it can do so safely.

- */

-void PNGAPI

-png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)

-{

-   if (png_ptr == NULL)

-      return;

-

-   if (window_bits > 15)

-   {

-      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");

-      window_bits = 15;

-   }

-

-   else if (window_bits < 8)

-   {

-      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");

-      window_bits = 8;

-   }

-

-   png_ptr->zlib_text_window_bits = window_bits;

-}

-

-void PNGAPI

-png_set_text_compression_method(png_structrp png_ptr, int method)

-{

-   png_debug(1, "in png_set_text_compression_method");

-

-   if (png_ptr == NULL)

-      return;

-

-   if (method != 8)

-      png_warning(png_ptr, "Only compression method 8 is supported by PNG");

-

-   png_ptr->zlib_text_method = method;

-}

-#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */

-/* end of API added to libpng-1.5.4 */

-

-void PNGAPI

-png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)

-{

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->write_row_fn = write_row_fn;

-}

-

-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED

-void PNGAPI

-png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr

-    write_user_transform_fn)

-{

-   png_debug(1, "in png_set_write_user_transform_fn");

-

-   if (png_ptr == NULL)

-      return;

-

-   png_ptr->transformations |= PNG_USER_TRANSFORM;

-   png_ptr->write_user_transform_fn = write_user_transform_fn;

-}

-#endif

-

-

-#ifdef PNG_INFO_IMAGE_SUPPORTED

-void PNGAPI

-png_write_png(png_structrp png_ptr, png_inforp info_ptr,

-    int transforms, voidp params)

-{

-   if (png_ptr == NULL || info_ptr == NULL)

-      return;

-

-   /* Write the file header information. */

-   png_write_info(png_ptr, info_ptr);

-

-   /* ------ these transformations don't touch the info structure ------- */

-

-#ifdef PNG_WRITE_INVERT_SUPPORTED

-   /* Invert monochrome pixels */

-   if (transforms & PNG_TRANSFORM_INVERT_MONO)

-      png_set_invert_mono(png_ptr);

-#endif

-

-#ifdef PNG_WRITE_SHIFT_SUPPORTED

-   /* Shift the pixels up to a legal bit depth and fill in

-    * as appropriate to correctly scale the image.

-    */

-   if ((transforms & PNG_TRANSFORM_SHIFT)

-       && (info_ptr->valid & PNG_INFO_sBIT))

-      png_set_shift(png_ptr, &info_ptr->sig_bit);

-#endif

-

-#ifdef PNG_WRITE_PACK_SUPPORTED

-   /* Pack pixels into bytes */

-   if (transforms & PNG_TRANSFORM_PACKING)

-       png_set_packing(png_ptr);

-#endif

-

-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED

-   /* Swap location of alpha bytes from ARGB to RGBA */

-   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)

-      png_set_swap_alpha(png_ptr);

-#endif

-

-#ifdef PNG_WRITE_FILLER_SUPPORTED

-   /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */

-   if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)

-      png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);

-

-   else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)

-      png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);

-#endif

-

-#ifdef PNG_WRITE_BGR_SUPPORTED

-   /* Flip BGR pixels to RGB */

-   if (transforms & PNG_TRANSFORM_BGR)

-      png_set_bgr(png_ptr);

-#endif

-

-#ifdef PNG_WRITE_SWAP_SUPPORTED

-   /* Swap bytes of 16-bit files to most significant byte first */

-   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)

-      png_set_swap(png_ptr);

-#endif

-

-#ifdef PNG_WRITE_PACKSWAP_SUPPORTED

-   /* Swap bits of 1, 2, 4 bit packed pixel formats */

-   if (transforms & PNG_TRANSFORM_PACKSWAP)

-      png_set_packswap(png_ptr);

-#endif

-

-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED

-   /* Invert the alpha channel from opacity to transparency */

-   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)

-      png_set_invert_alpha(png_ptr);

-#endif

-

-   /* ----------------------- end of transformations ------------------- */

-

-   /* Write the bits */

-   if (info_ptr->valid & PNG_INFO_IDAT)

-       png_write_image(png_ptr, info_ptr->row_pointers);

-

-   /* It is REQUIRED to call this to finish writing the rest of the file */

-   png_write_end(png_ptr, info_ptr);

-

-   PNG_UNUSED(transforms)   /* Quiet compiler warnings */

-   PNG_UNUSED(params)

-}

-#endif

-

-

-#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED

-#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */

-/* Initialize the write structure - general purpose utility. */

-static int

-png_image_write_init(png_imagep image)

-{

-   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image,

-          png_safe_error, png_safe_warning);

-

-   if (png_ptr != NULL)

-   {

-      png_infop info_ptr = png_create_info_struct(png_ptr);

-

-      if (info_ptr != NULL)

-      {

-         png_controlp control = png_voidcast(png_controlp,

-            png_malloc_warn(png_ptr, (sizeof *control)));

-

-         if (control != NULL)

-         {

-            memset(control, 0, (sizeof *control));

-

-            control->png_ptr = png_ptr;

-            control->info_ptr = info_ptr;

-            control->for_write = 1;

-

-            image->opaque = control;

-            return 1;

-         }

-

-         /* Error clean up */

-         png_destroy_info_struct(png_ptr, &info_ptr);

-      }

-

-      png_destroy_write_struct(&png_ptr, NULL);

-   }

-

-   return png_image_error(image, "png_image_write_: out of memory");

-}

-

-/* Arguments to png_image_write_main: */

-typedef struct

-{

-   /* Arguments: */

-   png_imagep      image;

-   png_const_voidp buffer;

-   png_int_32      row_stride;

-   png_const_voidp colormap;

-   int             convert_to_8bit;

-   /* Local variables: */

-   png_const_voidp first_row;

-   ptrdiff_t       row_bytes;

-   png_voidp       local_row;

-} png_image_write_control;

-

-/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to

- * do any necessary byte swapping.  The component order is defined by the

- * png_image format value.

- */

-static int

-png_write_image_16bit(png_voidp argument)

-{

-   png_image_write_control *display = png_voidcast(png_image_write_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-

-   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,

-      display->first_row);

-   png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);

-   png_uint_16p row_end;

-   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;

-   int aindex = 0;

-   png_uint_32 y = image->height;

-

-   if (image->format & PNG_FORMAT_FLAG_ALPHA)

-   {

-      if (image->format & PNG_FORMAT_FLAG_AFIRST)

-      {

-         aindex = -1;

-         ++input_row; /* To point to the first component */

-         ++output_row;

-      }

-

-      else

-         aindex = channels;

-   }

-

-   else

-      png_error(png_ptr, "png_write_image: internal call error");

-

-   /* Work out the output row end and count over this, note that the increment

-    * above to 'row' means that row_end can actually be beyond the end of the

-    * row; this is correct.

-    */

-   row_end = output_row + image->width * (channels+1);

-

-   while (y-- > 0)

-   {

-      png_const_uint_16p in_ptr = input_row;

-      png_uint_16p out_ptr = output_row;

-

-      while (out_ptr < row_end)

-      {

-         const png_uint_16 alpha = in_ptr[aindex];

-         png_uint_32 reciprocal = 0;

-         int c;

-

-         out_ptr[aindex] = alpha;

-

-         /* Calculate a reciprocal.  The correct calculation is simply

-          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this

-          * allows correct rounding by adding .5 before the shift.  'reciprocal'

-          * is only initialized when required.

-          */

-         if (alpha > 0 && alpha < 65535)

-            reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;

-

-         c = channels;

-         do /* always at least one channel */

-         {

-            png_uint_16 component = *in_ptr++;

-

-            /* The following gives 65535 for an alpha of 0, which is fine,

-             * otherwise if 0/0 is represented as some other value there is more

-             * likely to be a discontinuity which will probably damage

-             * compression when moving from a fully transparent area to a

-             * nearly transparent one.  (The assumption here is that opaque

-             * areas tend not to be 0 intensity.)

-             */

-            if (component >= alpha)

-               component = 65535;

-

-            /* component<alpha, so component/alpha is less than one and

-             * component*reciprocal is less than 2^31.

-             */

-            else if (component > 0 && alpha < 65535)

-            {

-               png_uint_32 calc = component * reciprocal;

-               calc += 16384; /* round to nearest */

-               component = (png_uint_16)(calc >> 15);

-            }

-

-            *out_ptr++ = component;

-         }

-         while (--c > 0);

-

-         /* Skip to next component (skip the intervening alpha channel) */

-         ++in_ptr;

-         ++out_ptr;

-      }

-

-      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));

-      input_row += display->row_bytes/(sizeof (png_uint_16));

-   }

-

-   return 1;

-}

-

-/* Given 16-bit input (1 to 4 channels) write 8-bit output.  If an alpha channel

- * is present it must be removed from the components, the components are then

- * written in sRGB encoding.  No components are added or removed.

- *

- * Calculate an alpha reciprocal to reverse pre-multiplication.  As above the

- * calculation can be done to 15 bits of accuracy; however, the output needs to

- * be scaled in the range 0..255*65535, so include that scaling here.

- */

-#define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha)

-

-static png_byte

-png_unpremultiply(png_uint_32 component, png_uint_32 alpha,

-   png_uint_32 reciprocal/*from the above macro*/)

-{

-   /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0

-    * is represented as some other value there is more likely to be a

-    * discontinuity which will probably damage compression when moving from a

-    * fully transparent area to a nearly transparent one.  (The assumption here

-    * is that opaque areas tend not to be 0 intensity.)

-    *

-    * There is a rounding problem here; if alpha is less than 128 it will end up

-    * as 0 when scaled to 8 bits.  To avoid introducing spurious colors into the

-    * output change for this too.

-    */

-   if (component >= alpha || alpha < 128)

-      return 255;

-

-   /* component<alpha, so component/alpha is less than one and

-    * component*reciprocal is less than 2^31.

-    */

-   else if (component > 0)

-   {

-      /* The test is that alpha/257 (rounded) is less than 255, the first value

-       * that becomes 255 is 65407.

-       * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore,

-       * be exact!)  [Could also test reciprocal != 0]

-       */

-      if (alpha < 65407)

-      {

-         component *= reciprocal;

-         component += 64; /* round to nearest */

-         component >>= 7;

-      }

-

-      else

-         component *= 255;

-

-      /* Convert the component to sRGB. */

-      return (png_byte)PNG_sRGB_FROM_LINEAR(component);

-   }

-

-   else

-      return 0;

-}

-

-static int

-png_write_image_8bit(png_voidp argument)

-{

-   png_image_write_control *display = png_voidcast(png_image_write_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-

-   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,

-      display->first_row);

-   png_bytep output_row = png_voidcast(png_bytep, display->local_row);

-   png_uint_32 y = image->height;

-   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;

-

-   if (image->format & PNG_FORMAT_FLAG_ALPHA)

-   {

-      png_bytep row_end;

-      int aindex;

-

-      if (image->format & PNG_FORMAT_FLAG_AFIRST)

-      {

-         aindex = -1;

-         ++input_row; /* To point to the first component */

-         ++output_row;

-      }

-

-      else

-         aindex = channels;

-

-      /* Use row_end in place of a loop counter: */

-      row_end = output_row + image->width * (channels+1);

-

-      while (y-- > 0)

-      {

-         png_const_uint_16p in_ptr = input_row;

-         png_bytep out_ptr = output_row;

-

-         while (out_ptr < row_end)

-         {

-            png_uint_16 alpha = in_ptr[aindex];

-            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);

-            png_uint_32 reciprocal = 0;

-            int c;

-

-            /* Scale and write the alpha channel. */

-            out_ptr[aindex] = alphabyte;

-

-            if (alphabyte > 0 && alphabyte < 255)

-               reciprocal = UNP_RECIPROCAL(alpha);

-

-            c = channels;

-            do /* always at least one channel */

-               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);

-            while (--c > 0);

-

-            /* Skip to next component (skip the intervening alpha channel) */

-            ++in_ptr;

-            ++out_ptr;

-         } /* while out_ptr < row_end */

-

-         png_write_row(png_ptr, png_voidcast(png_const_bytep,

-            display->local_row));

-         input_row += display->row_bytes/(sizeof (png_uint_16));

-      } /* while y */

-   }

-

-   else

-   {

-      /* No alpha channel, so the row_end really is the end of the row and it

-       * is sufficient to loop over the components one by one.

-       */

-      png_bytep row_end = output_row + image->width * channels;

-

-      while (y-- > 0)

-      {

-         png_const_uint_16p in_ptr = input_row;

-         png_bytep out_ptr = output_row;

-

-         while (out_ptr < row_end)

-         {

-            png_uint_32 component = *in_ptr++;

-

-            component *= 255;

-            *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);

-         }

-

-         png_write_row(png_ptr, output_row);

-         input_row += display->row_bytes/(sizeof (png_uint_16));

-      }

-   }

-

-   return 1;

-}

-

-static void

-png_image_set_PLTE(png_image_write_control *display)

-{

-   const png_imagep image = display->image;

-   const void *cmap = display->colormap;

-   const int entries = image->colormap_entries > 256 ? 256 :

-      (int)image->colormap_entries;

-

-   /* NOTE: the caller must check for cmap != NULL and entries != 0 */

-   const png_uint_32 format = image->format;

-   const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);

-

-#  ifdef PNG_FORMAT_BGR_SUPPORTED

-      const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&

-         (format & PNG_FORMAT_FLAG_ALPHA) != 0;

-#  else

-#     define afirst 0

-#  endif

-

-#  ifdef PNG_FORMAT_BGR_SUPPORTED

-      const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0;

-#  else

-#     define bgr 0

-#  endif

-

-   int i, num_trans;

-   png_color palette[256];

-   png_byte tRNS[256];

-

-   memset(tRNS, 255, (sizeof tRNS));

-   memset(palette, 0, (sizeof palette));

-

-   for (i=num_trans=0; i<entries; ++i)

-   {

-      /* This gets automatically converted to sRGB with reversal of the

-       * pre-multiplication if the color-map has an alpha channel.

-       */

-      if (format & PNG_FORMAT_FLAG_LINEAR)

-      {

-         png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);

-

-         entry += i * channels;

-

-         if (channels & 1) /* no alpha */

-         {

-            if (channels >= 3) /* RGB */

-            {

-               palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 *

-                  entry[(2 ^ bgr)]);

-               palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 *

-                  entry[1]);

-               palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 *

-                  entry[bgr]);

-            }

-

-            else /* Gray */

-               palette[i].blue = palette[i].red = palette[i].green =

-                  (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry);

-         }

-

-         else /* alpha */

-         {

-            png_uint_16 alpha = entry[afirst ? 0 : channels-1];

-            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);

-            png_uint_32 reciprocal = 0;

-

-            /* Calculate a reciprocal, as in the png_write_image_8bit code above

-             * this is designed to produce a value scaled to 255*65535 when

-             * divided by 128 (i.e. asr 7).

-             */

-            if (alphabyte > 0 && alphabyte < 255)

-               reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;

-

-            tRNS[i] = alphabyte;

-            if (alphabyte < 255)

-               num_trans = i+1;

-

-            if (channels >= 3) /* RGB */

-            {

-               palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],

-                  alpha, reciprocal);

-               palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,

-                  reciprocal);

-               palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,

-                  reciprocal);

-            }

-

-            else /* gray */

-               palette[i].blue = palette[i].red = palette[i].green =

-                  png_unpremultiply(entry[afirst], alpha, reciprocal);

-         }

-      }

-

-      else /* Color-map has sRGB values */

-      {

-         png_const_bytep entry = png_voidcast(png_const_bytep, cmap);

-

-         entry += i * channels;

-

-         switch (channels)

-         {

-            case 4:

-               tRNS[i] = entry[afirst ? 0 : 3];

-               if (tRNS[i] < 255)

-                  num_trans = i+1;

-               /* FALL THROUGH */

-            case 3:

-               palette[i].blue = entry[afirst + (2 ^ bgr)];

-               palette[i].green = entry[afirst + 1];

-               palette[i].red = entry[afirst + bgr];

-               break;

-

-            case 2:

-               tRNS[i] = entry[1 ^ afirst];

-               if (tRNS[i] < 255)

-                  num_trans = i+1;

-               /* FALL THROUGH */

-            case 1:

-               palette[i].blue = palette[i].red = palette[i].green =

-                  entry[afirst];

-               break;

-

-            default:

-               break;

-         }

-      }

-   }

-

-#  ifdef afirst

-#     undef afirst

-#  endif

-#  ifdef bgr

-#     undef bgr

-#  endif

-

-   png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,

-      entries);

-

-   if (num_trans > 0)

-      png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,

-         num_trans, NULL);

-

-   image->colormap_entries = entries;

-}

-

-static int

-png_image_write_main(png_voidp argument)

-{

-   png_image_write_control *display = png_voidcast(png_image_write_control*,

-      argument);

-   png_imagep image = display->image;

-   png_structrp png_ptr = image->opaque->png_ptr;

-   png_inforp info_ptr = image->opaque->info_ptr;

-   png_uint_32 format = image->format;

-

-   int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0;

-   int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */

-   int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0;

-   int write_16bit = linear && !colormap && !display->convert_to_8bit;

-

-#  ifdef PNG_BENIGN_ERRORS_SUPPORTED

-      /* Make sure we error out on any bad situation */

-      png_set_benign_errors(png_ptr, 0/*error*/);

-#  endif

-

-   /* Default the 'row_stride' parameter if required. */

-   if (display->row_stride == 0)

-      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);

-

-   /* Set the required transforms then write the rows in the correct order. */

-   if (format & PNG_FORMAT_FLAG_COLORMAP)

-   {

-      if (display->colormap != NULL && image->colormap_entries > 0)

-      {

-         png_uint_32 entries = image->colormap_entries;

-

-         png_set_IHDR(png_ptr, info_ptr, image->width, image->height,

-            entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),

-            PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,

-            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

-

-         png_image_set_PLTE(display);

-      }

-

-      else

-         png_error(image->opaque->png_ptr,

-            "no color-map for color-mapped image");

-   }

-

-   else

-      png_set_IHDR(png_ptr, info_ptr, image->width, image->height,

-         write_16bit ? 16 : 8,

-         ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +

-         ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),

-         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

-

-   /* Counter-intuitively the data transformations must be called *after*

-    * png_write_info, not before as in the read code, but the 'set' functions

-    * must still be called before.  Just set the color space information, never

-    * write an interlaced image.

-    */

-

-   if (write_16bit)

-   {

-      /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */

-      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);

-

-      if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))

-         png_set_cHRM_fixed(png_ptr, info_ptr,

-            /* color      x       y */

-            /* white */ 31270, 32900,

-            /* red   */ 64000, 33000,

-            /* green */ 30000, 60000,

-            /* blue  */ 15000,  6000

-         );

-   }

-

-   else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))

-      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

-

-   /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit

-    * space must still be gamma encoded.

-    */

-   else

-      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);

-

-   /* Write the file header. */

-   png_write_info(png_ptr, info_ptr);

-

-   /* Now set up the data transformations (*after* the header is written),

-    * remove the handled transformations from the 'format' flags for checking.

-    *

-    * First check for a little endian system if writing 16 bit files.

-    */

-   if (write_16bit)

-   {

-      PNG_CONST png_uint_16 le = 0x0001;

-

-      if (*(png_const_bytep)&le)

-         png_set_swap(png_ptr);

-   }

-

-#  ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED

-      if (format & PNG_FORMAT_FLAG_BGR)

-      {

-         if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0)

-            png_set_bgr(png_ptr);

-         format &= ~PNG_FORMAT_FLAG_BGR;

-      }

-#  endif

-

-#  ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED

-      if (format & PNG_FORMAT_FLAG_AFIRST)

-      {

-         if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0)

-            png_set_swap_alpha(png_ptr);

-         format &= ~PNG_FORMAT_FLAG_AFIRST;

-      }

-#  endif

-

-   /* If there are 16 or fewer color-map entries we wrote a lower bit depth

-    * above, but the application data is still byte packed.

-    */

-   if (colormap && image->colormap_entries <= 16)

-      png_set_packing(png_ptr);

-

-   /* That should have handled all (both) the transforms. */

-   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |

-         PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)

-      png_error(png_ptr, "png_write_image: unsupported transformation");

-

-   {

-      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);

-      ptrdiff_t row_bytes = display->row_stride;

-

-      if (linear)

-         row_bytes *= (sizeof (png_uint_16));

-

-      if (row_bytes < 0)

-         row += (image->height-1) * (-row_bytes);

-

-      display->first_row = row;

-      display->row_bytes = row_bytes;

-   }

-

-   /* Apply 'fast' options if the flag is set. */

-   if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)

-   {

-      png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS);

-      /* NOTE: determined by experiment using pngstest, this reflects some

-       * balance between the time to write the image once and the time to read

-       * it about 50 times.  The speed-up in pngstest was about 10-20% of the

-       * total (user) time on a heavily loaded system.

-       */

-      png_set_compression_level(png_ptr, 3);

-   }

-

-   /* Check for the cases that currently require a pre-transform on the row

-    * before it is written.  This only applies when the input is 16-bit and

-    * either there is an alpha channel or it is converted to 8-bit.

-    */

-   if ((linear && alpha) || (!colormap && display->convert_to_8bit))

-   {

-      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,

-         png_get_rowbytes(png_ptr, info_ptr)));

-      int result;

-

-      display->local_row = row;

-      if (write_16bit)

-         result = png_safe_execute(image, png_write_image_16bit, display);

-      else

-         result = png_safe_execute(image, png_write_image_8bit, display);

-      display->local_row = NULL;

-

-      png_free(png_ptr, row);

-

-      /* Skip the 'write_end' on error: */

-      if (!result)

-         return 0;

-   }

-

-   /* Otherwise this is the case where the input is in a format currently

-    * supported by the rest of the libpng write code; call it directly.

-    */

-   else

-   {

-      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);

-      ptrdiff_t row_bytes = display->row_bytes;

-      png_uint_32 y = image->height;

-

-      while (y-- > 0)

-      {

-         png_write_row(png_ptr, row);

-         row += row_bytes;

-      }

-   }

-

-   png_write_end(png_ptr, info_ptr);

-   return 1;

-}

-

-int PNGAPI

-png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,

-   const void *buffer, png_int_32 row_stride, const void *colormap)

-{

-   /* Write the image to the given (FILE*). */

-   if (image != NULL && image->version == PNG_IMAGE_VERSION)

-   {

-      if (file != NULL)

-      {

-         if (png_image_write_init(image))

-         {

-            png_image_write_control display;

-            int result;

-

-            /* This is slightly evil, but png_init_io doesn't do anything other

-             * than this and we haven't changed the standard IO functions so

-             * this saves a 'safe' function.

-             */

-            image->opaque->png_ptr->io_ptr = file;

-

-            memset(&display, 0, (sizeof display));

-            display.image = image;

-            display.buffer = buffer;

-            display.row_stride = row_stride;

-            display.colormap = colormap;

-            display.convert_to_8bit = convert_to_8bit;

-

-            result = png_safe_execute(image, png_image_write_main, &display);

-            png_image_free(image);

-            return result;

-         }

-

-         else

-            return 0;

-      }

-

-      else

-         return png_image_error(image,

-            "png_image_write_to_stdio: invalid argument");

-   }

-

-   else if (image != NULL)

-      return png_image_error(image,

-         "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");

-

-   else

-      return 0;

-}

-

-int PNGAPI

-png_image_write_to_file(png_imagep image, const char *file_name,

-   int convert_to_8bit, const void *buffer, png_int_32 row_stride,

-   const void *colormap)

-{

-   /* Write the image to the named file. */

-   if (image != NULL && image->version == PNG_IMAGE_VERSION)

-   {

-      if (file_name != NULL)

-      {

-         FILE *fp = fopen(file_name, "wb");

-

-         if (fp != NULL)

-         {

-            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,

-               row_stride, colormap))

-            {

-               int error; /* from fflush/fclose */

-

-               /* Make sure the file is flushed correctly. */

-               if (fflush(fp) == 0 && ferror(fp) == 0)

-               {

-                  if (fclose(fp) == 0)

-                     return 1;

-

-                  error = errno; /* from fclose */

-               }

-

-               else

-               {

-                  error = errno; /* from fflush or ferror */

-                  (void)fclose(fp);

-               }

-

-               (void)remove(file_name);

-               /* The image has already been cleaned up; this is just used to

-                * set the error (because the original write succeeded).

-                */

-               return png_image_error(image, strerror(error));

-            }

-

-            else

-            {

-               /* Clean up: just the opened file. */

-               (void)fclose(fp);

-               (void)remove(file_name);

-               return 0;

-            }

-         }

-

-         else

-            return png_image_error(image, strerror(errno));

-      }

-

-      else

-         return png_image_error(image,

-            "png_image_write_to_file: invalid argument");

-   }

-

-   else if (image != NULL)

-      return png_image_error(image,

-         "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");

-

-   else

-      return 0;

-}

-#endif /* PNG_STDIO_SUPPORTED */

-#endif /* SIMPLIFIED_WRITE */

-#endif /* PNG_WRITE_SUPPORTED */

+
+/* pngwrite.c - general routines to write a PNG file
+ *
+ * Last changed in libpng 1.6.19 [November 12, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
+#  include <errno.h>
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+/* Write out all the unknown chunks for the current given location */
+static void
+write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
+   unsigned int where)
+{
+   if (info_ptr->unknown_chunks_num != 0)
+   {
+      png_const_unknown_chunkp up;
+
+      png_debug(5, "writing extra chunks");
+
+      for (up = info_ptr->unknown_chunks;
+           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+           ++up)
+         if ((up->location & where) != 0)
+      {
+         /* If per-chunk unknown chunk handling is enabled use it, otherwise
+          * just write the chunks the application has set.
+          */
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+         int keep = png_handle_as_unknown(png_ptr, up->name);
+
+         /* NOTE: this code is radically different from the read side in the
+          * matter of handling an ancillary unknown chunk.  In the read side
+          * the default behavior is to discard it, in the code below the default
+          * behavior is to write it.  Critical chunks are, however, only
+          * written if explicitly listed or if the default is set to write all
+          * unknown chunks.
+          *
+          * The default handling is also slightly weird - it is not possible to
+          * stop the writing of all unsafe-to-copy chunks!
+          *
+          * TODO: REVIEW: this would seem to be a bug.
+          */
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+             ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ ||
+              keep == PNG_HANDLE_CHUNK_ALWAYS ||
+              (keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&
+               png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS)))
+#endif
+         {
+            /* TODO: review, what is wrong with a zero length unknown chunk? */
+            if (up->size == 0)
+               png_warning(png_ptr, "Writing zero-length unknown chunk");
+
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+      }
+   }
+}
+#endif /* WRITE_UNKNOWN_CHUNKS */
+
+/* Writes all the PNG information.  This is the suggested way to use the
+ * library.  If you have a new chunk to add, make a function to write it,
+ * and put it in the correct location here.  If you want the chunk written
+ * after the image data, put it in png_write_end().  I strongly encourage
+ * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
+ * the chunk, as that will keep the code from breaking if you want to just
+ * write a plain PNG file.  If you have long comments, I suggest writing
+ * them in png_write_end(), and compressing them.
+ */
+void PNGAPI
+png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
+{
+   png_debug(1, "in png_write_info_before_PLTE");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
+   {
+      /* Write PNG signature */
+      png_write_sig(png_ptr);
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+      if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && \
+          png_ptr->mng_features_permitted != 0)
+      {
+         png_warning(png_ptr,
+             "MNG features are not allowed in a PNG datastream");
+         png_ptr->mng_features_permitted = 0;
+      }
+#endif
+
+      /* Write IHDR information. */
+      png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+          info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
+          info_ptr->filter_type,
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+          info_ptr->interlace_type
+#else
+          0
+#endif
+         );
+
+      /* The rest of these check to see if the valid field has the appropriate
+       * flag set, and if it does, writes the chunk.
+       *
+       * 1.6.0: COLORSPACE support controls the writing of these chunks too, and
+       * the chunks will be written if the WRITE routine is there and
+       * information * is available in the COLORSPACE. (See
+       * png_colorspace_sync_info in png.c for where the valid flags get set.)
+       *
+       * Under certain circumstances the colorspace can be invalidated without
+       * syncing the info_struct 'valid' flags; this happens if libpng detects
+       * an error and calls png_error while the color space is being set, yet
+       * the application continues writing the PNG.  So check the 'invalid'
+       * flag here too.
+       */
+#ifdef PNG_GAMMA_SUPPORTED
+#  ifdef PNG_WRITE_gAMA_SUPPORTED
+      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+          (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
+          (info_ptr->valid & PNG_INFO_gAMA) != 0)
+         png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
+#  endif
+#endif
+
+#ifdef PNG_COLORSPACE_SUPPORTED
+      /* Write only one of sRGB or an ICC profile.  If a profile was supplied
+       * and it matches one of the known sRGB ones issue a warning.
+       */
+#  ifdef PNG_WRITE_iCCP_SUPPORTED
+         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+             (info_ptr->valid & PNG_INFO_iCCP) != 0)
+         {
+#    ifdef PNG_WRITE_sRGB_SUPPORTED
+               if ((info_ptr->valid & PNG_INFO_sRGB) != 0)
+                  png_app_warning(png_ptr,
+                     "profile matches sRGB but writing iCCP instead");
+#     endif
+
+            png_write_iCCP(png_ptr, info_ptr->iccp_name,
+               info_ptr->iccp_profile);
+         }
+#     ifdef PNG_WRITE_sRGB_SUPPORTED
+         else
+#     endif
+#  endif
+
+#  ifdef PNG_WRITE_sRGB_SUPPORTED
+         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+             (info_ptr->valid & PNG_INFO_sRGB) != 0)
+            png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);
+#  endif /* WRITE_sRGB */
+#endif /* COLORSPACE */
+
+#ifdef PNG_WRITE_sBIT_SUPPORTED
+         if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
+            png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
+#endif
+
+#ifdef PNG_COLORSPACE_SUPPORTED
+#  ifdef PNG_WRITE_cHRM_SUPPORTED
+         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
+             (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 &&
+             (info_ptr->valid & PNG_INFO_cHRM) != 0)
+            png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
+#  endif
+#endif
+
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+         write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);
+#endif
+
+      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
+   }
+}
+
+void PNGAPI
+png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
+{
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+   int i;
+#endif
+
+   png_debug(1, "in png_write_info");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_write_info_before_PLTE(png_ptr, info_ptr);
+
+   if ((info_ptr->valid & PNG_INFO_PLTE) != 0)
+      png_write_PLTE(png_ptr, info_ptr->palette,
+          (png_uint_32)info_ptr->num_palette);
+
+   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      png_error(png_ptr, "Valid palette required for paletted images");
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_tRNS) !=0)
+   {
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+      /* Invert the alpha channel (in tRNS) */
+      if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0 &&
+          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         int j, jend;
+
+         jend = info_ptr->num_trans;
+         if (jend > PNG_MAX_PALETTE_LENGTH)
+            jend = PNG_MAX_PALETTE_LENGTH;
+
+         for (j = 0; j<jend; ++j)
+            info_ptr->trans_alpha[j] =
+               (png_byte)(255 - info_ptr->trans_alpha[j]);
+      }
+#endif
+      png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
+          info_ptr->num_trans, info_ptr->color_type);
+   }
+#endif
+#ifdef PNG_WRITE_bKGD_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_bKGD) != 0)
+      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
+#endif
+
+#ifdef PNG_WRITE_hIST_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_hIST) != 0)
+      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
+#endif
+
+#ifdef PNG_WRITE_oFFs_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_oFFs) != 0)
+      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
+          info_ptr->offset_unit_type);
+#endif
+
+#ifdef PNG_WRITE_pCAL_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_pCAL) != 0)
+      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
+          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
+          info_ptr->pcal_units, info_ptr->pcal_params);
+#endif
+
+#ifdef PNG_WRITE_sCAL_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_sCAL) != 0)
+      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
+          info_ptr->scal_s_width, info_ptr->scal_s_height);
+#endif /* sCAL */
+
+#ifdef PNG_WRITE_pHYs_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_pHYs) != 0)
+      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
+          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
+#endif /* pHYs */
+
+#ifdef PNG_WRITE_tIME_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_tIME) != 0)
+   {
+      png_write_tIME(png_ptr, &(info_ptr->mod_time));
+      png_ptr->mode |= PNG_WROTE_tIME;
+   }
+#endif /* tIME */
+
+#ifdef PNG_WRITE_sPLT_SUPPORTED
+   if ((info_ptr->valid & PNG_INFO_sPLT) != 0)
+      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+         png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
+#endif /* sPLT */
+
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+   /* Check to see if we need to write text chunks */
+   for (i = 0; i < info_ptr->num_text; i++)
+   {
+      png_debug2(2, "Writing header text chunk %d, type %d", i,
+          info_ptr->text[i].compression);
+      /* An internationalized chunk? */
+      if (info_ptr->text[i].compression > 0)
+      {
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+         /* Write international chunk */
+         png_write_iTXt(png_ptr,
+             info_ptr->text[i].compression,
+             info_ptr->text[i].key,
+             info_ptr->text[i].lang,
+             info_ptr->text[i].lang_key,
+             info_ptr->text[i].text);
+         /* Mark this chunk as written */
+         if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+         else
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+#else
+         png_warning(png_ptr, "Unable to write international text");
+#endif
+      }
+
+      /* If we want a compressed text chunk */
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
+      {
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+         /* Write compressed chunk */
+         png_write_zTXt(png_ptr, info_ptr->text[i].key,
+             info_ptr->text[i].text, info_ptr->text[i].compression);
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+#else
+         png_warning(png_ptr, "Unable to write compressed text");
+#endif
+      }
+
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+      {
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+         /* Write uncompressed chunk */
+         png_write_tEXt(png_ptr, info_ptr->text[i].key,
+             info_ptr->text[i].text,
+             0);
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+#else
+         /* Can't get here */
+         png_warning(png_ptr, "Unable to write uncompressed text");
+#endif
+      }
+   }
+#endif /* tEXt */
+
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+   write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);
+#endif
+}
+
+/* Writes the end of the PNG file.  If you don't want to write comments or
+ * time information, you can pass NULL for info.  If you already wrote these
+ * in png_write_info(), do not write them again here.  If you have long
+ * comments, I suggest writing them here, and compressing them.
+ */
+void PNGAPI
+png_write_end(png_structrp png_ptr, png_inforp info_ptr)
+{
+   png_debug(1, "in png_write_end");
+
+   if (png_ptr == NULL)
+      return;
+
+   if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
+      png_error(png_ptr, "No IDATs written into file");
+
+#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+   if (png_ptr->num_palette_max > png_ptr->num_palette)
+      png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");
+#endif
+
+   /* See if user wants us to write information chunks */
+   if (info_ptr != NULL)
+   {
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+      int i; /* local index variable */
+#endif
+#ifdef PNG_WRITE_tIME_SUPPORTED
+      /* Check to see if user has supplied a time chunk */
+      if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
+          (png_ptr->mode & PNG_WROTE_tIME) == 0)
+         png_write_tIME(png_ptr, &(info_ptr->mod_time));
+
+#endif
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+      /* Loop through comment chunks */
+      for (i = 0; i < info_ptr->num_text; i++)
+      {
+         png_debug2(2, "Writing trailer text chunk %d, type %d", i,
+            info_ptr->text[i].compression);
+         /* An internationalized chunk? */
+         if (info_ptr->text[i].compression > 0)
+         {
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+            /* Write international chunk */
+            png_write_iTXt(png_ptr,
+                info_ptr->text[i].compression,
+                info_ptr->text[i].key,
+                info_ptr->text[i].lang,
+                info_ptr->text[i].lang_key,
+                info_ptr->text[i].text);
+            /* Mark this chunk as written */
+            if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+               info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+            else
+               info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+#else
+            png_warning(png_ptr, "Unable to write international text");
+#endif
+         }
+
+         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
+         {
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+            /* Write compressed chunk */
+            png_write_zTXt(png_ptr, info_ptr->text[i].key,
+                info_ptr->text[i].text, info_ptr->text[i].compression);
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+#else
+            png_warning(png_ptr, "Unable to write compressed text");
+#endif
+         }
+
+         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+         {
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+            /* Write uncompressed chunk */
+            png_write_tEXt(png_ptr, info_ptr->text[i].key,
+                info_ptr->text[i].text, 0);
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+#else
+            png_warning(png_ptr, "Unable to write uncompressed text");
+#endif
+         }
+      }
+#endif
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+      write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
+#endif
+   }
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+
+   /* Write end of PNG file */
+   png_write_IEND(png_ptr);
+
+   /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
+    * and restored again in libpng-1.2.30, may cause some applications that
+    * do not set png_ptr->output_flush_fn to crash.  If your application
+    * experiences a problem, please try building libpng with
+    * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
+    * png-mng-implement at lists.sf.net .
+    */
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
+   png_flush(png_ptr);
+#  endif
+#endif
+}
+
+#ifdef PNG_CONVERT_tIME_SUPPORTED
+void PNGAPI
+png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)
+{
+   png_debug(1, "in png_convert_from_struct_tm");
+
+   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
+   ptime->month = (png_byte)(ttime->tm_mon + 1);
+   ptime->day = (png_byte)ttime->tm_mday;
+   ptime->hour = (png_byte)ttime->tm_hour;
+   ptime->minute = (png_byte)ttime->tm_min;
+   ptime->second = (png_byte)ttime->tm_sec;
+}
+
+void PNGAPI
+png_convert_from_time_t(png_timep ptime, time_t ttime)
+{
+   struct tm *tbuf;
+
+   png_debug(1, "in png_convert_from_time_t");
+
+   tbuf = gmtime(&ttime);
+   png_convert_from_struct_tm(ptime, tbuf);
+}
+#endif
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
+{
+#ifndef PNG_USER_MEM_SUPPORTED
+   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
+       error_fn, warn_fn, NULL, NULL, NULL);
+#else
+   return png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
+       warn_fn, NULL, NULL, NULL);
+}
+
+/* Alternate initialize png_ptr structure, and allocate any memory needed */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+{
+   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
+       error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
+#endif /* USER_MEM */
+   if (png_ptr != NULL)
+   {
+      /* Set the zlib control values to defaults; they can be overridden by the
+       * application after the struct has been created.
+       */
+      png_ptr->zbuffer_size = PNG_ZBUF_SIZE;
+
+      /* The 'zlib_strategy' setting is irrelevant because png_default_claim in
+       * pngwutil.c defaults it according to whether or not filters will be
+       * used, and ignores this setting.
+       */
+      png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY;
+      png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION;
+      png_ptr->zlib_mem_level = 8;
+      png_ptr->zlib_window_bits = 15;
+      png_ptr->zlib_method = 8;
+
+#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+      png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;
+      png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;
+      png_ptr->zlib_text_mem_level = 8;
+      png_ptr->zlib_text_window_bits = 15;
+      png_ptr->zlib_text_method = 8;
+#endif /* WRITE_COMPRESSED_TEXT */
+
+      /* This is a highly dubious configuration option; by default it is off,
+       * but it may be appropriate for private builds that are testing
+       * extensions not conformant to the current specification, or of
+       * applications that must not fail to write at all costs!
+       */
+#ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED
+      /* In stable builds only warn if an application error can be completely
+       * handled.
+       */
+      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
+#endif
+
+      /* App warnings are warnings in release (or release candidate) builds but
+       * are errors during development.
+       */
+#if PNG_RELEASE_BUILD
+      png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
+#endif
+
+      /* TODO: delay this, it can be done in png_init_io() (if the app doesn't
+       * do it itself) avoiding setting the default function if it is not
+       * required.
+       */
+      png_set_write_fn(png_ptr, NULL, NULL, NULL);
+   }
+
+   return png_ptr;
+}
+
+
+/* Write a few rows of image data.  If the image is interlaced,
+ * either you will have to write the 7 sub images, or, if you
+ * have called png_set_interlace_handling(), you will have to
+ * "write" the image seven times.
+ */
+void PNGAPI
+png_write_rows(png_structrp png_ptr, png_bytepp row,
+    png_uint_32 num_rows)
+{
+   png_uint_32 i; /* row counter */
+   png_bytepp rp; /* row pointer */
+
+   png_debug(1, "in png_write_rows");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* Loop through the rows */
+   for (i = 0, rp = row; i < num_rows; i++, rp++)
+   {
+      png_write_row(png_ptr, *rp);
+   }
+}
+
+/* Write the image.  You only need to call this function once, even
+ * if you are writing an interlaced image.
+ */
+void PNGAPI
+png_write_image(png_structrp png_ptr, png_bytepp image)
+{
+   png_uint_32 i; /* row index */
+   int pass, num_pass; /* pass variables */
+   png_bytepp rp; /* points to current row */
+
+   if (png_ptr == NULL)
+      return;
+
+   png_debug(1, "in png_write_image");
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* Initialize interlace handling.  If image is not interlaced,
+    * this will set pass to 1
+    */
+   num_pass = png_set_interlace_handling(png_ptr);
+#else
+   num_pass = 1;
+#endif
+   /* Loop through passes */
+   for (pass = 0; pass < num_pass; pass++)
+   {
+      /* Loop through image */
+      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+      {
+         png_write_row(png_ptr, *rp);
+      }
+   }
+}
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+/* Performs intrapixel differencing  */
+static void
+png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_intrapixel");
+
+   if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      int bytes_per_pixel;
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 3;
+
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 4;
+
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            *(rp)     = (png_byte)(*rp       - *(rp + 1));
+            *(rp + 2) = (png_byte)(*(rp + 2) - *(rp + 1));
+         }
+      }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+      else if (row_info->bit_depth == 16)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 6;
+
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 8;
+
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);
+            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);
+            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);
+            png_uint_32 red  = (png_uint_32)((s0 - s1) & 0xffffL);
+            png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);
+            *(rp    ) = (png_byte)(red >> 8);
+            *(rp + 1) = (png_byte)red;
+            *(rp + 4) = (png_byte)(blue >> 8);
+            *(rp + 5) = (png_byte)blue;
+         }
+      }
+#endif /* WRITE_16BIT */
+   }
+}
+#endif /* MNG_FEATURES */
+
+/* Called by user to write a row of image data */
+void PNGAPI
+png_write_row(png_structrp png_ptr, png_const_bytep row)
+{
+   /* 1.5.6: moved from png_struct to be a local structure: */
+   png_row_info row_info;
+
+   if (png_ptr == NULL)
+      return;
+
+   png_debug2(1, "in png_write_row (row %u, pass %d)",
+      png_ptr->row_number, png_ptr->pass);
+
+   /* Initialize transformations and other stuff if first time */
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+      /* Make sure we wrote the header info */
+      if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
+         png_error(png_ptr,
+             "png_write_info was never called before png_write_row");
+
+      /* Check for transforms that have been set but were defined out */
+#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
+      if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
+         png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
+      if ((png_ptr->transformations & PNG_FILLER) != 0)
+         png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
+#endif
+#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
+    defined(PNG_READ_PACKSWAP_SUPPORTED)
+      if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
+         png_warning(png_ptr,
+             "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
+      if ((png_ptr->transformations & PNG_PACK) != 0)
+         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
+      if ((png_ptr->transformations & PNG_SHIFT) != 0)
+         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
+      if ((png_ptr->transformations & PNG_BGR) != 0)
+         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
+      if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
+         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
+#endif
+
+      png_write_start_row(png_ptr);
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* If interlaced and not interested in row, return */
+   if (png_ptr->interlaced != 0 &&
+       (png_ptr->transformations & PNG_INTERLACE) != 0)
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if ((png_ptr->row_number & 0x07) != 0)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 1:
+            if ((png_ptr->row_number & 0x07) != 0 || png_ptr->width < 5)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 2:
+            if ((png_ptr->row_number & 0x07) != 4)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 3:
+            if ((png_ptr->row_number & 0x03) != 0 || png_ptr->width < 3)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 4:
+            if ((png_ptr->row_number & 0x03) != 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 5:
+            if ((png_ptr->row_number & 0x01) != 0 || png_ptr->width < 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         case 6:
+            if ((png_ptr->row_number & 0x01) == 0)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+
+         default: /* error: ignore it */
+            break;
+      }
+   }
+#endif
+
+   /* Set up row info for transformations */
+   row_info.color_type = png_ptr->color_type;
+   row_info.width = png_ptr->usr_width;
+   row_info.channels = png_ptr->usr_channels;
+   row_info.bit_depth = png_ptr->usr_bit_depth;
+   row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);
+   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);
+
+   png_debug1(3, "row_info->color_type = %d", row_info.color_type);
+   png_debug1(3, "row_info->width = %u", row_info.width);
+   png_debug1(3, "row_info->channels = %d", row_info.channels);
+   png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);
+   png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);
+   png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);
+
+   /* Copy user's row into buffer, leaving room for filter byte. */
+   memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* Handle interlacing */
+   if (png_ptr->interlaced && png_ptr->pass < 6 &&
+       (png_ptr->transformations & PNG_INTERLACE) != 0)
+   {
+      png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
+      /* This should always get caught above, but still ... */
+      if (row_info.width == 0)
+      {
+         png_write_finish_row(png_ptr);
+         return;
+      }
+   }
+#endif
+
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+   /* Handle other transformations */
+   if (png_ptr->transformations != 0)
+      png_do_write_transformations(png_ptr, &row_info);
+#endif
+
+   /* At this point the row_info pixel depth must match the 'transformed' depth,
+    * which is also the output depth.
+    */
+   if (row_info.pixel_depth != png_ptr->pixel_depth ||
+       row_info.pixel_depth != png_ptr->transformed_pixel_depth)
+      png_error(png_ptr, "internal write transform logic error");
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   /* Write filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not write a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+   {
+      /* Intrapixel differencing */
+      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
+   }
+#endif
+
+/* Added at libpng-1.5.10 */
+#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+   /* Check for out-of-range palette index */
+   if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
+       png_ptr->num_palette_max >= 0)
+      png_do_check_palette_indexes(png_ptr, &row_info);
+#endif
+
+   /* Find a filter if necessary, filter the row and write it out. */
+   png_write_find_filter(png_ptr, &row_info);
+
+   if (png_ptr->write_row_fn != NULL)
+      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+/* Set the automatic flush interval or 0 to turn flushing off */
+void PNGAPI
+png_set_flush(png_structrp png_ptr, int nrows)
+{
+   png_debug(1, "in png_set_flush");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
+}
+
+/* Flush the current output buffers now */
+void PNGAPI
+png_write_flush(png_structrp png_ptr)
+{
+   png_debug(1, "in png_write_flush");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* We have already written out all of the data */
+   if (png_ptr->row_number >= png_ptr->num_rows)
+      return;
+
+   png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
+   png_ptr->flush_rows = 0;
+   png_flush(png_ptr);
+}
+#endif /* WRITE_FLUSH */
+
+/* Free any memory used in png_ptr struct without freeing the struct itself. */
+static void
+png_write_destroy(png_structrp png_ptr)
+{
+   png_debug(1, "in png_write_destroy");
+
+   /* Free any memory zlib uses */
+   if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
+      deflateEnd(&png_ptr->zstream);
+
+   /* Free our memory.  png_free checks NULL for us. */
+   png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
+   png_free(png_ptr, png_ptr->row_buf);
+   png_ptr->row_buf = NULL;
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+   png_free(png_ptr, png_ptr->prev_row);
+   png_free(png_ptr, png_ptr->try_row);
+   png_free(png_ptr, png_ptr->tst_row);
+   png_ptr->prev_row = NULL;
+   png_ptr->try_row = NULL;
+   png_ptr->tst_row = NULL;
+#endif
+
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+   png_free(png_ptr, png_ptr->chunk_list);
+   png_ptr->chunk_list = NULL;
+#endif
+
+   /* The error handling and memory handling information is left intact at this
+    * point: the jmp_buf may still have to be freed.  See png_destroy_png_struct
+    * for how this happens.
+    */
+}
+
+/* Free all memory used by the write.
+ * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for
+ * *png_ptr_ptr.  Prior to 1.6.0 it would accept such a value and it would free
+ * the passed in info_structs but it would quietly fail to free any of the data
+ * inside them.  In 1.6.0 it quietly does nothing (it has to be quiet because it
+ * has no png_ptr.)
+ */
+void PNGAPI
+png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
+{
+   png_debug(1, "in png_destroy_write_struct");
+
+   if (png_ptr_ptr != NULL)
+   {
+      png_structrp png_ptr = *png_ptr_ptr;
+
+      if (png_ptr != NULL) /* added in libpng 1.6.0 */
+      {
+         png_destroy_info_struct(png_ptr, info_ptr_ptr);
+
+         *png_ptr_ptr = NULL;
+         png_write_destroy(png_ptr);
+         png_destroy_png_struct(png_ptr);
+      }
+   }
+}
+
+/* Allow the application to select one or more row filters to use. */
+void PNGAPI
+png_set_filter(png_structrp png_ptr, int method, int filters)
+{
+   png_debug(1, "in png_set_filter");
+
+   if (png_ptr == NULL)
+      return;
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+       (method == PNG_INTRAPIXEL_DIFFERENCING))
+      method = PNG_FILTER_TYPE_BASE;
+
+#endif
+   if (method == PNG_FILTER_TYPE_BASE)
+   {
+      switch (filters & (PNG_ALL_FILTERS | 0x07))
+      {
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+         case 5:
+         case 6:
+         case 7: png_app_error(png_ptr, "Unknown row filter for method 0");
+            /* FALL THROUGH */
+#endif /* WRITE_FILTER */
+         case PNG_FILTER_VALUE_NONE:
+            png_ptr->do_filter = PNG_FILTER_NONE; break;
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+         case PNG_FILTER_VALUE_SUB:
+            png_ptr->do_filter = PNG_FILTER_SUB; break;
+
+         case PNG_FILTER_VALUE_UP:
+            png_ptr->do_filter = PNG_FILTER_UP; break;
+
+         case PNG_FILTER_VALUE_AVG:
+            png_ptr->do_filter = PNG_FILTER_AVG; break;
+
+         case PNG_FILTER_VALUE_PAETH:
+            png_ptr->do_filter = PNG_FILTER_PAETH; break;
+
+         default:
+            png_ptr->do_filter = (png_byte)filters; break;
+#else
+         default:
+            png_app_error(png_ptr, "Unknown row filter for method 0");
+#endif /* WRITE_FILTER */
+      }
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+      /* If we have allocated the row_buf, this means we have already started
+       * with the image and we should have allocated all of the filter buffers
+       * that have been selected.  If prev_row isn't already allocated, then
+       * it is too late to start using the filters that need it, since we
+       * will be missing the data in the previous row.  If an application
+       * wants to start and stop using particular filters during compression,
+       * it should start out with all of the filters, and then remove them
+       * or add them back after the start of compression.
+       *
+       * NOTE: this is a nasty constraint on the code, because it means that the
+       * prev_row buffer must be maintained even if there are currently no
+       * 'prev_row' requiring filters active.
+       */
+      if (png_ptr->row_buf != NULL)
+      {
+         int num_filters;
+         png_alloc_size_t buf_size;
+
+         /* Repeat the checks in png_write_start_row; 1 pixel high or wide
+          * images cannot benefit from certain filters.  If this isn't done here
+          * the check below will fire on 1 pixel high images.
+          */
+         if (png_ptr->height == 1)
+            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
+
+         if (png_ptr->width == 1)
+            filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
+
+         if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
+            && png_ptr->prev_row == NULL)
+         {
+            /* This is the error case, however it is benign - the previous row
+             * is not available so the filter can't be used.  Just warn here.
+             */
+            png_app_warning(png_ptr,
+               "png_set_filter: UP/AVG/PAETH cannot be added after start");
+            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
+         }
+
+         num_filters = 0;
+
+         if (filters & PNG_FILTER_SUB)
+            num_filters++;
+
+         if (filters & PNG_FILTER_UP)
+            num_filters++;
+
+         if (filters & PNG_FILTER_AVG)
+            num_filters++;
+
+         if (filters & PNG_FILTER_PAETH)
+            num_filters++;
+
+         /* Allocate needed row buffers if they have not already been
+          * allocated.
+          */
+         buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth,
+             png_ptr->width) + 1;
+
+         if (png_ptr->try_row == NULL)
+            png_ptr->try_row = png_voidcast(png_bytep,
+               png_malloc(png_ptr, buf_size));
+
+         if (num_filters > 1)
+         {
+            if (png_ptr->tst_row == NULL)
+               png_ptr->tst_row = png_voidcast(png_bytep,
+                  png_malloc(png_ptr, buf_size));
+         }
+      }
+      png_ptr->do_filter = (png_byte)filters;
+#endif
+   }
+   else
+      png_error(png_ptr, "Unknown custom filter method");
+}
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
+/* Provide floating and fixed point APIs */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
+    int num_weights, png_const_doublep filter_weights,
+    png_const_doublep filter_costs)
+{
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(heuristic_method)
+   PNG_UNUSED(num_weights)
+   PNG_UNUSED(filter_weights)
+   PNG_UNUSED(filter_costs)
+}
+#endif /* FLOATING_POINT */
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
+    int num_weights, png_const_fixed_point_p filter_weights,
+    png_const_fixed_point_p filter_costs)
+{
+   PNG_UNUSED(png_ptr)
+   PNG_UNUSED(heuristic_method)
+   PNG_UNUSED(num_weights)
+   PNG_UNUSED(filter_weights)
+   PNG_UNUSED(filter_costs)
+}
+#endif /* FIXED_POINT */
+#endif /* WRITE_WEIGHTED_FILTER */
+
+#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+void PNGAPI
+png_set_compression_level(png_structrp png_ptr, int level)
+{
+   png_debug(1, "in png_set_compression_level");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->zlib_level = level;
+}
+
+void PNGAPI
+png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
+{
+   png_debug(1, "in png_set_compression_mem_level");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->zlib_mem_level = mem_level;
+}
+
+void PNGAPI
+png_set_compression_strategy(png_structrp png_ptr, int strategy)
+{
+   png_debug(1, "in png_set_compression_strategy");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* The flag setting here prevents the libpng dynamic selection of strategy.
+    */
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
+   png_ptr->zlib_strategy = strategy;
+}
+
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
+void PNGAPI
+png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
+{
+   if (png_ptr == NULL)
+      return;
+
+   /* Prior to 1.6.0 this would warn but then set the window_bits value. This
+    * meant that negative window bits values could be selected that would cause
+    * libpng to write a non-standard PNG file with raw deflate or gzip
+    * compressed IDAT or ancillary chunks.  Such files can be read and there is
+    * no warning on read, so this seems like a very bad idea.
+    */
+   if (window_bits > 15)
+   {
+      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+      window_bits = 15;
+   }
+
+   else if (window_bits < 8)
+   {
+      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
+      window_bits = 8;
+   }
+
+   png_ptr->zlib_window_bits = window_bits;
+}
+
+void PNGAPI
+png_set_compression_method(png_structrp png_ptr, int method)
+{
+   png_debug(1, "in png_set_compression_method");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* This would produce an invalid PNG file if it worked, but it doesn't and
+    * deflate will fault it, so it is harmless to just warn here.
+    */
+   if (method != 8)
+      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+
+   png_ptr->zlib_method = method;
+}
+#endif /* WRITE_CUSTOMIZE_COMPRESSION */
+
+/* The following were added to libpng-1.5.4 */
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+void PNGAPI
+png_set_text_compression_level(png_structrp png_ptr, int level)
+{
+   png_debug(1, "in png_set_text_compression_level");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->zlib_text_level = level;
+}
+
+void PNGAPI
+png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
+{
+   png_debug(1, "in png_set_text_compression_mem_level");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->zlib_text_mem_level = mem_level;
+}
+
+void PNGAPI
+png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
+{
+   png_debug(1, "in png_set_text_compression_strategy");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->zlib_text_strategy = strategy;
+}
+
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
+void PNGAPI
+png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
+{
+   if (png_ptr == NULL)
+      return;
+
+   if (window_bits > 15)
+   {
+      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+      window_bits = 15;
+   }
+
+   else if (window_bits < 8)
+   {
+      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
+      window_bits = 8;
+   }
+
+   png_ptr->zlib_text_window_bits = window_bits;
+}
+
+void PNGAPI
+png_set_text_compression_method(png_structrp png_ptr, int method)
+{
+   png_debug(1, "in png_set_text_compression_method");
+
+   if (png_ptr == NULL)
+      return;
+
+   if (method != 8)
+      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+
+   png_ptr->zlib_text_method = method;
+}
+#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
+/* end of API added to libpng-1.5.4 */
+
+void PNGAPI
+png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
+{
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->write_row_fn = write_row_fn;
+}
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+void PNGAPI
+png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
+    write_user_transform_fn)
+{
+   png_debug(1, "in png_set_write_user_transform_fn");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->write_user_transform_fn = write_user_transform_fn;
+}
+#endif
+
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+void PNGAPI
+png_write_png(png_structrp png_ptr, png_inforp info_ptr,
+    int transforms, voidp params)
+{
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if ((info_ptr->valid & PNG_INFO_IDAT) == 0)
+   {
+      png_app_error(png_ptr, "no rows for png_write_image to write");
+      return;
+   }
+
+   /* Write the file header information. */
+   png_write_info(png_ptr, info_ptr);
+
+   /* ------ these transformations don't touch the info structure ------- */
+
+   /* Invert monochrome pixels */
+   if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
+#ifdef PNG_WRITE_INVERT_SUPPORTED
+      png_set_invert_mono(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported");
+#endif
+
+   /* Shift the pixels up to a legal bit depth and fill in
+    * as appropriate to correctly scale the image.
+    */
+   if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+      if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
+         png_set_shift(png_ptr, &info_ptr->sig_bit);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
+#endif
+
+   /* Pack pixels into bytes */
+   if ((transforms & PNG_TRANSFORM_PACKING) != 0)
+#ifdef PNG_WRITE_PACK_SUPPORTED
+      png_set_packing(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported");
+#endif
+
+   /* Swap location of alpha bytes from ARGB to RGBA */
+   if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+      png_set_swap_alpha(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported");
+#endif
+
+   /* Remove a filler (X) from XRGB/RGBX/AG/GA into to convert it into
+    * RGB, note that the code expects the input color type to be G or RGB; no
+    * alpha channel.
+    */
+   if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER|
+       PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0)
+   {
+#ifdef PNG_WRITE_FILLER_SUPPORTED
+      if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0)
+      {
+         if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
+            png_app_error(png_ptr,
+                "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported");
+
+         /* Continue if ignored - this is the pre-1.6.10 behavior */
+         png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
+      }
+
+      else if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
+         png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER not supported");
+#endif
+   }
+
+   /* Flip BGR pixels to RGB */
+   if ((transforms & PNG_TRANSFORM_BGR) != 0)
+#ifdef PNG_WRITE_BGR_SUPPORTED
+      png_set_bgr(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported");
+#endif
+
+   /* Swap bytes of 16-bit files to most significant byte first */
+   if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
+#ifdef PNG_WRITE_SWAP_SUPPORTED
+      png_set_swap(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported");
+#endif
+
+   /* Swap bits of 1-bit, 2-bit, 4-bit packed pixel formats */
+   if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
+#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
+      png_set_packswap(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported");
+#endif
+
+   /* Invert the alpha channel from opacity to transparency */
+   if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+      png_set_invert_alpha(png_ptr);
+#else
+      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported");
+#endif
+
+   /* ----------------------- end of transformations ------------------- */
+
+   /* Write the bits */
+   png_write_image(png_ptr, info_ptr->row_pointers);
+
+   /* It is REQUIRED to call this to finish writing the rest of the file */
+   png_write_end(png_ptr, info_ptr);
+
+   PNG_UNUSED(params)
+}
+#endif
+
+
+#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+# ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */
+/* Initialize the write structure - general purpose utility. */
+static int
+png_image_write_init(png_imagep image)
+{
+   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image,
+       png_safe_error, png_safe_warning);
+
+   if (png_ptr != NULL)
+   {
+      png_infop info_ptr = png_create_info_struct(png_ptr);
+
+      if (info_ptr != NULL)
+      {
+         png_controlp control = png_voidcast(png_controlp,
+             png_malloc_warn(png_ptr, (sizeof *control)));
+
+         if (control != NULL)
+         {
+            memset(control, 0, (sizeof *control));
+
+            control->png_ptr = png_ptr;
+            control->info_ptr = info_ptr;
+            control->for_write = 1;
+
+            image->opaque = control;
+            return 1;
+         }
+
+         /* Error clean up */
+         png_destroy_info_struct(png_ptr, &info_ptr);
+      }
+
+      png_destroy_write_struct(&png_ptr, NULL);
+   }
+
+   return png_image_error(image, "png_image_write_: out of memory");
+}
+
+/* Arguments to png_image_write_main: */
+typedef struct
+{
+   /* Arguments: */
+   png_imagep      image;
+   png_const_voidp buffer;
+   png_int_32      row_stride;
+   png_const_voidp colormap;
+   int             convert_to_8bit;
+   /* Local variables: */
+   png_const_voidp first_row;
+   ptrdiff_t       row_bytes;
+   png_voidp       local_row;
+} png_image_write_control;
+
+/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to
+ * do any necessary byte swapping.  The component order is defined by the
+ * png_image format value.
+ */
+static int
+png_write_image_16bit(png_voidp argument)
+{
+   png_image_write_control *display = png_voidcast(png_image_write_control*,
+       argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+
+   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
+       display->first_row);
+   png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);
+   png_uint_16p row_end;
+   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+   int aindex = 0;
+   png_uint_32 y = image->height;
+
+   if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+   {
+#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
+      if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
+      {
+         aindex = -1;
+         ++input_row; /* To point to the first component */
+         ++output_row;
+      }
+         else
+            aindex = channels;
+#     else
+         aindex = channels;
+#     endif
+   }
+
+   else
+      png_error(png_ptr, "png_write_image: internal call error");
+
+   /* Work out the output row end and count over this, note that the increment
+    * above to 'row' means that row_end can actually be beyond the end of the
+    * row; this is correct.
+    */
+   row_end = output_row + image->width * (channels+1);
+
+   while (y-- > 0)
+   {
+      png_const_uint_16p in_ptr = input_row;
+      png_uint_16p out_ptr = output_row;
+
+      while (out_ptr < row_end)
+      {
+         const png_uint_16 alpha = in_ptr[aindex];
+         png_uint_32 reciprocal = 0;
+         int c;
+
+         out_ptr[aindex] = alpha;
+
+         /* Calculate a reciprocal.  The correct calculation is simply
+          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this
+          * allows correct rounding by adding .5 before the shift.  'reciprocal'
+          * is only initialized when required.
+          */
+         if (alpha > 0 && alpha < 65535)
+            reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;
+
+         c = channels;
+         do /* always at least one channel */
+         {
+            png_uint_16 component = *in_ptr++;
+
+            /* The following gives 65535 for an alpha of 0, which is fine,
+             * otherwise if 0/0 is represented as some other value there is more
+             * likely to be a discontinuity which will probably damage
+             * compression when moving from a fully transparent area to a
+             * nearly transparent one.  (The assumption here is that opaque
+             * areas tend not to be 0 intensity.)
+             */
+            if (component >= alpha)
+               component = 65535;
+
+            /* component<alpha, so component/alpha is less than one and
+             * component*reciprocal is less than 2^31.
+             */
+            else if (component > 0 && alpha < 65535)
+            {
+               png_uint_32 calc = component * reciprocal;
+               calc += 16384; /* round to nearest */
+               component = (png_uint_16)(calc >> 15);
+            }
+
+            *out_ptr++ = component;
+         }
+         while (--c > 0);
+
+         /* Skip to next component (skip the intervening alpha channel) */
+         ++in_ptr;
+         ++out_ptr;
+      }
+
+      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
+      input_row += display->row_bytes/(sizeof (png_uint_16));
+   }
+
+   return 1;
+}
+
+/* Given 16-bit input (1 to 4 channels) write 8-bit output.  If an alpha channel
+ * is present it must be removed from the components, the components are then
+ * written in sRGB encoding.  No components are added or removed.
+ *
+ * Calculate an alpha reciprocal to reverse pre-multiplication.  As above the
+ * calculation can be done to 15 bits of accuracy; however, the output needs to
+ * be scaled in the range 0..255*65535, so include that scaling here.
+ */
+#   define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha)
+
+static png_byte
+png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
+   png_uint_32 reciprocal/*from the above macro*/)
+{
+   /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0
+    * is represented as some other value there is more likely to be a
+    * discontinuity which will probably damage compression when moving from a
+    * fully transparent area to a nearly transparent one.  (The assumption here
+    * is that opaque areas tend not to be 0 intensity.)
+    *
+    * There is a rounding problem here; if alpha is less than 128 it will end up
+    * as 0 when scaled to 8 bits.  To avoid introducing spurious colors into the
+    * output change for this too.
+    */
+   if (component >= alpha || alpha < 128)
+      return 255;
+
+   /* component<alpha, so component/alpha is less than one and
+    * component*reciprocal is less than 2^31.
+    */
+   else if (component > 0)
+   {
+      /* The test is that alpha/257 (rounded) is less than 255, the first value
+       * that becomes 255 is 65407.
+       * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore,
+       * be exact!)  [Could also test reciprocal != 0]
+       */
+      if (alpha < 65407)
+      {
+         component *= reciprocal;
+         component += 64; /* round to nearest */
+         component >>= 7;
+      }
+
+      else
+         component *= 255;
+
+      /* Convert the component to sRGB. */
+      return (png_byte)PNG_sRGB_FROM_LINEAR(component);
+   }
+
+   else
+      return 0;
+}
+
+static int
+png_write_image_8bit(png_voidp argument)
+{
+   png_image_write_control *display = png_voidcast(png_image_write_control*,
+       argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+
+   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
+       display->first_row);
+   png_bytep output_row = png_voidcast(png_bytep, display->local_row);
+   png_uint_32 y = image->height;
+   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+
+   if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
+   {
+      png_bytep row_end;
+      int aindex;
+
+#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
+      if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
+      {
+         aindex = -1;
+         ++input_row; /* To point to the first component */
+         ++output_row;
+      }
+
+      else
+#   endif
+      aindex = channels;
+
+      /* Use row_end in place of a loop counter: */
+      row_end = output_row + image->width * (channels+1);
+
+      while (y-- > 0)
+      {
+         png_const_uint_16p in_ptr = input_row;
+         png_bytep out_ptr = output_row;
+
+         while (out_ptr < row_end)
+         {
+            png_uint_16 alpha = in_ptr[aindex];
+            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
+            png_uint_32 reciprocal = 0;
+            int c;
+
+            /* Scale and write the alpha channel. */
+            out_ptr[aindex] = alphabyte;
+
+            if (alphabyte > 0 && alphabyte < 255)
+               reciprocal = UNP_RECIPROCAL(alpha);
+
+            c = channels;
+            do /* always at least one channel */
+               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
+            while (--c > 0);
+
+            /* Skip to next component (skip the intervening alpha channel) */
+            ++in_ptr;
+            ++out_ptr;
+         } /* while out_ptr < row_end */
+
+         png_write_row(png_ptr, png_voidcast(png_const_bytep,
+             display->local_row));
+         input_row += display->row_bytes/(sizeof (png_uint_16));
+      } /* while y */
+   }
+
+   else
+   {
+      /* No alpha channel, so the row_end really is the end of the row and it
+       * is sufficient to loop over the components one by one.
+       */
+      png_bytep row_end = output_row + image->width * channels;
+
+      while (y-- > 0)
+      {
+         png_const_uint_16p in_ptr = input_row;
+         png_bytep out_ptr = output_row;
+
+         while (out_ptr < row_end)
+         {
+            png_uint_32 component = *in_ptr++;
+
+            component *= 255;
+            *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);
+         }
+
+         png_write_row(png_ptr, output_row);
+         input_row += display->row_bytes/(sizeof (png_uint_16));
+      }
+   }
+
+   return 1;
+}
+
+static void
+png_image_set_PLTE(png_image_write_control *display)
+{
+   const png_imagep image = display->image;
+   const void *cmap = display->colormap;
+   const int entries = image->colormap_entries > 256 ? 256 :
+       (int)image->colormap_entries;
+
+   /* NOTE: the caller must check for cmap != NULL and entries != 0 */
+   const png_uint_32 format = image->format;
+   const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);
+
+#   if defined(PNG_FORMAT_BGR_SUPPORTED) &&\
+      defined(PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED)
+      const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
+          (format & PNG_FORMAT_FLAG_ALPHA) != 0;
+#   else
+#     define afirst 0
+#   endif
+
+#   ifdef PNG_FORMAT_BGR_SUPPORTED
+      const int bgr = (format & PNG_FORMAT_FLAG_BGR) != 0 ? 2 : 0;
+#   else
+#     define bgr 0
+#   endif
+
+   int i, num_trans;
+   png_color palette[256];
+   png_byte tRNS[256];
+
+   memset(tRNS, 255, (sizeof tRNS));
+   memset(palette, 0, (sizeof palette));
+
+   for (i=num_trans=0; i<entries; ++i)
+   {
+      /* This gets automatically converted to sRGB with reversal of the
+       * pre-multiplication if the color-map has an alpha channel.
+       */
+      if ((format & PNG_FORMAT_FLAG_LINEAR) != 0)
+      {
+         png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);
+
+         entry += i * channels;
+
+         if ((channels & 1) != 0) /* no alpha */
+         {
+            if (channels >= 3) /* RGB */
+            {
+               palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
+                   entry[(2 ^ bgr)]);
+               palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
+                   entry[1]);
+               palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
+                   entry[bgr]);
+            }
+
+            else /* Gray */
+               palette[i].blue = palette[i].red = palette[i].green =
+                  (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry);
+         }
+
+         else /* alpha */
+         {
+            png_uint_16 alpha = entry[afirst ? 0 : channels-1];
+            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
+            png_uint_32 reciprocal = 0;
+
+            /* Calculate a reciprocal, as in the png_write_image_8bit code above
+             * this is designed to produce a value scaled to 255*65535 when
+             * divided by 128 (i.e. asr 7).
+             */
+            if (alphabyte > 0 && alphabyte < 255)
+               reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;
+
+            tRNS[i] = alphabyte;
+            if (alphabyte < 255)
+               num_trans = i+1;
+
+            if (channels >= 3) /* RGB */
+            {
+               palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],
+                  alpha, reciprocal);
+               palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,
+                  reciprocal);
+               palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,
+                  reciprocal);
+            }
+
+            else /* gray */
+               palette[i].blue = palette[i].red = palette[i].green =
+                  png_unpremultiply(entry[afirst], alpha, reciprocal);
+         }
+      }
+
+      else /* Color-map has sRGB values */
+      {
+         png_const_bytep entry = png_voidcast(png_const_bytep, cmap);
+
+         entry += i * channels;
+
+         switch (channels)
+         {
+            case 4:
+               tRNS[i] = entry[afirst ? 0 : 3];
+               if (tRNS[i] < 255)
+                  num_trans = i+1;
+               /* FALL THROUGH */
+            case 3:
+               palette[i].blue = entry[afirst + (2 ^ bgr)];
+               palette[i].green = entry[afirst + 1];
+               palette[i].red = entry[afirst + bgr];
+               break;
+
+            case 2:
+               tRNS[i] = entry[1 ^ afirst];
+               if (tRNS[i] < 255)
+                  num_trans = i+1;
+               /* FALL THROUGH */
+            case 1:
+               palette[i].blue = palette[i].red = palette[i].green =
+                  entry[afirst];
+               break;
+
+            default:
+               break;
+         }
+      }
+   }
+
+#   ifdef afirst
+#     undef afirst
+#   endif
+#   ifdef bgr
+#     undef bgr
+#   endif
+
+   png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,
+      entries);
+
+   if (num_trans > 0)
+      png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,
+         num_trans, NULL);
+
+   image->colormap_entries = entries;
+}
+
+static int
+png_image_write_main(png_voidp argument)
+{
+   png_image_write_control *display = png_voidcast(png_image_write_control*,
+      argument);
+   png_imagep image = display->image;
+   png_structrp png_ptr = image->opaque->png_ptr;
+   png_inforp info_ptr = image->opaque->info_ptr;
+   png_uint_32 format = image->format;
+
+   /* The following four ints are actually booleans */
+   int colormap = (format & PNG_FORMAT_FLAG_COLORMAP);
+   int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */
+   int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA);
+   int write_16bit = linear && !colormap && (display->convert_to_8bit == 0);
+
+#   ifdef PNG_BENIGN_ERRORS_SUPPORTED
+      /* Make sure we error out on any bad situation */
+      png_set_benign_errors(png_ptr, 0/*error*/);
+#   endif
+
+   /* Default the 'row_stride' parameter if required. */
+   if (display->row_stride == 0)
+      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);
+
+   /* Set the required transforms then write the rows in the correct order. */
+   if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+   {
+      if (display->colormap != NULL && image->colormap_entries > 0)
+      {
+         png_uint_32 entries = image->colormap_entries;
+
+         png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
+            entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
+            PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
+            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+         png_image_set_PLTE(display);
+      }
+
+      else
+         png_error(image->opaque->png_ptr,
+            "no color-map for color-mapped image");
+   }
+
+   else
+      png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
+         write_16bit ? 16 : 8,
+         ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
+         ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
+         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+   /* Counter-intuitively the data transformations must be called *after*
+    * png_write_info, not before as in the read code, but the 'set' functions
+    * must still be called before.  Just set the color space information, never
+    * write an interlaced image.
+    */
+
+   if (write_16bit != 0)
+   {
+      /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */
+      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);
+
+      if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
+         png_set_cHRM_fixed(png_ptr, info_ptr,
+            /* color      x       y */
+            /* white */ 31270, 32900,
+            /* red   */ 64000, 33000,
+            /* green */ 30000, 60000,
+            /* blue  */ 15000,  6000
+         );
+   }
+
+   else if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
+      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
+
+   /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit
+    * space must still be gamma encoded.
+    */
+   else
+      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);
+
+   /* Write the file header. */
+   png_write_info(png_ptr, info_ptr);
+
+   /* Now set up the data transformations (*after* the header is written),
+    * remove the handled transformations from the 'format' flags for checking.
+    *
+    * First check for a little endian system if writing 16-bit files.
+    */
+   if (write_16bit != 0)
+   {
+      PNG_CONST png_uint_16 le = 0x0001;
+
+      if ((*(png_const_bytep) & le) != 0)
+         png_set_swap(png_ptr);
+   }
+
+#   ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
+      if ((format & PNG_FORMAT_FLAG_BGR) != 0)
+      {
+         if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0)
+            png_set_bgr(png_ptr);
+         format &= ~PNG_FORMAT_FLAG_BGR;
+      }
+#   endif
+
+#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
+      if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
+      {
+         if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
+            png_set_swap_alpha(png_ptr);
+         format &= ~PNG_FORMAT_FLAG_AFIRST;
+      }
+#   endif
+
+   /* If there are 16 or fewer color-map entries we wrote a lower bit depth
+    * above, but the application data is still byte packed.
+    */
+   if (colormap != 0 && image->colormap_entries <= 16)
+      png_set_packing(png_ptr);
+
+   /* That should have handled all (both) the transforms. */
+   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
+         PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
+      png_error(png_ptr, "png_write_image: unsupported transformation");
+
+   {
+      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
+      ptrdiff_t row_bytes = display->row_stride;
+
+      if (linear != 0)
+         row_bytes *= (sizeof (png_uint_16));
+
+      if (row_bytes < 0)
+         row += (image->height-1) * (-row_bytes);
+
+      display->first_row = row;
+      display->row_bytes = row_bytes;
+   }
+
+   /* Apply 'fast' options if the flag is set. */
+   if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)
+   {
+      png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS);
+      /* NOTE: determined by experiment using pngstest, this reflects some
+       * balance between the time to write the image once and the time to read
+       * it about 50 times.  The speed-up in pngstest was about 10-20% of the
+       * total (user) time on a heavily loaded system.
+       */
+#   ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+      png_set_compression_level(png_ptr, 3);
+#   endif
+   }
+
+   /* Check for the cases that currently require a pre-transform on the row
+    * before it is written.  This only applies when the input is 16-bit and
+    * either there is an alpha channel or it is converted to 8-bit.
+    */
+   if ((linear != 0 && alpha != 0 ) ||
+       (colormap == 0 && display->convert_to_8bit != 0))
+   {
+      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
+         png_get_rowbytes(png_ptr, info_ptr)));
+      int result;
+
+      display->local_row = row;
+      if (write_16bit != 0)
+         result = png_safe_execute(image, png_write_image_16bit, display);
+      else
+         result = png_safe_execute(image, png_write_image_8bit, display);
+      display->local_row = NULL;
+
+      png_free(png_ptr, row);
+
+      /* Skip the 'write_end' on error: */
+      if (result == 0)
+         return 0;
+   }
+
+   /* Otherwise this is the case where the input is in a format currently
+    * supported by the rest of the libpng write code; call it directly.
+    */
+   else
+   {
+      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
+      ptrdiff_t row_bytes = display->row_bytes;
+      png_uint_32 y = image->height;
+
+      while (y-- > 0)
+      {
+         png_write_row(png_ptr, row);
+         row += row_bytes;
+      }
+   }
+
+   png_write_end(png_ptr, info_ptr);
+   return 1;
+}
+
+int PNGAPI
+png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
+   const void *buffer, png_int_32 row_stride, const void *colormap)
+{
+   /* Write the image to the given (FILE*). */
+   if (image != NULL && image->version == PNG_IMAGE_VERSION)
+   {
+      if (file != NULL)
+      {
+         if (png_image_write_init(image) != 0)
+         {
+            png_image_write_control display;
+            int result;
+
+            /* This is slightly evil, but png_init_io doesn't do anything other
+             * than this and we haven't changed the standard IO functions so
+             * this saves a 'safe' function.
+             */
+            image->opaque->png_ptr->io_ptr = file;
+
+            memset(&display, 0, (sizeof display));
+            display.image = image;
+            display.buffer = buffer;
+            display.row_stride = row_stride;
+            display.colormap = colormap;
+            display.convert_to_8bit = convert_to_8bit;
+
+            result = png_safe_execute(image, png_image_write_main, &display);
+            png_image_free(image);
+            return result;
+         }
+
+         else
+            return 0;
+      }
+
+      else
+         return png_image_error(image,
+            "png_image_write_to_stdio: invalid argument");
+   }
+
+   else if (image != NULL)
+      return png_image_error(image,
+         "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");
+
+   else
+      return 0;
+}
+
+int PNGAPI
+png_image_write_to_file(png_imagep image, const char *file_name,
+   int convert_to_8bit, const void *buffer, png_int_32 row_stride,
+   const void *colormap)
+{
+   /* Write the image to the named file. */
+   if (image != NULL && image->version == PNG_IMAGE_VERSION)
+   {
+      if (file_name != NULL)
+      {
+         FILE *fp = fopen(file_name, "wb");
+
+         if (fp != NULL)
+         {
+            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
+               row_stride, colormap) != 0)
+            {
+               int error; /* from fflush/fclose */
+
+               /* Make sure the file is flushed correctly. */
+               if (fflush(fp) == 0 && ferror(fp) == 0)
+               {
+                  if (fclose(fp) == 0)
+                     return 1;
+
+                  error = errno; /* from fclose */
+               }
+
+               else
+               {
+                  error = errno; /* from fflush or ferror */
+                  (void)fclose(fp);
+               }
+
+               (void)remove(file_name);
+               /* The image has already been cleaned up; this is just used to
+                * set the error (because the original write succeeded).
+                */
+               return png_image_error(image, strerror(error));
+            }
+
+            else
+            {
+               /* Clean up: just the opened file. */
+               (void)fclose(fp);
+               (void)remove(file_name);
+               return 0;
+            }
+         }
+
+         else
+            return png_image_error(image, strerror(errno));
+      }
+
+      else
+         return png_image_error(image,
+            "png_image_write_to_file: invalid argument");
+   }
+
+   else if (image != NULL)
+      return png_image_error(image,
+         "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");
+
+   else
+      return 0;
+}
+# endif /* STDIO */
+#endif /* SIMPLIFIED_WRITE */
+#endif /* WRITE */
diff --git a/third_party/lpng_v163/pngwtran.c b/third_party/libpng/pngwtran.c
similarity index 81%
rename from third_party/lpng_v163/pngwtran.c
rename to third_party/libpng/pngwtran.c
index 1fc3340..5dc9491 100644
--- a/third_party/lpng_v163/pngwtran.c
+++ b/third_party/libpng/pngwtran.c
@@ -1,636 +1,576 @@
-

-/* pngwtran.c - transforms the data in a row for PNG writers

- *

- * Last changed in libpng 1.6.0 [February 14, 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-#include "pngpriv.h"

-

-#ifdef PNG_WRITE_SUPPORTED

-

-#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED

-/* Transform the data according to the user's wishes.  The order of

- * transformations is significant.

- */

-void /* PRIVATE */

-png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info)

-{

-   png_debug(1, "in png_do_write_transformations");

-

-   if (png_ptr == NULL)

-      return;

-

-#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED

-   if (png_ptr->transformations & PNG_USER_TRANSFORM)

-      if (png_ptr->write_user_transform_fn != NULL)

-         (*(png_ptr->write_user_transform_fn)) /* User write transform

-                                                 function */

-             (png_ptr,  /* png_ptr */

-             row_info,  /* row_info: */

-                /*  png_uint_32 width;       width of row */

-                /*  png_size_t rowbytes;     number of bytes in row */

-                /*  png_byte color_type;     color type of pixels */

-                /*  png_byte bit_depth;      bit depth of samples */

-                /*  png_byte channels;       number of channels (1-4) */

-                /*  png_byte pixel_depth;    bits per pixel (depth*channels) */

-             png_ptr->row_buf + 1);      /* start of pixel data for row */

-#endif

-

-#ifdef PNG_WRITE_FILLER_SUPPORTED

-   if (png_ptr->transformations & PNG_FILLER)

-      png_do_strip_channel(row_info, png_ptr->row_buf + 1,

-         !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));

-#endif

-

-#ifdef PNG_WRITE_PACKSWAP_SUPPORTED

-   if (png_ptr->transformations & PNG_PACKSWAP)

-      png_do_packswap(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_WRITE_PACK_SUPPORTED

-   if (png_ptr->transformations & PNG_PACK)

-      png_do_pack(row_info, png_ptr->row_buf + 1,

-          (png_uint_32)png_ptr->bit_depth);

-#endif

-

-#ifdef PNG_WRITE_SWAP_SUPPORTED

-   if (png_ptr->transformations & PNG_SWAP_BYTES)

-      png_do_swap(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_WRITE_SHIFT_SUPPORTED

-   if (png_ptr->transformations & PNG_SHIFT)

-      png_do_shift(row_info, png_ptr->row_buf + 1,

-          &(png_ptr->shift));

-#endif

-

-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED

-   if (png_ptr->transformations & PNG_SWAP_ALPHA)

-      png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED

-   if (png_ptr->transformations & PNG_INVERT_ALPHA)

-      png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_WRITE_BGR_SUPPORTED

-   if (png_ptr->transformations & PNG_BGR)

-      png_do_bgr(row_info, png_ptr->row_buf + 1);

-#endif

-

-#ifdef PNG_WRITE_INVERT_SUPPORTED

-   if (png_ptr->transformations & PNG_INVERT_MONO)

-      png_do_invert(row_info, png_ptr->row_buf + 1);

-#endif

-}

-

-#ifdef PNG_WRITE_PACK_SUPPORTED

-/* Pack pixels into bytes.  Pass the true bit depth in bit_depth.  The

- * row_info bit depth should be 8 (one pixel per byte).  The channels

- * should be 1 (this only happens on grayscale and paletted images).

- */

-void /* PRIVATE */

-png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)

-{

-   png_debug(1, "in png_do_pack");

-

-   if (row_info->bit_depth == 8 &&

-      row_info->channels == 1)

-   {

-      switch ((int)bit_depth)

-      {

-         case 1:

-         {

-            png_bytep sp, dp;

-            int mask, v;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            sp = row;

-            dp = row;

-            mask = 0x80;

-            v = 0;

-

-            for (i = 0; i < row_width; i++)

-            {

-               if (*sp != 0)

-                  v |= mask;

-

-               sp++;

-

-               if (mask > 1)

-                  mask >>= 1;

-

-               else

-               {

-                  mask = 0x80;

-                  *dp = (png_byte)v;

-                  dp++;

-                  v = 0;

-               }

-            }

-

-            if (mask != 0x80)

-               *dp = (png_byte)v;

-

-            break;

-         }

-

-         case 2:

-         {

-            png_bytep sp, dp;

-            int shift, v;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            sp = row;

-            dp = row;

-            shift = 6;

-            v = 0;

-

-            for (i = 0; i < row_width; i++)

-            {

-               png_byte value;

-

-               value = (png_byte)(*sp & 0x03);

-               v |= (value << shift);

-

-               if (shift == 0)

-               {

-                  shift = 6;

-                  *dp = (png_byte)v;

-                  dp++;

-                  v = 0;

-               }

-

-               else

-                  shift -= 2;

-

-               sp++;

-            }

-

-            if (shift != 6)

-               *dp = (png_byte)v;

-

-            break;

-         }

-

-         case 4:

-         {

-            png_bytep sp, dp;

-            int shift, v;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            sp = row;

-            dp = row;

-            shift = 4;

-            v = 0;

-

-            for (i = 0; i < row_width; i++)

-            {

-               png_byte value;

-

-               value = (png_byte)(*sp & 0x0f);

-               v |= (value << shift);

-

-               if (shift == 0)

-               {

-                  shift = 4;

-                  *dp = (png_byte)v;

-                  dp++;

-                  v = 0;

-               }

-

-               else

-                  shift -= 4;

-

-               sp++;

-            }

-

-            if (shift != 4)

-               *dp = (png_byte)v;

-

-            break;

-         }

-

-         default:

-            break;

-      }

-

-      row_info->bit_depth = (png_byte)bit_depth;

-      row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);

-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,

-          row_info->width);

-   }

-}

-#endif

-

-#ifdef PNG_WRITE_SHIFT_SUPPORTED

-/* Shift pixel values to take advantage of whole range.  Pass the

- * true number of bits in bit_depth.  The row should be packed

- * according to row_info->bit_depth.  Thus, if you had a row of

- * bit depth 4, but the pixels only had values from 0 to 7, you

- * would pass 3 as bit_depth, and this routine would translate the

- * data to 0 to 15.

- */

-void /* PRIVATE */

-png_do_shift(png_row_infop row_info, png_bytep row,

-    png_const_color_8p bit_depth)

-{

-   png_debug(1, "in png_do_shift");

-

-   if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)

-   {

-      int shift_start[4], shift_dec[4];

-      int channels = 0;

-

-      if (row_info->color_type & PNG_COLOR_MASK_COLOR)

-      {

-         shift_start[channels] = row_info->bit_depth - bit_depth->red;

-         shift_dec[channels] = bit_depth->red;

-         channels++;

-

-         shift_start[channels] = row_info->bit_depth - bit_depth->green;

-         shift_dec[channels] = bit_depth->green;

-         channels++;

-

-         shift_start[channels] = row_info->bit_depth - bit_depth->blue;

-         shift_dec[channels] = bit_depth->blue;

-         channels++;

-      }

-

-      else

-      {

-         shift_start[channels] = row_info->bit_depth - bit_depth->gray;

-         shift_dec[channels] = bit_depth->gray;

-         channels++;

-      }

-

-      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)

-      {

-         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;

-         shift_dec[channels] = bit_depth->alpha;

-         channels++;

-      }

-

-      /* With low row depths, could only be grayscale, so one channel */

-      if (row_info->bit_depth < 8)

-      {

-         png_bytep bp = row;

-         png_size_t i;

-         unsigned int mask;

-         png_size_t row_bytes = row_info->rowbytes;

-

-         if (bit_depth->gray == 1 && row_info->bit_depth == 2)

-            mask = 0x55;

-

-         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)

-            mask = 0x11;

-

-         else

-            mask = 0xff;

-

-         for (i = 0; i < row_bytes; i++, bp++)

-         {

-            int j;

-            unsigned int v, out;

-

-            v = *bp;

-            out = 0;

-

-            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])

-            {

-               if (j > 0)

-                  out |= v << j;

-

-               else

-                  out |= (v >> (-j)) & mask;

-            }

-

-            *bp = (png_byte)(out & 0xff);

-         }

-      }

-

-      else if (row_info->bit_depth == 8)

-      {

-         png_bytep bp = row;

-         png_uint_32 i;

-         png_uint_32 istop = channels * row_info->width;

-

-         for (i = 0; i < istop; i++, bp++)

-         {

-

-            const unsigned int c = i%channels;

-            int j;

-            unsigned int v, out;

-

-            v = *bp;

-            out = 0;

-

-            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])

-            {

-               if (j > 0)

-                  out |= v << j;

-

-               else

-                  out |= v >> (-j);

-            }

-

-            *bp = (png_byte)(out & 0xff);

-         }

-      }

-

-      else

-      {

-         png_bytep bp;

-         png_uint_32 i;

-         png_uint_32 istop = channels * row_info->width;

-

-         for (bp = row, i = 0; i < istop; i++)

-         {

-            const unsigned int c = i%channels;

-            int j;

-            unsigned int value, v;

-

-            v = png_get_uint_16(bp);

-            value = 0;

-

-            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])

-            {

-               if (j > 0)

-                  value |= v << j;

-

-               else

-                  value |= v >> (-j);

-            }

-            *bp++ = (png_byte)((value >> 8) & 0xff);

-            *bp++ = (png_byte)(value & 0xff);

-         }

-      }

-   }

-}

-#endif

-

-#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED

-void /* PRIVATE */

-png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_write_swap_alpha");

-

-   {

-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            /* This converts from ARGB to RGBA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               png_byte save = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = save;

-            }

-         }

-

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-         else

-         {

-            /* This converts from AARRGGBB to RRGGBBAA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               png_byte save[2];

-               save[0] = *(sp++);

-               save[1] = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = save[0];

-               *(dp++) = save[1];

-            }

-         }

-#endif /* PNG_WRITE_16BIT_SUPPORTED */

-      }

-

-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            /* This converts from AG to GA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               png_byte save = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = save;

-            }

-         }

-

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-         else

-         {

-            /* This converts from AAGG to GGAA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               png_byte save[2];

-               save[0] = *(sp++);

-               save[1] = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = save[0];

-               *(dp++) = save[1];

-            }

-         }

-#endif /* PNG_WRITE_16BIT_SUPPORTED */

-      }

-   }

-}

-#endif

-

-#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED

-void /* PRIVATE */

-png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_write_invert_alpha");

-

-   {

-      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            /* This inverts the alpha channel in RGBA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               /* Does nothing

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               */

-               sp+=3; dp = sp;

-               *(dp++) = (png_byte)(255 - *(sp++));

-            }

-         }

-

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-         else

-         {

-            /* This inverts the alpha channel in RRGGBBAA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               /* Does nothing

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               */

-               sp+=6; dp = sp;

-               *(dp++) = (png_byte)(255 - *(sp++));

-               *(dp++) = (png_byte)(255 - *(sp++));

-            }

-         }

-#endif /* PNG_WRITE_16BIT_SUPPORTED */

-      }

-

-      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)

-      {

-         if (row_info->bit_depth == 8)

-         {

-            /* This inverts the alpha channel in GA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               *(dp++) = *(sp++);

-               *(dp++) = (png_byte)(255 - *(sp++));

-            }

-         }

-

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-         else

-         {

-            /* This inverts the alpha channel in GGAA */

-            png_bytep sp, dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            for (i = 0, sp = dp = row; i < row_width; i++)

-            {

-               /* Does nothing

-               *(dp++) = *(sp++);

-               *(dp++) = *(sp++);

-               */

-               sp+=2; dp = sp;

-               *(dp++) = (png_byte)(255 - *(sp++));

-               *(dp++) = (png_byte)(255 - *(sp++));

-            }

-         }

-#endif /* PNG_WRITE_16BIT_SUPPORTED */

-      }

-   }

-}

-#endif

-#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */

-

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-/* Undoes intrapixel differencing  */

-void /* PRIVATE */

-png_do_write_intrapixel(png_row_infop row_info, png_bytep row)

-{

-   png_debug(1, "in png_do_write_intrapixel");

-

-   if ((row_info->color_type & PNG_COLOR_MASK_COLOR))

-   {

-      int bytes_per_pixel;

-      png_uint_32 row_width = row_info->width;

-      if (row_info->bit_depth == 8)

-      {

-         png_bytep rp;

-         png_uint_32 i;

-

-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-            bytes_per_pixel = 3;

-

-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-            bytes_per_pixel = 4;

-

-         else

-            return;

-

-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)

-         {

-            *(rp)     = (png_byte)((*rp       - *(rp + 1)) & 0xff);

-            *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff);

-         }

-      }

-

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-      else if (row_info->bit_depth == 16)

-      {

-         png_bytep rp;

-         png_uint_32 i;

-

-         if (row_info->color_type == PNG_COLOR_TYPE_RGB)

-            bytes_per_pixel = 6;

-

-         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)

-            bytes_per_pixel = 8;

-

-         else

-            return;

-

-         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)

-         {

-            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);

-            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);

-            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);

-            png_uint_32 red  = (png_uint_32)((s0 - s1) & 0xffffL);

-            png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);

-            *(rp    ) = (png_byte)((red >> 8) & 0xff);

-            *(rp + 1) = (png_byte)(red & 0xff);

-            *(rp + 4) = (png_byte)((blue >> 8) & 0xff);

-            *(rp + 5) = (png_byte)(blue & 0xff);

-         }

-      }

-#endif /* PNG_WRITE_16BIT_SUPPORTED */

-   }

-}

-#endif /* PNG_MNG_FEATURES_SUPPORTED */

-#endif /* PNG_WRITE_SUPPORTED */

+
+/* pngwtran.c - transforms the data in a row for PNG writers
+ *
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+
+#ifdef PNG_WRITE_PACK_SUPPORTED
+/* Pack pixels into bytes.  Pass the true bit depth in bit_depth.  The
+ * row_info bit depth should be 8 (one pixel per byte).  The channels
+ * should be 1 (this only happens on grayscale and paletted images).
+ */
+static void
+png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
+{
+   png_debug(1, "in png_do_pack");
+
+   if (row_info->bit_depth == 8 &&
+      row_info->channels == 1)
+   {
+      switch ((int)bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int mask, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            mask = 0x80;
+            v = 0;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (*sp != 0)
+                  v |= mask;
+
+               sp++;
+
+               if (mask > 1)
+                  mask >>= 1;
+
+               else
+               {
+                  mask = 0x80;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+            }
+
+            if (mask != 0x80)
+               *dp = (png_byte)v;
+
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp, dp;
+            unsigned int shift;
+            int v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 6;
+            v = 0;
+
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x03);
+               v |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+
+               else
+                  shift -= 2;
+
+               sp++;
+            }
+
+            if (shift != 6)
+               *dp = (png_byte)v;
+
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp, dp;
+            unsigned int shift;
+            int v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 4;
+            v = 0;
+
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x0f);
+               v |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+
+               else
+                  shift -= 4;
+
+               sp++;
+            }
+
+            if (shift != 4)
+               *dp = (png_byte)v;
+
+            break;
+         }
+
+         default:
+            break;
+      }
+
+      row_info->bit_depth = (png_byte)bit_depth;
+      row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+          row_info->width);
+   }
+}
+#endif
+
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+/* Shift pixel values to take advantage of whole range.  Pass the
+ * true number of bits in bit_depth.  The row should be packed
+ * according to row_info->bit_depth.  Thus, if you had a row of
+ * bit depth 4, but the pixels only had values from 0 to 7, you
+ * would pass 3 as bit_depth, and this routine would translate the
+ * data to 0 to 15.
+ */
+static void
+png_do_shift(png_row_infop row_info, png_bytep row,
+    png_const_color_8p bit_depth)
+{
+   png_debug(1, "in png_do_shift");
+
+   if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift_start[4], shift_dec[4];
+      int channels = 0;
+
+      if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->red;
+         shift_dec[channels] = bit_depth->red;
+         channels++;
+
+         shift_start[channels] = row_info->bit_depth - bit_depth->green;
+         shift_dec[channels] = bit_depth->green;
+         channels++;
+
+         shift_start[channels] = row_info->bit_depth - bit_depth->blue;
+         shift_dec[channels] = bit_depth->blue;
+         channels++;
+      }
+
+      else
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->gray;
+         shift_dec[channels] = bit_depth->gray;
+         channels++;
+      }
+
+      if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
+         shift_dec[channels] = bit_depth->alpha;
+         channels++;
+      }
+
+      /* With low row depths, could only be grayscale, so one channel */
+      if (row_info->bit_depth < 8)
+      {
+         png_bytep bp = row;
+         png_size_t i;
+         unsigned int mask;
+         png_size_t row_bytes = row_info->rowbytes;
+
+         if (bit_depth->gray == 1 && row_info->bit_depth == 2)
+            mask = 0x55;
+
+         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
+            mask = 0x11;
+
+         else
+            mask = 0xff;
+
+         for (i = 0; i < row_bytes; i++, bp++)
+         {
+            int j;
+            unsigned int v, out;
+
+            v = *bp;
+            out = 0;
+
+            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
+            {
+               if (j > 0)
+                  out |= v << j;
+
+               else
+                  out |= (v >> (-j)) & mask;
+            }
+
+            *bp = (png_byte)(out & 0xff);
+         }
+      }
+
+      else if (row_info->bit_depth == 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (i = 0; i < istop; i++, bp++)
+         {
+
+            const unsigned int c = i%channels;
+            int j;
+            unsigned int v, out;
+
+            v = *bp;
+            out = 0;
+
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  out |= v << j;
+
+               else
+                  out |= v >> (-j);
+            }
+
+            *bp = (png_byte)(out & 0xff);
+         }
+      }
+
+      else
+      {
+         png_bytep bp;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (bp = row, i = 0; i < istop; i++)
+         {
+            const unsigned int c = i%channels;
+            int j;
+            unsigned int value, v;
+
+            v = png_get_uint_16(bp);
+            value = 0;
+
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  value |= v << j;
+
+               else
+                  value |= v >> (-j);
+            }
+            *bp++ = (png_byte)((value >> 8) & 0xff);
+            *bp++ = (png_byte)(value & 0xff);
+         }
+      }
+   }
+}
+#endif
+
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+static void
+png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_swap_alpha");
+
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from ARGB to RGBA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         else
+         {
+            /* This converts from AARRGGBB to RRGGBBAA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+#endif /* WRITE_16BIT */
+      }
+
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from AG to GA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         else
+         {
+            /* This converts from AAGG to GGAA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+#endif /* WRITE_16BIT */
+      }
+   }
+}
+#endif
+
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+static void
+png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_invert_alpha");
+
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This inverts the alpha channel in RGBA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               /* Does nothing
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               */
+               sp+=3; dp = sp;
+               *dp = (png_byte)(255 - *(sp++));
+            }
+         }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         else
+         {
+            /* This inverts the alpha channel in RRGGBBAA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               /* Does nothing
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               */
+               sp+=6; dp = sp;
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *dp     = (png_byte)(255 - *(sp++));
+            }
+         }
+#endif /* WRITE_16BIT */
+      }
+
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This inverts the alpha channel in GA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         else
+         {
+            /* This inverts the alpha channel in GGAA */
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               /* Does nothing
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               */
+               sp+=2; dp = sp;
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *dp     = (png_byte)(255 - *(sp++));
+            }
+         }
+#endif /* WRITE_16BIT */
+      }
+   }
+}
+#endif
+
+/* Transform the data according to the user's wishes.  The order of
+ * transformations is significant.
+ */
+void /* PRIVATE */
+png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info)
+{
+   png_debug(1, "in png_do_write_transformations");
+
+   if (png_ptr == NULL)
+      return;
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+   if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
+      if (png_ptr->write_user_transform_fn != NULL)
+         (*(png_ptr->write_user_transform_fn)) /* User write transform
+                                                 function */
+             (png_ptr,  /* png_ptr */
+             row_info,  /* row_info: */
+                /*  png_uint_32 width;       width of row */
+                /*  png_size_t rowbytes;     number of bytes in row */
+                /*  png_byte color_type;     color type of pixels */
+                /*  png_byte bit_depth;      bit depth of samples */
+                /*  png_byte channels;       number of channels (1-4) */
+                /*  png_byte pixel_depth;    bits per pixel (depth*channels) */
+             png_ptr->row_buf + 1);      /* start of pixel data for row */
+#endif
+
+#ifdef PNG_WRITE_FILLER_SUPPORTED
+   if ((png_ptr->transformations & PNG_FILLER) != 0)
+      png_do_strip_channel(row_info, png_ptr->row_buf + 1,
+         !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
+#endif
+
+#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
+   if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
+      png_do_packswap(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_PACK_SUPPORTED
+   if ((png_ptr->transformations & PNG_PACK) != 0)
+      png_do_pack(row_info, png_ptr->row_buf + 1,
+          (png_uint_32)png_ptr->bit_depth);
+#endif
+
+#ifdef PNG_WRITE_SWAP_SUPPORTED
+#  ifdef PNG_16BIT_SUPPORTED
+   if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
+      png_do_swap(row_info, png_ptr->row_buf + 1);
+#  endif
+#endif
+
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+   if ((png_ptr->transformations & PNG_SHIFT) != 0)
+      png_do_shift(row_info, png_ptr->row_buf + 1,
+          &(png_ptr->shift));
+#endif
+
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0)
+      png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+   if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
+      png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_BGR_SUPPORTED
+   if ((png_ptr->transformations & PNG_BGR) != 0)
+      png_do_bgr(row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_INVERT_SUPPORTED
+   if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
+      png_do_invert(row_info, png_ptr->row_buf + 1);
+#endif
+}
+#endif /* WRITE_TRANSFORMS */
+#endif /* WRITE */
diff --git a/third_party/lpng_v163/pngwutil.c b/third_party/libpng/pngwutil.c
similarity index 77%
rename from third_party/lpng_v163/pngwutil.c
rename to third_party/libpng/pngwutil.c
index dd5d659..0ee102b 100644
--- a/third_party/lpng_v163/pngwutil.c
+++ b/third_party/libpng/pngwutil.c
@@ -1,3022 +1,2709 @@
-

-/* pngwutil.c - utilities to write a PNG file

- *

- * 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.)

- *

- * This code is released under the libpng license.

- * For conditions of distribution and use, see the disclaimer

- * and license in png.h

- */

-#include "pngpriv.h"

-

-#ifdef PNG_WRITE_SUPPORTED

-

-#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED

-/* Place a 32-bit number into a buffer in PNG byte order.  We work

- * with unsigned numbers for convenience, although one supported

- * ancillary chunk uses signed (two's complement) numbers.

- */

-void PNGAPI

-png_save_uint_32(png_bytep buf, png_uint_32 i)

-{

-   buf[0] = (png_byte)((i >> 24) & 0xff);

-   buf[1] = (png_byte)((i >> 16) & 0xff);

-   buf[2] = (png_byte)((i >> 8) & 0xff);

-   buf[3] = (png_byte)(i & 0xff);

-}

-

-/* Place a 16-bit number into a buffer in PNG byte order.

- * The parameter is declared unsigned int, not png_uint_16,

- * just to avoid potential problems on pre-ANSI C compilers.

- */

-void PNGAPI

-png_save_uint_16(png_bytep buf, unsigned int i)

-{

-   buf[0] = (png_byte)((i >> 8) & 0xff);

-   buf[1] = (png_byte)(i & 0xff);

-}

-#endif

-

-/* Simple function to write the signature.  If we have already written

- * the magic bytes of the signature, or more likely, the PNG stream is

- * being embedded into another stream and doesn't need its own signature,

- * we should call png_set_sig_bytes() to tell libpng how many of the

- * bytes have already been written.

- */

-void PNGAPI

-png_write_sig(png_structrp png_ptr)

-{

-   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   /* Inform the I/O callback that the signature is being written */

-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE;

-#endif

-

-   /* Write the rest of the 8 byte signature */

-   png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],

-      (png_size_t)(8 - png_ptr->sig_bytes));

-

-   if (png_ptr->sig_bytes < 3)

-      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;

-}

-

-/* Write the start of a PNG chunk.  The type is the chunk type.

- * The total_length is the sum of the lengths of all the data you will be

- * passing in png_write_chunk_data().

- */

-static void

-png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name,

-    png_uint_32 length)

-{

-   png_byte buf[8];

-

-#if defined(PNG_DEBUG) && (PNG_DEBUG > 0)

-   PNG_CSTRING_FROM_CHUNK(buf, chunk_name);

-   png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length);

-#endif

-

-   if (png_ptr == NULL)

-      return;

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   /* Inform the I/O callback that the chunk header is being written.

-    * PNG_IO_CHUNK_HDR requires a single I/O call.

-    */

-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR;

-#endif

-

-   /* Write the length and the chunk name */

-   png_save_uint_32(buf, length);

-   png_save_uint_32(buf + 4, chunk_name);

-   png_write_data(png_ptr, buf, 8);

-

-   /* Put the chunk name into png_ptr->chunk_name */

-   png_ptr->chunk_name = chunk_name;

-

-   /* Reset the crc and run it over the chunk name */

-   png_reset_crc(png_ptr);

-

-   png_calculate_crc(png_ptr, buf + 4, 4);

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   /* Inform the I/O callback that chunk data will (possibly) be written.

-    * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls.

-    */

-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA;

-#endif

-}

-

-void PNGAPI

-png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string,

-    png_uint_32 length)

-{

-   png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length);

-}

-

-/* Write the data of a PNG chunk started with png_write_chunk_header().

- * Note that multiple calls to this function are allowed, and that the

- * sum of the lengths from these calls *must* add up to the total_length

- * given to png_write_chunk_header().

- */

-void PNGAPI

-png_write_chunk_data(png_structrp png_ptr, png_const_bytep data,

-    png_size_t length)

-{

-   /* Write the data, and run the CRC over it */

-   if (png_ptr == NULL)

-      return;

-

-   if (data != NULL && length > 0)

-   {

-      png_write_data(png_ptr, data, length);

-

-      /* Update the CRC after writing the data,

-       * in case that the user I/O routine alters it.

-       */

-      png_calculate_crc(png_ptr, data, length);

-   }

-}

-

-/* Finish a chunk started with png_write_chunk_header(). */

-void PNGAPI

-png_write_chunk_end(png_structrp png_ptr)

-{

-   png_byte buf[4];

-

-   if (png_ptr == NULL) return;

-

-#ifdef PNG_IO_STATE_SUPPORTED

-   /* Inform the I/O callback that the chunk CRC is being written.

-    * PNG_IO_CHUNK_CRC requires a single I/O function call.

-    */

-   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC;

-#endif

-

-   /* Write the crc in a single operation */

-   png_save_uint_32(buf, png_ptr->crc);

-

-   png_write_data(png_ptr, buf, (png_size_t)4);

-}

-

-/* Write a PNG chunk all at once.  The type is an array of ASCII characters

- * representing the chunk name.  The array must be at least 4 bytes in

- * length, and does not need to be null terminated.  To be safe, pass the

- * pre-defined chunk names here, and if you need a new one, define it

- * where the others are defined.  The length is the length of the data.

- * All the data must be present.  If that is not possible, use the

- * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()

- * functions instead.

- */

-static void

-png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,

-   png_const_bytep data, png_size_t length)

-{

-   if (png_ptr == NULL)

-      return;

-

-   /* On 64 bit architectures 'length' may not fit in a png_uint_32. */

-   if (length > PNG_UINT_31_MAX)

-      png_error(png_ptr, "length exceeds PNG maxima");

-

-   png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);

-   png_write_chunk_data(png_ptr, data, length);

-   png_write_chunk_end(png_ptr);

-}

-

-/* This is the API that calls the internal function above. */

-void PNGAPI

-png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,

-   png_const_bytep data, png_size_t length)

-{

-   png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data,

-      length);

-}

-

-/* This is used below to find the size of an image to pass to png_deflate_claim,

- * so it only needs to be accurate if the size is less than 16384 bytes (the

- * point at which a lower LZ window size can be used.)

- */

-static png_alloc_size_t

-png_image_size(png_structrp png_ptr)

-{

-   /* Only return sizes up to the maximum of a png_uint_32, do this by limiting

-    * the width and height used to 15 bits.

-    */

-   png_uint_32 h = png_ptr->height;

-

-   if (png_ptr->rowbytes < 32768 && h < 32768)

-   {

-      if (png_ptr->interlaced)

-      {

-         /* Interlacing makes the image larger because of the replication of

-          * both the filter byte and the padding to a byte boundary.

-          */

-         png_uint_32 w = png_ptr->width;

-         unsigned int pd = png_ptr->pixel_depth;

-         png_alloc_size_t cb_base;

-         int pass;

-

-         for (cb_base=0, pass=0; pass<=6; ++pass)

-         {

-            png_uint_32 pw = PNG_PASS_COLS(w, pass);

-

-            if (pw > 0)

-               cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass);

-         }

-

-         return cb_base;

-      }

-

-      else

-         return (png_ptr->rowbytes+1) * h;

-   }

-

-   else

-      return 0xffffffffU;

-}

-

-#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED

-   /* This is the code to hack the first two bytes of the deflate stream (the

-    * deflate header) to correct the windowBits value to match the actual data

-    * size.  Note that the second argument is the *uncompressed* size but the

-    * first argument is the *compressed* data (and it must be deflate

-    * compressed.)

-    */

-static void

-optimize_cmf(png_bytep data, png_alloc_size_t data_size)

-{

-   /* Optimize the CMF field in the zlib stream.  The resultant zlib stream is

-    * still compliant to the stream specification.

-    */

-   if (data_size <= 16384) /* else windowBits must be 15 */

-   {

-      unsigned int z_cmf = data[0];  /* zlib compression method and flags */

-

-      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)

-      {

-         unsigned int z_cinfo;

-         unsigned int half_z_window_size;

-

-         z_cinfo = z_cmf >> 4;

-         half_z_window_size = 1U << (z_cinfo + 7);

-

-         if (data_size <= half_z_window_size) /* else no change */

-         {

-            unsigned int tmp;

-

-            do

-            {

-               half_z_window_size >>= 1;

-               --z_cinfo;

-            }

-            while (z_cinfo > 0 && data_size <= half_z_window_size);

-

-            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);

-

-            data[0] = (png_byte)z_cmf;

-            tmp = data[1] & 0xe0;

-            tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;

-            data[1] = (png_byte)tmp;

-         }

-      }

-   }

-}

-#else

-#  define optimize_cmf(dp,dl) ((void)0)

-#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */

-

-/* Initialize the compressor for the appropriate type of compression. */

-static int

-png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,

-   png_alloc_size_t data_size)

-{

-   if (png_ptr->zowner != 0)

-   {

-      char msg[64];

-

-      PNG_STRING_FROM_CHUNK(msg, owner);

-      msg[4] = ':';

-      msg[5] = ' ';

-      PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);

-      /* So the message that results is "<chunk> using zstream"; this is an

-       * internal error, but is very useful for debugging.  i18n requirements

-       * are minimal.

-       */

-      (void)png_safecat(msg, (sizeof msg), 10, " using zstream");

-#     if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC

-         png_warning(png_ptr, msg);

-

-         /* Attempt sane error recovery */

-         if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */

-         {

-            png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");

-            return Z_STREAM_ERROR;

-         }

-

-         png_ptr->zowner = 0;

-#     else

-         png_error(png_ptr, msg);

-#     endif

-   }

-

-   {

-      int level = png_ptr->zlib_level;

-      int method = png_ptr->zlib_method;

-      int windowBits = png_ptr->zlib_window_bits;

-      int memLevel = png_ptr->zlib_mem_level;

-      int strategy; /* set below */

-      int ret; /* zlib return code */

-

-      if (owner == png_IDAT)

-      {

-         if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)

-            strategy = png_ptr->zlib_strategy;

-

-         else if (png_ptr->do_filter != PNG_FILTER_NONE)

-            strategy = PNG_Z_DEFAULT_STRATEGY;

-

-         else

-            strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY;

-      }

-

-      else

-      {

-#        ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED

-            level = png_ptr->zlib_text_level;

-            method = png_ptr->zlib_text_method;

-            windowBits = png_ptr->zlib_text_window_bits;

-            memLevel = png_ptr->zlib_text_mem_level;

-            strategy = png_ptr->zlib_text_strategy;

-#        else

-            /* If customization is not supported the values all come from the

-             * IDAT values except for the strategy, which is fixed to the

-             * default.  (This is the pre-1.6.0 behavior too, although it was

-             * implemented in a very different way.)

-             */

-            strategy = Z_DEFAULT_STRATEGY;

-#        endif

-      }

-

-      /* Adjust 'windowBits' down if larger than 'data_size'; to stop this

-       * happening just pass 32768 as the data_size parameter.  Notice that zlib

-       * requires an extra 262 bytes in the window in addition to the data to be

-       * able to see the whole of the data, so if data_size+262 takes us to the

-       * next windowBits size we need to fix up the value later.  (Because even

-       * though deflate needs the extra window, inflate does not!)

-       */

-      if (data_size <= 16384)

-      {

-         /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to

-          * work round a Microsoft Visual C misbehavior which, contrary to C-90,

-          * widens the result of the following shift to 64-bits if (and,

-          * apparently, only if) it is used in a test.

-          */

-         unsigned int half_window_size = 1U << (windowBits-1);

-

-         while (data_size + 262 <= half_window_size)

-         {

-            half_window_size >>= 1;

-            --windowBits;

-         }

-      }

-

-      /* Check against the previous initialized values, if any. */

-      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) &&

-         (png_ptr->zlib_set_level != level ||

-         png_ptr->zlib_set_method != method ||

-         png_ptr->zlib_set_window_bits != windowBits ||

-         png_ptr->zlib_set_mem_level != memLevel ||

-         png_ptr->zlib_set_strategy != strategy))

-      {

-         if (deflateEnd(&png_ptr->zstream) != Z_OK)

-            png_warning(png_ptr, "deflateEnd failed (ignored)");

-

-         png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;

-      }

-

-      /* For safety clear out the input and output pointers (currently zlib

-       * doesn't use them on Init, but it might in the future).

-       */

-      png_ptr->zstream.next_in = NULL;

-      png_ptr->zstream.avail_in = 0;

-      png_ptr->zstream.next_out = NULL;

-      png_ptr->zstream.avail_out = 0;

-

-      /* Now initialize if required, setting the new parameters, otherwise just

-       * to a simple reset to the previous parameters.

-       */

-      if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)

-         ret = deflateReset(&png_ptr->zstream);

-

-      else

-      {

-         ret = deflateInit2(&png_ptr->zstream, level, method, windowBits,

-            memLevel, strategy);

-

-         if (ret == Z_OK)

-            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;

-      }

-

-      /* The return code is from either deflateReset or deflateInit2; they have

-       * pretty much the same set of error codes.

-       */

-      if (ret == Z_OK)

-         png_ptr->zowner = owner;

-

-      else

-         png_zstream_error(png_ptr, ret);

-

-      return ret;

-   }

-}

-

-/* Clean up (or trim) a linked list of compression buffers. */

-void /* PRIVATE */

-png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)

-{

-   png_compression_bufferp list = *listp;

-

-   if (list != NULL)

-   {

-      *listp = NULL;

-

-      do

-      {

-         png_compression_bufferp next = list->next;

-

-         png_free(png_ptr, list);

-         list = next;

-      }

-      while (list != NULL);

-   }

-}

-

-#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED

-/* This pair of functions encapsulates the operation of (a) compressing a

- * text string, and (b) issuing it later as a series of chunk data writes.

- * The compression_state structure is shared context for these functions

- * set up by the caller to allow access to the relevant local variables.

- *

- * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size

- * temporary buffers.  From 1.6.0 it is retained in png_struct so that it will

- * be correctly freed in the event of a write error (previous implementations

- * just leaked memory.)

- */

-typedef struct

-{

-   png_const_bytep      input;        /* The uncompressed input data */

-   png_alloc_size_t     input_len;    /* Its length */

-   png_uint_32          output_len;   /* Final compressed length */

-   png_byte             output[1024]; /* First block of output */

-} compression_state;

-

-static void

-png_text_compress_init(compression_state *comp, png_const_bytep input,

-   png_alloc_size_t input_len)

-{

-   comp->input = input;

-   comp->input_len = input_len;

-   comp->output_len = 0;

-}

-

-/* Compress the data in the compression state input */

-static int

-png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,

-   compression_state *comp, png_uint_32 prefix_len)

-{

-   int ret;

-

-   /* To find the length of the output it is necessary to first compress the

-    * input, the result is buffered rather than using the two-pass algorithm

-    * that is used on the inflate side; deflate is assumed to be slower and a

-    * PNG writer is assumed to have more memory available than a PNG reader.

-    *

-    * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an

-    * upper limit on the output size, but it is always bigger than the input

-    * size so it is likely to be more efficient to use this linked-list

-    * approach.

-    */

-   ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);

-

-   if (ret != Z_OK)

-      return ret;

-

-   /* Set up the compression buffers, we need a loop here to avoid overflowing a

-    * uInt.  Use ZLIB_IO_MAX to limit the input.  The output is always limited

-    * by the output buffer size, so there is no need to check that.  Since this

-    * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits

-    * in size.

-    */

-   {

-      png_compression_bufferp *end = &png_ptr->zbuffer_list;

-      png_alloc_size_t input_len = comp->input_len; /* may be zero! */

-      png_uint_32 output_len;

-

-      /* zlib updates these for us: */

-      png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);

-      png_ptr->zstream.avail_in = 0; /* Set below */

-      png_ptr->zstream.next_out = comp->output;

-      png_ptr->zstream.avail_out = (sizeof comp->output);

-

-      output_len = png_ptr->zstream.avail_out;

-

-      do

-      {

-         uInt avail_in = ZLIB_IO_MAX;

-

-         if (avail_in > input_len)

-            avail_in = (uInt)input_len;

-

-         input_len -= avail_in;

-

-         png_ptr->zstream.avail_in = avail_in;

-

-         if (png_ptr->zstream.avail_out == 0)

-         {

-            png_compression_buffer *next;

-

-            /* Chunk data is limited to 2^31 bytes in length, so the prefix

-             * length must be counted here.

-             */

-            if (output_len + prefix_len > PNG_UINT_31_MAX)

-            {

-               ret = Z_MEM_ERROR;

-               break;

-            }

-

-            /* Need a new (malloc'ed) buffer, but there may be one present

-             * already.

-             */

-            next = *end;

-            if (next == NULL)

-            {

-               next = png_voidcast(png_compression_bufferp, png_malloc_base

-                  (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));

-

-               if (next == NULL)

-               {

-                  ret = Z_MEM_ERROR;

-                  break;

-               }

-

-               /* Link in this buffer (so that it will be freed later) */

-               next->next = NULL;

-               *end = next;

-            }

-

-            png_ptr->zstream.next_out = next->output;

-            png_ptr->zstream.avail_out = png_ptr->zbuffer_size;

-            output_len += png_ptr->zstream.avail_out;

-

-            /* Move 'end' to the next buffer pointer. */

-            end = &next->next;

-         }

-

-         /* Compress the data */

-         ret = deflate(&png_ptr->zstream,

-            input_len > 0 ? Z_NO_FLUSH : Z_FINISH);

-

-         /* Claw back input data that was not consumed (because avail_in is

-          * reset above every time round the loop).

-          */

-         input_len += png_ptr->zstream.avail_in;

-         png_ptr->zstream.avail_in = 0; /* safety */

-      }

-      while (ret == Z_OK);

-

-      /* There may be some space left in the last output buffer, this needs to

-       * be subtracted from output_len.

-       */

-      output_len -= png_ptr->zstream.avail_out;

-      png_ptr->zstream.avail_out = 0; /* safety */

-      comp->output_len = output_len;

-

-      /* Now double check the output length, put in a custom message if it is

-       * too long.  Otherwise ensure the z_stream::msg pointer is set to

-       * something.

-       */

-      if (output_len + prefix_len >= PNG_UINT_31_MAX)

-      {

-         png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");

-         ret = Z_MEM_ERROR;

-      }

-

-      else

-         png_zstream_error(png_ptr, ret);

-

-      /* Reset zlib for another zTXt/iTXt or image data */

-      png_ptr->zowner = 0;

-

-      /* The only success case is Z_STREAM_END, input_len must be 0, if not this

-       * is an internal error.

-       */

-      if (ret == Z_STREAM_END && input_len == 0)

-      {

-         /* Fix up the deflate header, if required */

-         optimize_cmf(comp->output, comp->input_len);

-

-         /* But Z_OK is returned, not Z_STREAM_END; this allows the claim

-          * function above to return Z_STREAM_END on an error (though it never

-          * does in the current versions of zlib.)

-          */

-         return Z_OK;

-      }

-

-      else

-         return ret;

-   }

-}

-

-/* Ship the compressed text out via chunk writes */

-static void

-png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)

-{

-   png_uint_32 output_len = comp->output_len;

-   png_const_bytep output = comp->output;

-   png_uint_32 avail = (sizeof comp->output);

-   png_compression_buffer *next = png_ptr->zbuffer_list;

-

-   for (;;)

-   {

-      if (avail > output_len)

-         avail = output_len;

-

-      png_write_chunk_data(png_ptr, output, avail);

-

-      output_len -= avail;

-

-      if (output_len == 0 || next == NULL)

-         break;

-

-      avail = png_ptr->zbuffer_size;

-      output = next->output;

-      next = next->next;

-   }

-

-   /* This is an internal error; 'next' must have been NULL! */

-   if (output_len > 0)

-      png_error(png_ptr, "error writing ancillary chunked compressed data");

-}

-#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */

-

-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \

-    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)

-/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,

- * and if invalid, correct the keyword rather than discarding the entire

- * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in

- * length, forbids leading or trailing whitespace, multiple internal spaces,

- * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.

- *

- * The 'new_key' buffer must be 80 characters in size (for the keyword plus a

- * trailing '\0').  If this routine returns 0 then there was no keyword, or a

- * valid one could not be generated, and the caller must png_error.

- */

-static png_uint_32

-png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)

-{

-   png_const_charp orig_key = key;

-   png_uint_32 key_len = 0;

-   int bad_character = 0;

-   int space = 1;

-

-   png_debug(1, "in png_check_keyword");

-

-   if (key == NULL)

-   {

-      *new_key = 0;

-      return 0;

-   }

-

-   while (*key && key_len < 79)

-   {

-      png_byte ch = (png_byte)(0xff & *key++);

-

-      if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))

-         *new_key++ = ch, ++key_len, space = 0;

-

-      else if (!space)

-      {

-         /* A space or an invalid character when one wasn't seen immediately

-          * before; output just a space.

-          */

-         *new_key++ = 32, ++key_len, space = 1;

-

-         /* If the character was not a space then it is invalid. */

-         if (ch != 32)

-            bad_character = ch;

-      }

-

-      else if (!bad_character)

-         bad_character = ch; /* just skip it, record the first error */

-   }

-

-   if (key_len > 0 && space) /* trailing space */

-   {

-      --key_len, --new_key;

-      if (!bad_character)

-         bad_character = 32;

-   }

-

-   /* Terminate the keyword */

-   *new_key = 0;

-

-   if (key_len == 0)

-      return 0;

-

-   /* Try to only output one warning per keyword: */

-   if (*key) /* keyword too long */

-      png_warning(png_ptr, "keyword truncated");

-

-   else if (bad_character)

-   {

-      PNG_WARNING_PARAMETERS(p)

-

-      png_warning_parameter(p, 1, orig_key);

-      png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character);

-

-      png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");

-   }

-

-   return key_len;

-}

-#endif

-

-/* Write the IHDR chunk, and update the png_struct with the necessary

- * information.  Note that the rest of this code depends upon this

- * information being correct.

- */

-void /* PRIVATE */

-png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,

-    int bit_depth, int color_type, int compression_type, int filter_type,

-    int interlace_type)

-{

-   png_byte buf[13]; /* Buffer to store the IHDR info */

-

-   png_debug(1, "in png_write_IHDR");

-

-   /* Check that we have valid input data from the application info */

-   switch (color_type)

-   {

-      case PNG_COLOR_TYPE_GRAY:

-         switch (bit_depth)

-         {

-            case 1:

-            case 2:

-            case 4:

-            case 8:

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-            case 16:

-#endif

-               png_ptr->channels = 1; break;

-

-            default:

-               png_error(png_ptr,

-                   "Invalid bit depth for grayscale image");

-         }

-         break;

-

-      case PNG_COLOR_TYPE_RGB:

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-         if (bit_depth != 8 && bit_depth != 16)

-#else

-         if (bit_depth != 8)

-#endif

-            png_error(png_ptr, "Invalid bit depth for RGB image");

-

-         png_ptr->channels = 3;

-         break;

-

-      case PNG_COLOR_TYPE_PALETTE:

-         switch (bit_depth)

-         {

-            case 1:

-            case 2:

-            case 4:

-            case 8:

-               png_ptr->channels = 1;

-               break;

-

-            default:

-               png_error(png_ptr, "Invalid bit depth for paletted image");

-         }

-         break;

-

-      case PNG_COLOR_TYPE_GRAY_ALPHA:

-         if (bit_depth != 8 && bit_depth != 16)

-            png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");

-

-         png_ptr->channels = 2;

-         break;

-

-      case PNG_COLOR_TYPE_RGB_ALPHA:

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-         if (bit_depth != 8 && bit_depth != 16)

-#else

-         if (bit_depth != 8)

-#endif

-            png_error(png_ptr, "Invalid bit depth for RGBA image");

-

-         png_ptr->channels = 4;

-         break;

-

-      default:

-         png_error(png_ptr, "Invalid image color type specified");

-   }

-

-   if (compression_type != PNG_COMPRESSION_TYPE_BASE)

-   {

-      png_warning(png_ptr, "Invalid compression type specified");

-      compression_type = PNG_COMPRESSION_TYPE_BASE;

-   }

-

-   /* Write filter_method 64 (intrapixel differencing) only if

-    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and

-    * 2. Libpng did not write a PNG signature (this filter_method is only

-    *    used in PNG datastreams that are embedded in MNG datastreams) and

-    * 3. The application called png_permit_mng_features with a mask that

-    *    included PNG_FLAG_MNG_FILTER_64 and

-    * 4. The filter_method is 64 and

-    * 5. The color_type is RGB or RGBA

-    */

-   if (

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-       !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&

-       ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&

-       (color_type == PNG_COLOR_TYPE_RGB ||

-        color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&

-       (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&

-#endif

-       filter_type != PNG_FILTER_TYPE_BASE)

-   {

-      png_warning(png_ptr, "Invalid filter type specified");

-      filter_type = PNG_FILTER_TYPE_BASE;

-   }

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   if (interlace_type != PNG_INTERLACE_NONE &&

-       interlace_type != PNG_INTERLACE_ADAM7)

-   {

-      png_warning(png_ptr, "Invalid interlace type specified");

-      interlace_type = PNG_INTERLACE_ADAM7;

-   }

-#else

-   interlace_type=PNG_INTERLACE_NONE;

-#endif

-

-   /* Save the relevent information */

-   png_ptr->bit_depth = (png_byte)bit_depth;

-   png_ptr->color_type = (png_byte)color_type;

-   png_ptr->interlaced = (png_byte)interlace_type;

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-   png_ptr->filter_type = (png_byte)filter_type;

-#endif

-   png_ptr->compression_type = (png_byte)compression_type;

-   png_ptr->width = width;

-   png_ptr->height = height;

-

-   png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);

-   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);

-   /* Set the usr info, so any transformations can modify it */

-   png_ptr->usr_width = png_ptr->width;

-   png_ptr->usr_bit_depth = png_ptr->bit_depth;

-   png_ptr->usr_channels = png_ptr->channels;

-

-   /* Pack the header information into the buffer */

-   png_save_uint_32(buf, width);

-   png_save_uint_32(buf + 4, height);

-   buf[8] = (png_byte)bit_depth;

-   buf[9] = (png_byte)color_type;

-   buf[10] = (png_byte)compression_type;

-   buf[11] = (png_byte)filter_type;

-   buf[12] = (png_byte)interlace_type;

-

-   /* Write the chunk */

-   png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);

-

-   if (!(png_ptr->do_filter))

-   {

-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||

-          png_ptr->bit_depth < 8)

-         png_ptr->do_filter = PNG_FILTER_NONE;

-

-      else

-         png_ptr->do_filter = PNG_ALL_FILTERS;

-   }

-

-   png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */

-}

-

-/* Write the palette.  We are careful not to trust png_color to be in the

- * correct order for PNG, so people can redefine it to any convenient

- * structure.

- */

-void /* PRIVATE */

-png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,

-    png_uint_32 num_pal)

-{

-   png_uint_32 i;

-   png_const_colorp pal_ptr;

-   png_byte buf[3];

-

-   png_debug(1, "in png_write_PLTE");

-

-   if ((

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-       !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&

-#endif

-       num_pal == 0) || num_pal > 256)

-   {

-      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)

-      {

-         png_error(png_ptr, "Invalid number of colors in palette");

-      }

-

-      else

-      {

-         png_warning(png_ptr, "Invalid number of colors in palette");

-         return;

-      }

-   }

-

-   if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))

-   {

-      png_warning(png_ptr,

-          "Ignoring request to write a PLTE chunk in grayscale PNG");

-

-      return;

-   }

-

-   png_ptr->num_palette = (png_uint_16)num_pal;

-   png_debug1(3, "num_palette = %d", png_ptr->num_palette);

-

-   png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3));

-#ifdef PNG_POINTER_INDEXING_SUPPORTED

-

-   for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)

-   {

-      buf[0] = pal_ptr->red;

-      buf[1] = pal_ptr->green;

-      buf[2] = pal_ptr->blue;

-      png_write_chunk_data(png_ptr, buf, (png_size_t)3);

-   }

-

-#else

-   /* This is a little slower but some buggy compilers need to do this

-    * instead

-    */

-   pal_ptr=palette;

-

-   for (i = 0; i < num_pal; i++)

-   {

-      buf[0] = pal_ptr[i].red;

-      buf[1] = pal_ptr[i].green;

-      buf[2] = pal_ptr[i].blue;

-      png_write_chunk_data(png_ptr, buf, (png_size_t)3);

-   }

-

-#endif

-   png_write_chunk_end(png_ptr);

-   png_ptr->mode |= PNG_HAVE_PLTE;

-}

-

-/* This is similar to png_text_compress, above, except that it does not require

- * all of the data at once and, instead of buffering the compressed result,

- * writes it as IDAT chunks.  Unlike png_text_compress it *can* png_error out

- * because it calls the write interface.  As a result it does its own error

- * reporting and does not return an error code.  In the event of error it will

- * just call png_error.  The input data length may exceed 32-bits.  The 'flush'

- * parameter is exactly the same as that to deflate, with the following

- * meanings:

- *

- * Z_NO_FLUSH: normal incremental output of compressed data

- * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush

- * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up

- *

- * The routine manages the acquire and release of the png_ptr->zstream by

- * checking and (at the end) clearing png_ptr->zowner, it does some sanity

- * checks on the 'mode' flags while doing this.

- */

-void /* PRIVATE */

-png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,

-   png_alloc_size_t input_len, int flush)

-{

-   if (png_ptr->zowner != png_IDAT)

-   {

-      /* First time.   Ensure we have a temporary buffer for compression and

-       * trim the buffer list if it has more than one entry to free memory.

-       * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been

-       * created at this point, but the check here is quick and safe.

-       */

-      if (png_ptr->zbuffer_list == NULL)

-      {

-         png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,

-            png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));

-         png_ptr->zbuffer_list->next = NULL;

-      }

-

-      else

-         png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);

-

-      /* It is a terminal error if we can't claim the zstream. */

-      if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)

-         png_error(png_ptr, png_ptr->zstream.msg);

-

-      /* The output state is maintained in png_ptr->zstream, so it must be

-       * initialized here after the claim.

-       */

-      png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;

-      png_ptr->zstream.avail_out = png_ptr->zbuffer_size;

-   }

-

-   /* Now loop reading and writing until all the input is consumed or an error

-    * terminates the operation.  The _out values are maintained across calls to

-    * this function, but the input must be reset each time.

-    */

-   png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);

-   png_ptr->zstream.avail_in = 0; /* set below */

-   for (;;)

-   {

-      int ret;

-

-      /* INPUT: from the row data */

-      uInt avail = ZLIB_IO_MAX;

-

-      if (avail > input_len)

-         avail = (uInt)input_len; /* safe because of the check */

-

-      png_ptr->zstream.avail_in = avail;

-      input_len -= avail;

-

-      ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);

-

-      /* Include as-yet unconsumed input */

-      input_len += png_ptr->zstream.avail_in;

-      png_ptr->zstream.avail_in = 0;

-

-      /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note

-       * that these two zstream fields are preserved across the calls, therefore

-       * there is no need to set these up on entry to the loop.

-       */

-      if (png_ptr->zstream.avail_out == 0)

-      {

-         png_bytep data = png_ptr->zbuffer_list->output;

-         uInt size = png_ptr->zbuffer_size;

-

-         /* Write an IDAT containing the data then reset the buffer.  The

-          * first IDAT may need deflate header optimization.

-          */

-#        ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED

-            if (!(png_ptr->mode & PNG_HAVE_IDAT) &&

-               png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)

-               optimize_cmf(data, png_image_size(png_ptr));

-#        endif

-

-         png_write_complete_chunk(png_ptr, png_IDAT, data, size);

-         png_ptr->mode |= PNG_HAVE_IDAT;

-

-         png_ptr->zstream.next_out = data;

-         png_ptr->zstream.avail_out = size;

-

-         /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with

-          * the same flush parameter until it has finished output, for NO_FLUSH

-          * it doesn't matter.

-          */

-         if (ret == Z_OK && flush != Z_NO_FLUSH)

-            continue;

-      }

-

-      /* The order of these checks doesn't matter much; it just effect which

-       * possible error might be detected if multiple things go wrong at once.

-       */

-      if (ret == Z_OK) /* most likely return code! */

-      {

-         /* If all the input has been consumed then just return.  If Z_FINISH

-          * was used as the flush parameter something has gone wrong if we get

-          * here.

-          */

-         if (input_len == 0)

-         {

-            if (flush == Z_FINISH)

-               png_error(png_ptr, "Z_OK on Z_FINISH with output space");

-

-            return;

-         }

-      }

-

-      else if (ret == Z_STREAM_END && flush == Z_FINISH)

-      {

-         /* This is the end of the IDAT data; any pending output must be

-          * flushed.  For small PNG files we may still be at the beginning.

-          */

-         png_bytep data = png_ptr->zbuffer_list->output;

-         uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;

-

-#        ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED

-            if (!(png_ptr->mode & PNG_HAVE_IDAT) &&

-               png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)

-               optimize_cmf(data, png_image_size(png_ptr));

-#        endif

-

-         png_write_complete_chunk(png_ptr, png_IDAT, data, size);

-         png_ptr->zstream.avail_out = 0;

-         png_ptr->zstream.next_out = NULL;

-         png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;

-

-         png_ptr->zowner = 0; /* Release the stream */

-         return;

-      }

-

-      else

-      {

-         /* This is an error condition. */

-         png_zstream_error(png_ptr, ret);

-         png_error(png_ptr, png_ptr->zstream.msg);

-      }

-   }

-}

-

-/* Write an IEND chunk */

-void /* PRIVATE */

-png_write_IEND(png_structrp png_ptr)

-{

-   png_debug(1, "in png_write_IEND");

-

-   png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0);

-   png_ptr->mode |= PNG_HAVE_IEND;

-}

-

-#ifdef PNG_WRITE_gAMA_SUPPORTED

-/* Write a gAMA chunk */

-void /* PRIVATE */

-png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma)

-{

-   png_byte buf[4];

-

-   png_debug(1, "in png_write_gAMA");

-

-   /* file_gamma is saved in 1/100,000ths */

-   png_save_uint_32(buf, (png_uint_32)file_gamma);

-   png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);

-}

-#endif

-

-#ifdef PNG_WRITE_sRGB_SUPPORTED

-/* Write a sRGB chunk */

-void /* PRIVATE */

-png_write_sRGB(png_structrp png_ptr, int srgb_intent)

-{

-   png_byte buf[1];

-

-   png_debug(1, "in png_write_sRGB");

-

-   if (srgb_intent >= PNG_sRGB_INTENT_LAST)

-      png_warning(png_ptr,

-          "Invalid sRGB rendering intent specified");

-

-   buf[0]=(png_byte)srgb_intent;

-   png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);

-}

-#endif

-

-#ifdef PNG_WRITE_iCCP_SUPPORTED

-/* Write an iCCP chunk */

-void /* PRIVATE */

-png_write_iCCP(png_structrp png_ptr, png_const_charp name,

-    png_const_bytep profile)

-{

-   png_uint_32 name_len;

-   png_uint_32 profile_len;

-   png_byte new_name[81]; /* 1 byte for the compression byte */

-   compression_state comp;

-

-   png_debug(1, "in png_write_iCCP");

-

-   /* These are all internal problems: the profile should have been checked

-    * before when it was stored.

-    */

-   if (profile == NULL)

-      png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */

-

-   profile_len = png_get_uint_32(profile);

-

-   if (profile_len < 132)

-      png_error(png_ptr, "ICC profile too short");

-

-   if (profile_len & 0x03)

-      png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");

-

-   {

-      png_uint_32 embedded_profile_len = png_get_uint_32(profile);

-

-      if (profile_len != embedded_profile_len)

-         png_error(png_ptr, "Profile length does not match profile");

-   }

-

-   name_len = png_check_keyword(png_ptr, name, new_name);

-

-   if (name_len == 0)

-      png_error(png_ptr, "iCCP: invalid keyword");

-

-   new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;

-

-   /* Make sure we include the NULL after the name and the compression type */

-   ++name_len;

-

-   png_text_compress_init(&comp, profile, profile_len);

-

-   /* Allow for keyword terminator and compression byte */

-   if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)

-      png_error(png_ptr, png_ptr->zstream.msg);

-

-   png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);

-

-   png_write_chunk_data(png_ptr, new_name, name_len);

-

-   png_write_compressed_data_out(png_ptr, &comp);

-

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_sPLT_SUPPORTED

-/* Write a sPLT chunk */

-void /* PRIVATE */

-png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)

-{

-   png_uint_32 name_len;

-   png_byte new_name[80];

-   png_byte entrybuf[10];

-   png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);

-   png_size_t palette_size = entry_size * spalette->nentries;

-   png_sPLT_entryp ep;

-#ifndef PNG_POINTER_INDEXING_SUPPORTED

-   int i;

-#endif

-

-   png_debug(1, "in png_write_sPLT");

-

-   name_len = png_check_keyword(png_ptr, spalette->name, new_name);

-

-   if (name_len == 0)

-      png_error(png_ptr, "sPLT: invalid keyword");

-

-   /* Make sure we include the NULL after the name */

-   png_write_chunk_header(png_ptr, png_sPLT,

-       (png_uint_32)(name_len + 2 + palette_size));

-

-   png_write_chunk_data(png_ptr, (png_bytep)new_name,

-       (png_size_t)(name_len + 1));

-

-   png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);

-

-   /* Loop through each palette entry, writing appropriately */

-#ifdef PNG_POINTER_INDEXING_SUPPORTED

-   for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)

-   {

-      if (spalette->depth == 8)

-      {

-         entrybuf[0] = (png_byte)ep->red;

-         entrybuf[1] = (png_byte)ep->green;

-         entrybuf[2] = (png_byte)ep->blue;

-         entrybuf[3] = (png_byte)ep->alpha;

-         png_save_uint_16(entrybuf + 4, ep->frequency);

-      }

-

-      else

-      {

-         png_save_uint_16(entrybuf + 0, ep->red);

-         png_save_uint_16(entrybuf + 2, ep->green);

-         png_save_uint_16(entrybuf + 4, ep->blue);

-         png_save_uint_16(entrybuf + 6, ep->alpha);

-         png_save_uint_16(entrybuf + 8, ep->frequency);

-      }

-

-      png_write_chunk_data(png_ptr, entrybuf, entry_size);

-   }

-#else

-   ep=spalette->entries;

-   for (i = 0; i>spalette->nentries; i++)

-   {

-      if (spalette->depth == 8)

-      {

-         entrybuf[0] = (png_byte)ep[i].red;

-         entrybuf[1] = (png_byte)ep[i].green;

-         entrybuf[2] = (png_byte)ep[i].blue;

-         entrybuf[3] = (png_byte)ep[i].alpha;

-         png_save_uint_16(entrybuf + 4, ep[i].frequency);

-      }

-

-      else

-      {

-         png_save_uint_16(entrybuf + 0, ep[i].red);

-         png_save_uint_16(entrybuf + 2, ep[i].green);

-         png_save_uint_16(entrybuf + 4, ep[i].blue);

-         png_save_uint_16(entrybuf + 6, ep[i].alpha);

-         png_save_uint_16(entrybuf + 8, ep[i].frequency);

-      }

-

-      png_write_chunk_data(png_ptr, entrybuf, entry_size);

-   }

-#endif

-

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_sBIT_SUPPORTED

-/* Write the sBIT chunk */

-void /* PRIVATE */

-png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)

-{

-   png_byte buf[4];

-   png_size_t size;

-

-   png_debug(1, "in png_write_sBIT");

-

-   /* Make sure we don't depend upon the order of PNG_COLOR_8 */

-   if (color_type & PNG_COLOR_MASK_COLOR)

-   {

-      png_byte maxbits;

-

-      maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :

-          png_ptr->usr_bit_depth);

-

-      if (sbit->red == 0 || sbit->red > maxbits ||

-          sbit->green == 0 || sbit->green > maxbits ||

-          sbit->blue == 0 || sbit->blue > maxbits)

-      {

-         png_warning(png_ptr, "Invalid sBIT depth specified");

-         return;

-      }

-

-      buf[0] = sbit->red;

-      buf[1] = sbit->green;

-      buf[2] = sbit->blue;

-      size = 3;

-   }

-

-   else

-   {

-      if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)

-      {

-         png_warning(png_ptr, "Invalid sBIT depth specified");

-         return;

-      }

-

-      buf[0] = sbit->gray;

-      size = 1;

-   }

-

-   if (color_type & PNG_COLOR_MASK_ALPHA)

-   {

-      if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)

-      {

-         png_warning(png_ptr, "Invalid sBIT depth specified");

-         return;

-      }

-

-      buf[size++] = sbit->alpha;

-   }

-

-   png_write_complete_chunk(png_ptr, png_sBIT, buf, size);

-}

-#endif

-

-#ifdef PNG_WRITE_cHRM_SUPPORTED

-/* Write the cHRM chunk */

-void /* PRIVATE */

-png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy)

-{

-   png_byte buf[32];

-

-   png_debug(1, "in png_write_cHRM");

-

-   /* Each value is saved in 1/100,000ths */

-   png_save_int_32(buf,      xy->whitex);

-   png_save_int_32(buf +  4, xy->whitey);

-

-   png_save_int_32(buf +  8, xy->redx);

-   png_save_int_32(buf + 12, xy->redy);

-

-   png_save_int_32(buf + 16, xy->greenx);

-   png_save_int_32(buf + 20, xy->greeny);

-

-   png_save_int_32(buf + 24, xy->bluex);

-   png_save_int_32(buf + 28, xy->bluey);

-

-   png_write_complete_chunk(png_ptr, png_cHRM, buf, 32);

-}

-#endif

-

-#ifdef PNG_WRITE_tRNS_SUPPORTED

-/* Write the tRNS chunk */

-void /* PRIVATE */

-png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha,

-    png_const_color_16p tran, int num_trans, int color_type)

-{

-   png_byte buf[6];

-

-   png_debug(1, "in png_write_tRNS");

-

-   if (color_type == PNG_COLOR_TYPE_PALETTE)

-   {

-      if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)

-      {

-         png_app_warning(png_ptr,

-             "Invalid number of transparent colors specified");

-         return;

-      }

-

-      /* Write the chunk out as it is */

-      png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha,

-         (png_size_t)num_trans);

-   }

-

-   else if (color_type == PNG_COLOR_TYPE_GRAY)

-   {

-      /* One 16 bit value */

-      if (tran->gray >= (1 << png_ptr->bit_depth))

-      {

-         png_app_warning(png_ptr,

-             "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");

-

-         return;

-      }

-

-      png_save_uint_16(buf, tran->gray);

-      png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);

-   }

-

-   else if (color_type == PNG_COLOR_TYPE_RGB)

-   {

-      /* Three 16 bit values */

-      png_save_uint_16(buf, tran->red);

-      png_save_uint_16(buf + 2, tran->green);

-      png_save_uint_16(buf + 4, tran->blue);

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-      if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))

-#else

-      if (buf[0] | buf[2] | buf[4])

-#endif

-      {

-         png_app_warning(png_ptr,

-           "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");

-         return;

-      }

-

-      png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);

-   }

-

-   else

-   {

-      png_app_warning(png_ptr, "Can't write tRNS with an alpha channel");

-   }

-}

-#endif

-

-#ifdef PNG_WRITE_bKGD_SUPPORTED

-/* Write the background chunk */

-void /* PRIVATE */

-png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)

-{

-   png_byte buf[6];

-

-   png_debug(1, "in png_write_bKGD");

-

-   if (color_type == PNG_COLOR_TYPE_PALETTE)

-   {

-      if (

-#ifdef PNG_MNG_FEATURES_SUPPORTED

-          (png_ptr->num_palette ||

-          (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&

-#endif

-         back->index >= png_ptr->num_palette)

-      {

-         png_warning(png_ptr, "Invalid background palette index");

-         return;

-      }

-

-      buf[0] = back->index;

-      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);

-   }

-

-   else if (color_type & PNG_COLOR_MASK_COLOR)

-   {

-      png_save_uint_16(buf, back->red);

-      png_save_uint_16(buf + 2, back->green);

-      png_save_uint_16(buf + 4, back->blue);

-#ifdef PNG_WRITE_16BIT_SUPPORTED

-      if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))

-#else

-      if (buf[0] | buf[2] | buf[4])

-#endif

-      {

-         png_warning(png_ptr,

-             "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");

-

-         return;

-      }

-

-      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);

-   }

-

-   else

-   {

-      if (back->gray >= (1 << png_ptr->bit_depth))

-      {

-         png_warning(png_ptr,

-             "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");

-

-         return;

-      }

-

-      png_save_uint_16(buf, back->gray);

-      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);

-   }

-}

-#endif

-

-#ifdef PNG_WRITE_hIST_SUPPORTED

-/* Write the histogram */

-void /* PRIVATE */

-png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist)

-{

-   int i;

-   png_byte buf[3];

-

-   png_debug(1, "in png_write_hIST");

-

-   if (num_hist > (int)png_ptr->num_palette)

-   {

-      png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,

-          png_ptr->num_palette);

-

-      png_warning(png_ptr, "Invalid number of histogram entries specified");

-      return;

-   }

-

-   png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));

-

-   for (i = 0; i < num_hist; i++)

-   {

-      png_save_uint_16(buf, hist[i]);

-      png_write_chunk_data(png_ptr, buf, (png_size_t)2);

-   }

-

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_tEXt_SUPPORTED

-/* Write a tEXt chunk */

-void /* PRIVATE */

-png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,

-    png_size_t text_len)

-{

-   png_uint_32 key_len;

-   png_byte new_key[80];

-

-   png_debug(1, "in png_write_tEXt");

-

-   key_len = png_check_keyword(png_ptr, key, new_key);

-

-   if (key_len == 0)

-      png_error(png_ptr, "tEXt: invalid keyword");

-

-   if (text == NULL || *text == '\0')

-      text_len = 0;

-

-   else

-      text_len = strlen(text);

-

-   if (text_len > PNG_UINT_31_MAX - (key_len+1))

-      png_error(png_ptr, "tEXt: text too long");

-

-   /* Make sure we include the 0 after the key */

-   png_write_chunk_header(png_ptr, png_tEXt,

-       (png_uint_32)/*checked above*/(key_len + text_len + 1));

-   /*

-    * We leave it to the application to meet PNG-1.0 requirements on the

-    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of

-    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.

-    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.

-    */

-   png_write_chunk_data(png_ptr, new_key, key_len + 1);

-

-   if (text_len)

-      png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len);

-

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_zTXt_SUPPORTED

-/* Write a compressed text chunk */

-void /* PRIVATE */

-png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,

-    png_size_t text_len, int compression)

-{

-   png_uint_32 key_len;

-   png_byte new_key[81];

-   compression_state comp;

-

-   png_debug(1, "in png_write_zTXt");

-   PNG_UNUSED(text_len) /* Always use strlen */

-

-   if (compression == PNG_TEXT_COMPRESSION_NONE)

-   {

-      png_write_tEXt(png_ptr, key, text, 0);

-      return;

-   }

-

-   if (compression != PNG_TEXT_COMPRESSION_zTXt)

-      png_error(png_ptr, "zTXt: invalid compression type");

-

-   key_len = png_check_keyword(png_ptr, key, new_key);

-

-   if (key_len == 0)

-      png_error(png_ptr, "zTXt: invalid keyword");

-

-   /* Add the compression method and 1 for the keyword separator. */

-   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;

-   ++key_len;

-

-   /* Compute the compressed data; do it now for the length */

-   png_text_compress_init(&comp, (png_const_bytep)text,

-      text == NULL ? 0 : strlen(text));

-

-   if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)

-      png_error(png_ptr, png_ptr->zstream.msg);

-

-   /* Write start of chunk */

-   png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);

-

-   /* Write key */

-   png_write_chunk_data(png_ptr, new_key, key_len);

-

-   /* Write the compressed data */

-   png_write_compressed_data_out(png_ptr, &comp);

-

-   /* Close the chunk */

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_iTXt_SUPPORTED

-/* Write an iTXt chunk */

-void /* PRIVATE */

-png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,

-    png_const_charp lang, png_const_charp lang_key, png_const_charp text)

-{

-   png_uint_32 key_len, prefix_len;

-   png_size_t lang_len, lang_key_len;

-   png_byte new_key[82];

-   compression_state comp;

-

-   png_debug(1, "in png_write_iTXt");

-

-   key_len = png_check_keyword(png_ptr, key, new_key);

-

-   if (key_len == 0)

-      png_error(png_ptr, "iTXt: invalid keyword");

-

-   /* Set the compression flag */

-   switch (compression)

-   {

-      case PNG_ITXT_COMPRESSION_NONE:

-      case PNG_TEXT_COMPRESSION_NONE:

-         compression = new_key[++key_len] = 0; /* no compression */

-         break;

-

-      case PNG_TEXT_COMPRESSION_zTXt:

-      case PNG_ITXT_COMPRESSION_zTXt:

-         compression = new_key[++key_len] = 1; /* compressed */

-         break;

-

-      default:

-         png_error(png_ptr, "iTXt: invalid compression");

-   }

-

-   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;

-   ++key_len; /* for the keywod separator */

-

-   /* We leave it to the application to meet PNG-1.0 requirements on the

-    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of

-    * any non-Latin-1 characters except for NEWLINE.  ISO PNG, however,

-    * specifies that the text is UTF-8 and this really doesn't require any

-    * checking.

-    *

-    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.

-    *

-    * TODO: validate the language tag correctly (see the spec.)

-    */

-   if (lang == NULL) lang = ""; /* empty language is valid */

-   lang_len = strlen(lang)+1;

-   if (lang_key == NULL) lang_key = ""; /* may be empty */

-   lang_key_len = strlen(lang_key)+1;

-   if (text == NULL) text = ""; /* may be empty */

-

-   prefix_len = key_len;

-   if (lang_len > PNG_UINT_31_MAX-prefix_len)

-      prefix_len = PNG_UINT_31_MAX;

-   else

-      prefix_len = (png_uint_32)(prefix_len + lang_len);

-

-   if (lang_key_len > PNG_UINT_31_MAX-prefix_len)

-      prefix_len = PNG_UINT_31_MAX;

-   else

-      prefix_len = (png_uint_32)(prefix_len + lang_key_len);

-

-   png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));

-

-   if (compression)

-   {

-      if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)

-         png_error(png_ptr, png_ptr->zstream.msg);

-   }

-

-   else

-   {

-      if (comp.input_len > PNG_UINT_31_MAX-prefix_len)

-         png_error(png_ptr, "iTXt: uncompressed text too long");

-

-      /* So the string will fit in a chunk: */

-      comp.output_len = (png_uint_32)/*SAFE*/comp.input_len;

-   }

-

-   png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);

-

-   png_write_chunk_data(png_ptr, new_key, key_len);

-

-   png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);

-

-   png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);

-

-   if (compression)

-      png_write_compressed_data_out(png_ptr, &comp);

-

-   else

-      png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.input_len);

-

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_oFFs_SUPPORTED

-/* Write the oFFs chunk */

-void /* PRIVATE */

-png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset,

-    int unit_type)

-{

-   png_byte buf[9];

-

-   png_debug(1, "in png_write_oFFs");

-

-   if (unit_type >= PNG_OFFSET_LAST)

-      png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");

-

-   png_save_int_32(buf, x_offset);

-   png_save_int_32(buf + 4, y_offset);

-   buf[8] = (png_byte)unit_type;

-

-   png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);

-}

-#endif

-#ifdef PNG_WRITE_pCAL_SUPPORTED

-/* Write the pCAL chunk (described in the PNG extensions document) */

-void /* PRIVATE */

-png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0,

-    png_int_32 X1, int type, int nparams, png_const_charp units,

-    png_charpp params)

-{

-   png_uint_32 purpose_len;

-   png_size_t units_len, total_len;

-   png_size_tp params_len;

-   png_byte buf[10];

-   png_byte new_purpose[80];

-   int i;

-

-   png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);

-

-   if (type >= PNG_EQUATION_LAST)

-      png_error(png_ptr, "Unrecognized equation type for pCAL chunk");

-

-   purpose_len = png_check_keyword(png_ptr, purpose, new_purpose);

-

-   if (purpose_len == 0)

-      png_error(png_ptr, "pCAL: invalid keyword");

-

-   ++purpose_len; /* terminator */

-

-   png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);

-   units_len = strlen(units) + (nparams == 0 ? 0 : 1);

-   png_debug1(3, "pCAL units length = %d", (int)units_len);

-   total_len = purpose_len + units_len + 10;

-

-   params_len = (png_size_tp)png_malloc(png_ptr,

-       (png_alloc_size_t)(nparams * (sizeof (png_size_t))));

-

-   /* Find the length of each parameter, making sure we don't count the

-    * null terminator for the last parameter.

-    */

-   for (i = 0; i < nparams; i++)

-   {

-      params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1);

-      png_debug2(3, "pCAL parameter %d length = %lu", i,

-          (unsigned long)params_len[i]);

-      total_len += params_len[i];

-   }

-

-   png_debug1(3, "pCAL total length = %d", (int)total_len);

-   png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);

-   png_write_chunk_data(png_ptr, new_purpose, purpose_len);

-   png_save_int_32(buf, X0);

-   png_save_int_32(buf + 4, X1);

-   buf[8] = (png_byte)type;

-   buf[9] = (png_byte)nparams;

-   png_write_chunk_data(png_ptr, buf, (png_size_t)10);

-   png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);

-

-   for (i = 0; i < nparams; i++)

-   {

-      png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);

-   }

-

-   png_free(png_ptr, params_len);

-   png_write_chunk_end(png_ptr);

-}

-#endif

-

-#ifdef PNG_WRITE_sCAL_SUPPORTED

-/* Write the sCAL chunk */

-void /* PRIVATE */

-png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width,

-    png_const_charp height)

-{

-   png_byte buf[64];

-   png_size_t wlen, hlen, total_len;

-

-   png_debug(1, "in png_write_sCAL_s");

-

-   wlen = strlen(width);

-   hlen = strlen(height);

-   total_len = wlen + hlen + 2;

-

-   if (total_len > 64)

-   {

-      png_warning(png_ptr, "Can't write sCAL (buffer too small)");

-      return;

-   }

-

-   buf[0] = (png_byte)unit;

-   memcpy(buf + 1, width, wlen + 1);      /* Append the '\0' here */

-   memcpy(buf + wlen + 2, height, hlen);  /* Do NOT append the '\0' here */

-

-   png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);

-   png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len);

-}

-#endif

-

-#ifdef PNG_WRITE_pHYs_SUPPORTED

-/* Write the pHYs chunk */

-void /* PRIVATE */

-png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit,

-    png_uint_32 y_pixels_per_unit,

-    int unit_type)

-{

-   png_byte buf[9];

-

-   png_debug(1, "in png_write_pHYs");

-

-   if (unit_type >= PNG_RESOLUTION_LAST)

-      png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");

-

-   png_save_uint_32(buf, x_pixels_per_unit);

-   png_save_uint_32(buf + 4, y_pixels_per_unit);

-   buf[8] = (png_byte)unit_type;

-

-   png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);

-}

-#endif

-

-#ifdef PNG_WRITE_tIME_SUPPORTED

-/* Write the tIME chunk.  Use either png_convert_from_struct_tm()

- * or png_convert_from_time_t(), or fill in the structure yourself.

- */

-void /* PRIVATE */

-png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)

-{

-   png_byte buf[7];

-

-   png_debug(1, "in png_write_tIME");

-

-   if (mod_time->month  > 12 || mod_time->month  < 1 ||

-       mod_time->day    > 31 || mod_time->day    < 1 ||

-       mod_time->hour   > 23 || mod_time->second > 60)

-   {

-      png_warning(png_ptr, "Invalid time specified for tIME chunk");

-      return;

-   }

-

-   png_save_uint_16(buf, mod_time->year);

-   buf[2] = mod_time->month;

-   buf[3] = mod_time->day;

-   buf[4] = mod_time->hour;

-   buf[5] = mod_time->minute;

-   buf[6] = mod_time->second;

-

-   png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7);

-}

-#endif

-

-/* Initializes the row writing capability of libpng */

-void /* PRIVATE */

-png_write_start_row(png_structrp png_ptr)

-{

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */

-

-   /* Start of interlace block */

-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};

-

-   /* Offset to next interlace block */

-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};

-

-   /* Start of interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};

-

-   /* Offset to next interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};

-#endif

-

-   png_alloc_size_t buf_size;

-   int usr_pixel_depth;

-

-   png_debug(1, "in png_write_start_row");

-

-   usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth;

-   buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1;

-

-   /* 1.5.6: added to allow checking in the row write code. */

-   png_ptr->transformed_pixel_depth = png_ptr->pixel_depth;

-   png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth;

-

-   /* Set up row buffer */

-   png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, buf_size);

-

-   png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;

-

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-   /* Set up filtering buffer, if using this filter */

-   if (png_ptr->do_filter & PNG_FILTER_SUB)

-   {

-      png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1);

-

-      png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;

-   }

-

-   /* We only need to keep the previous row if we are using one of these. */

-   if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))

-   {

-      /* Set up previous row buffer */

-      png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, buf_size);

-

-      if (png_ptr->do_filter & PNG_FILTER_UP)

-      {

-         png_ptr->up_row = (png_bytep)png_malloc(png_ptr,

-            png_ptr->rowbytes + 1);

-

-         png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;

-      }

-

-      if (png_ptr->do_filter & PNG_FILTER_AVG)

-      {

-         png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,

-             png_ptr->rowbytes + 1);

-

-         png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;

-      }

-

-      if (png_ptr->do_filter & PNG_FILTER_PAETH)

-      {

-         png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,

-             png_ptr->rowbytes + 1);

-

-         png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;

-      }

-   }

-#endif /* PNG_WRITE_FILTER_SUPPORTED */

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* If interlaced, we need to set up width and height of pass */

-   if (png_ptr->interlaced)

-   {

-      if (!(png_ptr->transformations & PNG_INTERLACE))

-      {

-         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -

-             png_pass_ystart[0]) / png_pass_yinc[0];

-

-         png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -

-             png_pass_start[0]) / png_pass_inc[0];

-      }

-

-      else

-      {

-         png_ptr->num_rows = png_ptr->height;

-         png_ptr->usr_width = png_ptr->width;

-      }

-   }

-

-   else

-#endif

-   {

-      png_ptr->num_rows = png_ptr->height;

-      png_ptr->usr_width = png_ptr->width;

-   }

-}

-

-/* Internal use only.  Called when finished processing a row of data. */

-void /* PRIVATE */

-png_write_finish_row(png_structrp png_ptr)

-{

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */

-

-   /* Start of interlace block */

-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};

-

-   /* Offset to next interlace block */

-   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};

-

-   /* Start of interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};

-

-   /* Offset to next interlace block in the y direction */

-   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};

-#endif

-

-   png_debug(1, "in png_write_finish_row");

-

-   /* Next row */

-   png_ptr->row_number++;

-

-   /* See if we are done */

-   if (png_ptr->row_number < png_ptr->num_rows)

-      return;

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-   /* If interlaced, go to next pass */

-   if (png_ptr->interlaced)

-   {

-      png_ptr->row_number = 0;

-      if (png_ptr->transformations & PNG_INTERLACE)

-      {

-         png_ptr->pass++;

-      }

-

-      else

-      {

-         /* Loop until we find a non-zero width or height pass */

-         do

-         {

-            png_ptr->pass++;

-

-            if (png_ptr->pass >= 7)

-               break;

-

-            png_ptr->usr_width = (png_ptr->width +

-                png_pass_inc[png_ptr->pass] - 1 -

-                png_pass_start[png_ptr->pass]) /

-                png_pass_inc[png_ptr->pass];

-

-            png_ptr->num_rows = (png_ptr->height +

-                png_pass_yinc[png_ptr->pass] - 1 -

-                png_pass_ystart[png_ptr->pass]) /

-                png_pass_yinc[png_ptr->pass];

-

-            if (png_ptr->transformations & PNG_INTERLACE)

-               break;

-

-         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);

-

-      }

-

-      /* Reset the row above the image for the next pass */

-      if (png_ptr->pass < 7)

-      {

-         if (png_ptr->prev_row != NULL)

-            memset(png_ptr->prev_row, 0,

-                (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*

-                png_ptr->usr_bit_depth, png_ptr->width)) + 1);

-

-         return;

-      }

-   }

-#endif

-

-   /* If we get here, we've just written the last row, so we need

-      to flush the compressor */

-   png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);

-}

-

-#ifdef PNG_WRITE_INTERLACING_SUPPORTED

-/* Pick out the correct pixels for the interlace pass.

- * The basic idea here is to go through the row with a source

- * pointer and a destination pointer (sp and dp), and copy the

- * correct pixels for the pass.  As the row gets compacted,

- * sp will always be >= dp, so we should never overwrite anything.

- * See the default: case for the easiest code to understand.

- */

-void /* PRIVATE */

-png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)

-{

-   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */

-

-   /* Start of interlace block */

-   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};

-

-   /* Offset to next interlace block */

-   static PNG_CONST png_byte  png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};

-

-   png_debug(1, "in png_do_write_interlace");

-

-   /* We don't have to do anything on the last pass (6) */

-   if (pass < 6)

-   {

-      /* Each pixel depth is handled separately */

-      switch (row_info->pixel_depth)

-      {

-         case 1:

-         {

-            png_bytep sp;

-            png_bytep dp;

-            int shift;

-            int d;

-            int value;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            dp = row;

-            d = 0;

-            shift = 7;

-

-            for (i = png_pass_start[pass]; i < row_width;

-               i += png_pass_inc[pass])

-            {

-               sp = row + (png_size_t)(i >> 3);

-               value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;

-               d |= (value << shift);

-

-               if (shift == 0)

-               {

-                  shift = 7;

-                  *dp++ = (png_byte)d;

-                  d = 0;

-               }

-

-               else

-                  shift--;

-

-            }

-            if (shift != 7)

-               *dp = (png_byte)d;

-

-            break;

-         }

-

-         case 2:

-         {

-            png_bytep sp;

-            png_bytep dp;

-            int shift;

-            int d;

-            int value;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            dp = row;

-            shift = 6;

-            d = 0;

-

-            for (i = png_pass_start[pass]; i < row_width;

-               i += png_pass_inc[pass])

-            {

-               sp = row + (png_size_t)(i >> 2);

-               value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;

-               d |= (value << shift);

-

-               if (shift == 0)

-               {

-                  shift = 6;

-                  *dp++ = (png_byte)d;

-                  d = 0;

-               }

-

-               else

-                  shift -= 2;

-            }

-            if (shift != 6)

-               *dp = (png_byte)d;

-

-            break;

-         }

-

-         case 4:

-         {

-            png_bytep sp;

-            png_bytep dp;

-            int shift;

-            int d;

-            int value;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-

-            dp = row;

-            shift = 4;

-            d = 0;

-            for (i = png_pass_start[pass]; i < row_width;

-                i += png_pass_inc[pass])

-            {

-               sp = row + (png_size_t)(i >> 1);

-               value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;

-               d |= (value << shift);

-

-               if (shift == 0)

-               {

-                  shift = 4;

-                  *dp++ = (png_byte)d;

-                  d = 0;

-               }

-

-               else

-                  shift -= 4;

-            }

-            if (shift != 4)

-               *dp = (png_byte)d;

-

-            break;

-         }

-

-         default:

-         {

-            png_bytep sp;

-            png_bytep dp;

-            png_uint_32 i;

-            png_uint_32 row_width = row_info->width;

-            png_size_t pixel_bytes;

-

-            /* Start at the beginning */

-            dp = row;

-

-            /* Find out how many bytes each pixel takes up */

-            pixel_bytes = (row_info->pixel_depth >> 3);

-

-            /* Loop through the row, only looking at the pixels that matter */

-            for (i = png_pass_start[pass]; i < row_width;

-               i += png_pass_inc[pass])

-            {

-               /* Find out where the original pixel is */

-               sp = row + (png_size_t)i * pixel_bytes;

-

-               /* Move the pixel */

-               if (dp != sp)

-                  memcpy(dp, sp, pixel_bytes);

-

-               /* Next pixel */

-               dp += pixel_bytes;

-            }

-            break;

-         }

-      }

-      /* Set new row width */

-      row_info->width = (row_info->width +

-          png_pass_inc[pass] - 1 -

-          png_pass_start[pass]) /

-          png_pass_inc[pass];

-

-      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,

-          row_info->width);

-   }

-}

-#endif

-

-/* This filters the row, chooses which filter to use, if it has not already

- * been specified by the application, and then writes the row out with the

- * chosen filter.

- */

-static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,

-   png_size_t row_bytes);

-

-#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)

-#define PNG_HISHIFT 10

-#define PNG_LOMASK ((png_uint_32)0xffffL)

-#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))

-void /* PRIVATE */

-png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)

-{

-   png_bytep best_row;

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-   png_bytep prev_row, row_buf;

-   png_uint_32 mins, bpp;

-   png_byte filter_to_do = png_ptr->do_filter;

-   png_size_t row_bytes = row_info->rowbytes;

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-   int num_p_filters = png_ptr->num_prev_filters;

-#endif

-

-   png_debug(1, "in png_write_find_filter");

-

-#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-  if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS)

-  {

-     /* These will never be selected so we need not test them. */

-     filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH);

-  }

-#endif

-

-   /* Find out how many bytes offset each pixel is */

-   bpp = (row_info->pixel_depth + 7) >> 3;

-

-   prev_row = png_ptr->prev_row;

-#endif

-   best_row = png_ptr->row_buf;

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-   row_buf = best_row;

-   mins = PNG_MAXSUM;

-

-   /* The prediction method we use is to find which method provides the

-    * smallest value when summing the absolute values of the distances

-    * from zero, using anything >= 128 as negative numbers.  This is known

-    * as the "minimum sum of absolute differences" heuristic.  Other

-    * heuristics are the "weighted minimum sum of absolute differences"

-    * (experimental and can in theory improve compression), and the "zlib

-    * predictive" method (not implemented yet), which does test compressions

-    * of lines using different filter methods, and then chooses the

-    * (series of) filter(s) that give minimum compressed data size (VERY

-    * computationally expensive).

-    *

-    * GRR 980525:  consider also

-    *

-    *   (1) minimum sum of absolute differences from running average (i.e.,

-    *       keep running sum of non-absolute differences & count of bytes)

-    *       [track dispersion, too?  restart average if dispersion too large?]

-    *

-    *  (1b) minimum sum of absolute differences from sliding average, probably

-    *       with window size <= deflate window (usually 32K)

-    *

-    *   (2) minimum sum of squared differences from zero or running average

-    *       (i.e., ~ root-mean-square approach)

-    */

-

-

-   /* We don't need to test the 'no filter' case if this is the only filter

-    * that has been chosen, as it doesn't actually do anything to the data.

-    */

-   if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE)

-   {

-      png_bytep rp;

-      png_uint_32 sum = 0;

-      png_size_t i;

-      int v;

-

-      for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)

-      {

-         v = *rp;

-         sum += (v < 128) ? v : 256 - v;

-      }

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         png_uint_32 sumhi, sumlo;

-         int j;

-         sumlo = sum & PNG_LOMASK;

-         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */

-

-         /* Reduce the sum if we match any of the previous rows */

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)

-            {

-               sumlo = (sumlo * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               sumhi = (sumhi * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         /* Factor in the cost of this filter (this is here for completeness,

-          * but it makes no sense to have a "cost" for the NONE filter, as

-          * it has the minimum possible computational cost - none).

-          */

-         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>

-             PNG_COST_SHIFT;

-

-         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>

-             PNG_COST_SHIFT;

-

-         if (sumhi > PNG_HIMASK)

-            sum = PNG_MAXSUM;

-

-         else

-            sum = (sumhi << PNG_HISHIFT) + sumlo;

-      }

-#endif

-      mins = sum;

-   }

-

-   /* Sub filter */

-   if (filter_to_do == PNG_FILTER_SUB)

-   /* It's the only filter so no testing is needed */

-   {

-      png_bytep rp, lp, dp;

-      png_size_t i;

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;

-           i++, rp++, dp++)

-      {

-         *dp = *rp;

-      }

-

-      for (lp = row_buf + 1; i < row_bytes;

-         i++, rp++, lp++, dp++)

-      {

-         *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);

-      }

-

-      best_row = png_ptr->sub_row;

-   }

-

-   else if (filter_to_do & PNG_FILTER_SUB)

-   {

-      png_bytep rp, dp, lp;

-      png_uint_32 sum = 0, lmins = mins;

-      png_size_t i;

-      int v;

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      /* We temporarily increase the "minimum sum" by the factor we

-       * would reduce the sum of this filter, so that we can do the

-       * early exit comparison without scaling the sum each time.

-       */

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 lmhi, lmlo;

-         lmlo = lmins & PNG_LOMASK;

-         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)

-            {

-               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>

-             PNG_COST_SHIFT;

-

-         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>

-             PNG_COST_SHIFT;

-

-         if (lmhi > PNG_HIMASK)

-            lmins = PNG_MAXSUM;

-

-         else

-            lmins = (lmhi << PNG_HISHIFT) + lmlo;

-      }

-#endif

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;

-           i++, rp++, dp++)

-      {

-         v = *dp = *rp;

-

-         sum += (v < 128) ? v : 256 - v;

-      }

-

-      for (lp = row_buf + 1; i < row_bytes;

-         i++, rp++, lp++, dp++)

-      {

-         v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);

-

-         sum += (v < 128) ? v : 256 - v;

-

-         if (sum > lmins)  /* We are already worse, don't continue. */

-            break;

-      }

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 sumhi, sumlo;

-         sumlo = sum & PNG_LOMASK;

-         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)

-            {

-               sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>

-             PNG_COST_SHIFT;

-

-         sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>

-             PNG_COST_SHIFT;

-

-         if (sumhi > PNG_HIMASK)

-            sum = PNG_MAXSUM;

-

-         else

-            sum = (sumhi << PNG_HISHIFT) + sumlo;

-      }

-#endif

-

-      if (sum < mins)

-      {

-         mins = sum;

-         best_row = png_ptr->sub_row;

-      }

-   }

-

-   /* Up filter */

-   if (filter_to_do == PNG_FILTER_UP)

-   {

-      png_bytep rp, dp, pp;

-      png_size_t i;

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,

-          pp = prev_row + 1; i < row_bytes;

-          i++, rp++, pp++, dp++)

-      {

-         *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);

-      }

-

-      best_row = png_ptr->up_row;

-   }

-

-   else if (filter_to_do & PNG_FILTER_UP)

-   {

-      png_bytep rp, dp, pp;

-      png_uint_32 sum = 0, lmins = mins;

-      png_size_t i;

-      int v;

-

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 lmhi, lmlo;

-         lmlo = lmins & PNG_LOMASK;

-         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)

-            {

-               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>

-             PNG_COST_SHIFT;

-

-         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>

-             PNG_COST_SHIFT;

-

-         if (lmhi > PNG_HIMASK)

-            lmins = PNG_MAXSUM;

-

-         else

-            lmins = (lmhi << PNG_HISHIFT) + lmlo;

-      }

-#endif

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,

-          pp = prev_row + 1; i < row_bytes; i++)

-      {

-         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);

-

-         sum += (v < 128) ? v : 256 - v;

-

-         if (sum > lmins)  /* We are already worse, don't continue. */

-            break;

-      }

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 sumhi, sumlo;

-         sumlo = sum & PNG_LOMASK;

-         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)

-            {

-               sumlo = (sumlo * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               sumhi = (sumhi * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>

-             PNG_COST_SHIFT;

-

-         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>

-             PNG_COST_SHIFT;

-

-         if (sumhi > PNG_HIMASK)

-            sum = PNG_MAXSUM;

-

-         else

-            sum = (sumhi << PNG_HISHIFT) + sumlo;

-      }

-#endif

-

-      if (sum < mins)

-      {

-         mins = sum;

-         best_row = png_ptr->up_row;

-      }

-   }

-

-   /* Avg filter */

-   if (filter_to_do == PNG_FILTER_AVG)

-   {

-      png_bytep rp, dp, pp, lp;

-      png_uint_32 i;

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,

-           pp = prev_row + 1; i < bpp; i++)

-      {

-         *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);

-      }

-

-      for (lp = row_buf + 1; i < row_bytes; i++)

-      {

-         *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))

-                 & 0xff);

-      }

-      best_row = png_ptr->avg_row;

-   }

-

-   else if (filter_to_do & PNG_FILTER_AVG)

-   {

-      png_bytep rp, dp, pp, lp;

-      png_uint_32 sum = 0, lmins = mins;

-      png_size_t i;

-      int v;

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 lmhi, lmlo;

-         lmlo = lmins & PNG_LOMASK;

-         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)

-            {

-               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>

-             PNG_COST_SHIFT;

-

-         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>

-             PNG_COST_SHIFT;

-

-         if (lmhi > PNG_HIMASK)

-            lmins = PNG_MAXSUM;

-

-         else

-            lmins = (lmhi << PNG_HISHIFT) + lmlo;

-      }

-#endif

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,

-           pp = prev_row + 1; i < bpp; i++)

-      {

-         v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);

-

-         sum += (v < 128) ? v : 256 - v;

-      }

-

-      for (lp = row_buf + 1; i < row_bytes; i++)

-      {

-         v = *dp++ =

-             (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);

-

-         sum += (v < 128) ? v : 256 - v;

-

-         if (sum > lmins)  /* We are already worse, don't continue. */

-            break;

-      }

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 sumhi, sumlo;

-         sumlo = sum & PNG_LOMASK;

-         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)

-            {

-               sumlo = (sumlo * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               sumhi = (sumhi * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>

-             PNG_COST_SHIFT;

-

-         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>

-             PNG_COST_SHIFT;

-

-         if (sumhi > PNG_HIMASK)

-            sum = PNG_MAXSUM;

-

-         else

-            sum = (sumhi << PNG_HISHIFT) + sumlo;

-      }

-#endif

-

-      if (sum < mins)

-      {

-         mins = sum;

-         best_row = png_ptr->avg_row;

-      }

-   }

-

-   /* Paeth filter */

-   if (filter_to_do == PNG_FILTER_PAETH)

-   {

-      png_bytep rp, dp, pp, cp, lp;

-      png_size_t i;

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,

-          pp = prev_row + 1; i < bpp; i++)

-      {

-         *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);

-      }

-

-      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)

-      {

-         int a, b, c, pa, pb, pc, p;

-

-         b = *pp++;

-         c = *cp++;

-         a = *lp++;

-

-         p = b - c;

-         pc = a - c;

-

-#ifdef PNG_USE_ABS

-         pa = abs(p);

-         pb = abs(pc);

-         pc = abs(p + pc);

-#else

-         pa = p < 0 ? -p : p;

-         pb = pc < 0 ? -pc : pc;

-         pc = (p + pc) < 0 ? -(p + pc) : p + pc;

-#endif

-

-         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;

-

-         *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);

-      }

-      best_row = png_ptr->paeth_row;

-   }

-

-   else if (filter_to_do & PNG_FILTER_PAETH)

-   {

-      png_bytep rp, dp, pp, cp, lp;

-      png_uint_32 sum = 0, lmins = mins;

-      png_size_t i;

-      int v;

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 lmhi, lmlo;

-         lmlo = lmins & PNG_LOMASK;

-         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)

-            {

-               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>

-             PNG_COST_SHIFT;

-

-         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>

-             PNG_COST_SHIFT;

-

-         if (lmhi > PNG_HIMASK)

-            lmins = PNG_MAXSUM;

-

-         else

-            lmins = (lmhi << PNG_HISHIFT) + lmlo;

-      }

-#endif

-

-      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,

-          pp = prev_row + 1; i < bpp; i++)

-      {

-         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);

-

-         sum += (v < 128) ? v : 256 - v;

-      }

-

-      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)

-      {

-         int a, b, c, pa, pb, pc, p;

-

-         b = *pp++;

-         c = *cp++;

-         a = *lp++;

-

-#ifndef PNG_SLOW_PAETH

-         p = b - c;

-         pc = a - c;

-#ifdef PNG_USE_ABS

-         pa = abs(p);

-         pb = abs(pc);

-         pc = abs(p + pc);

-#else

-         pa = p < 0 ? -p : p;

-         pb = pc < 0 ? -pc : pc;

-         pc = (p + pc) < 0 ? -(p + pc) : p + pc;

-#endif

-         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;

-#else /* PNG_SLOW_PAETH */

-         p = a + b - c;

-         pa = abs(p - a);

-         pb = abs(p - b);

-         pc = abs(p - c);

-

-         if (pa <= pb && pa <= pc)

-            p = a;

-

-         else if (pb <= pc)

-            p = b;

-

-         else

-            p = c;

-#endif /* PNG_SLOW_PAETH */

-

-         v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);

-

-         sum += (v < 128) ? v : 256 - v;

-

-         if (sum > lmins)  /* We are already worse, don't continue. */

-            break;

-      }

-

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)

-      {

-         int j;

-         png_uint_32 sumhi, sumlo;

-         sumlo = sum & PNG_LOMASK;

-         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;

-

-         for (j = 0; j < num_p_filters; j++)

-         {

-            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)

-            {

-               sumlo = (sumlo * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-

-               sumhi = (sumhi * png_ptr->filter_weights[j]) >>

-                   PNG_WEIGHT_SHIFT;

-            }

-         }

-

-         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>

-             PNG_COST_SHIFT;

-

-         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>

-             PNG_COST_SHIFT;

-

-         if (sumhi > PNG_HIMASK)

-            sum = PNG_MAXSUM;

-

-         else

-            sum = (sumhi << PNG_HISHIFT) + sumlo;

-      }

-#endif

-

-      if (sum < mins)

-      {

-         best_row = png_ptr->paeth_row;

-      }

-   }

-#endif /* PNG_WRITE_FILTER_SUPPORTED */

-

-   /* Do the actual writing of the filtered row data from the chosen filter. */

-   png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1);

-

-#ifdef PNG_WRITE_FILTER_SUPPORTED

-#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED

-   /* Save the type of filter we picked this time for future calculations */

-   if (png_ptr->num_prev_filters > 0)

-   {

-      int j;

-

-      for (j = 1; j < num_p_filters; j++)

-      {

-         png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];

-      }

-

-      png_ptr->prev_filters[j] = best_row[0];

-   }

-#endif

-#endif /* PNG_WRITE_FILTER_SUPPORTED */

-}

-

-

-/* Do the actual writing of a previously filtered row. */

-static void

-png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,

-   png_size_t full_row_length/*includes filter byte*/)

-{

-   png_debug(1, "in png_write_filtered_row");

-

-   png_debug1(2, "filter = %d", filtered_row[0]);

-

-   png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);

-

-   /* Swap the current and previous rows */

-   if (png_ptr->prev_row != NULL)

-   {

-      png_bytep tptr;

-

-      tptr = png_ptr->prev_row;

-      png_ptr->prev_row = png_ptr->row_buf;

-      png_ptr->row_buf = tptr;

-   }

-

-   /* Finish row - updates counters and flushes zlib if last row */

-   png_write_finish_row(png_ptr);

-

-#ifdef PNG_WRITE_FLUSH_SUPPORTED

-   png_ptr->flush_rows++;

-

-   if (png_ptr->flush_dist > 0 &&

-       png_ptr->flush_rows >= png_ptr->flush_dist)

-   {

-      png_write_flush(png_ptr);

-   }

-#endif

-}

-#endif /* PNG_WRITE_SUPPORTED */

+
+/* pngwutil.c - utilities to write a PNG file
+ *
+ * Last changed in libpng 1.6.19 [November 12, 2015]
+ * Copyright (c) 1998-2015 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.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+
+#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+/* Place a 32-bit number into a buffer in PNG byte order.  We work
+ * with unsigned numbers for convenience, although one supported
+ * ancillary chunk uses signed (two's complement) numbers.
+ */
+void PNGAPI
+png_save_uint_32(png_bytep buf, png_uint_32 i)
+{
+   buf[0] = (png_byte)(i >> 24);
+   buf[1] = (png_byte)(i >> 16);
+   buf[2] = (png_byte)(i >> 8);
+   buf[3] = (png_byte)(i     );
+}
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+void PNGAPI
+png_save_uint_16(png_bytep buf, unsigned int i)
+{
+   buf[0] = (png_byte)(i >> 8);
+   buf[1] = (png_byte)(i     );
+}
+#endif
+
+/* Simple function to write the signature.  If we have already written
+ * the magic bytes of the signature, or more likely, the PNG stream is
+ * being embedded into another stream and doesn't need its own signature,
+ * we should call png_set_sig_bytes() to tell libpng how many of the
+ * bytes have already been written.
+ */
+void PNGAPI
+png_write_sig(png_structrp png_ptr)
+{
+   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   /* Inform the I/O callback that the signature is being written */
+   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE;
+#endif
+
+   /* Write the rest of the 8 byte signature */
+   png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
+      (png_size_t)(8 - png_ptr->sig_bytes));
+
+   if (png_ptr->sig_bytes < 3)
+      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+}
+
+/* Write the start of a PNG chunk.  The type is the chunk type.
+ * The total_length is the sum of the lengths of all the data you will be
+ * passing in png_write_chunk_data().
+ */
+static void
+png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name,
+    png_uint_32 length)
+{
+   png_byte buf[8];
+
+#if defined(PNG_DEBUG) && (PNG_DEBUG > 0)
+   PNG_CSTRING_FROM_CHUNK(buf, chunk_name);
+   png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length);
+#endif
+
+   if (png_ptr == NULL)
+      return;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   /* Inform the I/O callback that the chunk header is being written.
+    * PNG_IO_CHUNK_HDR requires a single I/O call.
+    */
+   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR;
+#endif
+
+   /* Write the length and the chunk name */
+   png_save_uint_32(buf, length);
+   png_save_uint_32(buf + 4, chunk_name);
+   png_write_data(png_ptr, buf, 8);
+
+   /* Put the chunk name into png_ptr->chunk_name */
+   png_ptr->chunk_name = chunk_name;
+
+   /* Reset the crc and run it over the chunk name */
+   png_reset_crc(png_ptr);
+
+   png_calculate_crc(png_ptr, buf + 4, 4);
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   /* Inform the I/O callback that chunk data will (possibly) be written.
+    * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls.
+    */
+   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA;
+#endif
+}
+
+void PNGAPI
+png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string,
+    png_uint_32 length)
+{
+   png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length);
+}
+
+/* Write the data of a PNG chunk started with png_write_chunk_header().
+ * Note that multiple calls to this function are allowed, and that the
+ * sum of the lengths from these calls *must* add up to the total_length
+ * given to png_write_chunk_header().
+ */
+void PNGAPI
+png_write_chunk_data(png_structrp png_ptr, png_const_bytep data,
+    png_size_t length)
+{
+   /* Write the data, and run the CRC over it */
+   if (png_ptr == NULL)
+      return;
+
+   if (data != NULL && length > 0)
+   {
+      png_write_data(png_ptr, data, length);
+
+      /* Update the CRC after writing the data,
+       * in case the user I/O routine alters it.
+       */
+      png_calculate_crc(png_ptr, data, length);
+   }
+}
+
+/* Finish a chunk started with png_write_chunk_header(). */
+void PNGAPI
+png_write_chunk_end(png_structrp png_ptr)
+{
+   png_byte buf[4];
+
+   if (png_ptr == NULL) return;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+   /* Inform the I/O callback that the chunk CRC is being written.
+    * PNG_IO_CHUNK_CRC requires a single I/O function call.
+    */
+   png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC;
+#endif
+
+   /* Write the crc in a single operation */
+   png_save_uint_32(buf, png_ptr->crc);
+
+   png_write_data(png_ptr, buf, (png_size_t)4);
+}
+
+/* Write a PNG chunk all at once.  The type is an array of ASCII characters
+ * representing the chunk name.  The array must be at least 4 bytes in
+ * length, and does not need to be null terminated.  To be safe, pass the
+ * pre-defined chunk names here, and if you need a new one, define it
+ * where the others are defined.  The length is the length of the data.
+ * All the data must be present.  If that is not possible, use the
+ * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
+ * functions instead.
+ */
+static void
+png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,
+   png_const_bytep data, png_size_t length)
+{
+   if (png_ptr == NULL)
+      return;
+
+   /* On 64-bit architectures 'length' may not fit in a png_uint_32. */
+   if (length > PNG_UINT_31_MAX)
+      png_error(png_ptr, "length exceeds PNG maximum");
+
+   png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);
+   png_write_chunk_data(png_ptr, data, length);
+   png_write_chunk_end(png_ptr);
+}
+
+/* This is the API that calls the internal function above. */
+void PNGAPI
+png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,
+   png_const_bytep data, png_size_t length)
+{
+   png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data,
+      length);
+}
+
+/* This is used below to find the size of an image to pass to png_deflate_claim,
+ * so it only needs to be accurate if the size is less than 16384 bytes (the
+ * point at which a lower LZ window size can be used.)
+ */
+static png_alloc_size_t
+png_image_size(png_structrp png_ptr)
+{
+   /* Only return sizes up to the maximum of a png_uint_32; do this by limiting
+    * the width and height used to 15 bits.
+    */
+   png_uint_32 h = png_ptr->height;
+
+   if (png_ptr->rowbytes < 32768 && h < 32768)
+   {
+      if (png_ptr->interlaced != 0)
+      {
+         /* Interlacing makes the image larger because of the replication of
+          * both the filter byte and the padding to a byte boundary.
+          */
+         png_uint_32 w = png_ptr->width;
+         unsigned int pd = png_ptr->pixel_depth;
+         png_alloc_size_t cb_base;
+         int pass;
+
+         for (cb_base=0, pass=0; pass<=6; ++pass)
+         {
+            png_uint_32 pw = PNG_PASS_COLS(w, pass);
+
+            if (pw > 0)
+               cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass);
+         }
+
+         return cb_base;
+      }
+
+      else
+         return (png_ptr->rowbytes+1) * h;
+   }
+
+   else
+      return 0xffffffffU;
+}
+
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+   /* This is the code to hack the first two bytes of the deflate stream (the
+    * deflate header) to correct the windowBits value to match the actual data
+    * size.  Note that the second argument is the *uncompressed* size but the
+    * first argument is the *compressed* data (and it must be deflate
+    * compressed.)
+    */
+static void
+optimize_cmf(png_bytep data, png_alloc_size_t data_size)
+{
+   /* Optimize the CMF field in the zlib stream.  The resultant zlib stream is
+    * still compliant to the stream specification.
+    */
+   if (data_size <= 16384) /* else windowBits must be 15 */
+   {
+      unsigned int z_cmf = data[0];  /* zlib compression method and flags */
+
+      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+      {
+         unsigned int z_cinfo;
+         unsigned int half_z_window_size;
+
+         z_cinfo = z_cmf >> 4;
+         half_z_window_size = 1U << (z_cinfo + 7);
+
+         if (data_size <= half_z_window_size) /* else no change */
+         {
+            unsigned int tmp;
+
+            do
+            {
+               half_z_window_size >>= 1;
+               --z_cinfo;
+            }
+            while (z_cinfo > 0 && data_size <= half_z_window_size);
+
+            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+
+            data[0] = (png_byte)z_cmf;
+            tmp = data[1] & 0xe0;
+            tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
+            data[1] = (png_byte)tmp;
+         }
+      }
+   }
+}
+#endif /* WRITE_OPTIMIZE_CMF */
+
+/* Initialize the compressor for the appropriate type of compression. */
+static int
+png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
+   png_alloc_size_t data_size)
+{
+   if (png_ptr->zowner != 0)
+   {
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED)
+      char msg[64];
+
+      PNG_STRING_FROM_CHUNK(msg, owner);
+      msg[4] = ':';
+      msg[5] = ' ';
+      PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);
+      /* So the message that results is "<chunk> using zstream"; this is an
+       * internal error, but is very useful for debugging.  i18n requirements
+       * are minimal.
+       */
+      (void)png_safecat(msg, (sizeof msg), 10, " using zstream");
+#endif
+#if PNG_RELEASE_BUILD
+         png_warning(png_ptr, msg);
+
+         /* Attempt sane error recovery */
+         if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */
+         {
+            png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");
+            return Z_STREAM_ERROR;
+         }
+
+         png_ptr->zowner = 0;
+#else
+         png_error(png_ptr, msg);
+#endif
+   }
+
+   {
+      int level = png_ptr->zlib_level;
+      int method = png_ptr->zlib_method;
+      int windowBits = png_ptr->zlib_window_bits;
+      int memLevel = png_ptr->zlib_mem_level;
+      int strategy; /* set below */
+      int ret; /* zlib return code */
+
+      if (owner == png_IDAT)
+      {
+         if ((png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) != 0)
+            strategy = png_ptr->zlib_strategy;
+
+         else if (png_ptr->do_filter != PNG_FILTER_NONE)
+            strategy = PNG_Z_DEFAULT_STRATEGY;
+
+         else
+            strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY;
+      }
+
+      else
+      {
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+            level = png_ptr->zlib_text_level;
+            method = png_ptr->zlib_text_method;
+            windowBits = png_ptr->zlib_text_window_bits;
+            memLevel = png_ptr->zlib_text_mem_level;
+            strategy = png_ptr->zlib_text_strategy;
+#else
+            /* If customization is not supported the values all come from the
+             * IDAT values except for the strategy, which is fixed to the
+             * default.  (This is the pre-1.6.0 behavior too, although it was
+             * implemented in a very different way.)
+             */
+            strategy = Z_DEFAULT_STRATEGY;
+#endif
+      }
+
+      /* Adjust 'windowBits' down if larger than 'data_size'; to stop this
+       * happening just pass 32768 as the data_size parameter.  Notice that zlib
+       * requires an extra 262 bytes in the window in addition to the data to be
+       * able to see the whole of the data, so if data_size+262 takes us to the
+       * next windowBits size we need to fix up the value later.  (Because even
+       * though deflate needs the extra window, inflate does not!)
+       */
+      if (data_size <= 16384)
+      {
+         /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
+          * work round a Microsoft Visual C misbehavior which, contrary to C-90,
+          * widens the result of the following shift to 64-bits if (and,
+          * apparently, only if) it is used in a test.
+          */
+         unsigned int half_window_size = 1U << (windowBits-1);
+
+         while (data_size + 262 <= half_window_size)
+         {
+            half_window_size >>= 1;
+            --windowBits;
+         }
+      }
+
+      /* Check against the previous initialized values, if any. */
+      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0 &&
+         (png_ptr->zlib_set_level != level ||
+         png_ptr->zlib_set_method != method ||
+         png_ptr->zlib_set_window_bits != windowBits ||
+         png_ptr->zlib_set_mem_level != memLevel ||
+         png_ptr->zlib_set_strategy != strategy))
+      {
+         if (deflateEnd(&png_ptr->zstream) != Z_OK)
+            png_warning(png_ptr, "deflateEnd failed (ignored)");
+
+         png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;
+      }
+
+      /* For safety clear out the input and output pointers (currently zlib
+       * doesn't use them on Init, but it might in the future).
+       */
+      png_ptr->zstream.next_in = NULL;
+      png_ptr->zstream.avail_in = 0;
+      png_ptr->zstream.next_out = NULL;
+      png_ptr->zstream.avail_out = 0;
+
+      /* Now initialize if required, setting the new parameters, otherwise just
+       * to a simple reset to the previous parameters.
+       */
+      if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
+         ret = deflateReset(&png_ptr->zstream);
+
+      else
+      {
+         ret = deflateInit2(&png_ptr->zstream, level, method, windowBits,
+            memLevel, strategy);
+
+         if (ret == Z_OK)
+            png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
+      }
+
+      /* The return code is from either deflateReset or deflateInit2; they have
+       * pretty much the same set of error codes.
+       */
+      if (ret == Z_OK)
+         png_ptr->zowner = owner;
+
+      else
+         png_zstream_error(png_ptr, ret);
+
+      return ret;
+   }
+}
+
+/* Clean up (or trim) a linked list of compression buffers. */
+void /* PRIVATE */
+png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
+{
+   png_compression_bufferp list = *listp;
+
+   if (list != NULL)
+   {
+      *listp = NULL;
+
+      do
+      {
+         png_compression_bufferp next = list->next;
+
+         png_free(png_ptr, list);
+         list = next;
+      }
+      while (list != NULL);
+   }
+}
+
+#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+/* This pair of functions encapsulates the operation of (a) compressing a
+ * text string, and (b) issuing it later as a series of chunk data writes.
+ * The compression_state structure is shared context for these functions
+ * set up by the caller to allow access to the relevant local variables.
+ *
+ * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
+ * temporary buffers.  From 1.6.0 it is retained in png_struct so that it will
+ * be correctly freed in the event of a write error (previous implementations
+ * just leaked memory.)
+ */
+typedef struct
+{
+   png_const_bytep      input;        /* The uncompressed input data */
+   png_alloc_size_t     input_len;    /* Its length */
+   png_uint_32          output_len;   /* Final compressed length */
+   png_byte             output[1024]; /* First block of output */
+} compression_state;
+
+static void
+png_text_compress_init(compression_state *comp, png_const_bytep input,
+   png_alloc_size_t input_len)
+{
+   comp->input = input;
+   comp->input_len = input_len;
+   comp->output_len = 0;
+}
+
+/* Compress the data in the compression state input */
+static int
+png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
+   compression_state *comp, png_uint_32 prefix_len)
+{
+   int ret;
+
+   /* To find the length of the output it is necessary to first compress the
+    * input. The result is buffered rather than using the two-pass algorithm
+    * that is used on the inflate side; deflate is assumed to be slower and a
+    * PNG writer is assumed to have more memory available than a PNG reader.
+    *
+    * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an
+    * upper limit on the output size, but it is always bigger than the input
+    * size so it is likely to be more efficient to use this linked-list
+    * approach.
+    */
+   ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);
+
+   if (ret != Z_OK)
+      return ret;
+
+   /* Set up the compression buffers, we need a loop here to avoid overflowing a
+    * uInt.  Use ZLIB_IO_MAX to limit the input.  The output is always limited
+    * by the output buffer size, so there is no need to check that.  Since this
+    * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
+    * in size.
+    */
+   {
+      png_compression_bufferp *end = &png_ptr->zbuffer_list;
+      png_alloc_size_t input_len = comp->input_len; /* may be zero! */
+      png_uint_32 output_len;
+
+      /* zlib updates these for us: */
+      png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);
+      png_ptr->zstream.avail_in = 0; /* Set below */
+      png_ptr->zstream.next_out = comp->output;
+      png_ptr->zstream.avail_out = (sizeof comp->output);
+
+      output_len = png_ptr->zstream.avail_out;
+
+      do
+      {
+         uInt avail_in = ZLIB_IO_MAX;
+
+         if (avail_in > input_len)
+            avail_in = (uInt)input_len;
+
+         input_len -= avail_in;
+
+         png_ptr->zstream.avail_in = avail_in;
+
+         if (png_ptr->zstream.avail_out == 0)
+         {
+            png_compression_buffer *next;
+
+            /* Chunk data is limited to 2^31 bytes in length, so the prefix
+             * length must be counted here.
+             */
+            if (output_len + prefix_len > PNG_UINT_31_MAX)
+            {
+               ret = Z_MEM_ERROR;
+               break;
+            }
+
+            /* Need a new (malloc'ed) buffer, but there may be one present
+             * already.
+             */
+            next = *end;
+            if (next == NULL)
+            {
+               next = png_voidcast(png_compression_bufferp, png_malloc_base
+                  (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
+
+               if (next == NULL)
+               {
+                  ret = Z_MEM_ERROR;
+                  break;
+               }
+
+               /* Link in this buffer (so that it will be freed later) */
+               next->next = NULL;
+               *end = next;
+            }
+
+            png_ptr->zstream.next_out = next->output;
+            png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
+            output_len += png_ptr->zstream.avail_out;
+
+            /* Move 'end' to the next buffer pointer. */
+            end = &next->next;
+         }
+
+         /* Compress the data */
+         ret = deflate(&png_ptr->zstream,
+            input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
+
+         /* Claw back input data that was not consumed (because avail_in is
+          * reset above every time round the loop).
+          */
+         input_len += png_ptr->zstream.avail_in;
+         png_ptr->zstream.avail_in = 0; /* safety */
+      }
+      while (ret == Z_OK);
+
+      /* There may be some space left in the last output buffer. This needs to
+       * be subtracted from output_len.
+       */
+      output_len -= png_ptr->zstream.avail_out;
+      png_ptr->zstream.avail_out = 0; /* safety */
+      comp->output_len = output_len;
+
+      /* Now double check the output length, put in a custom message if it is
+       * too long.  Otherwise ensure the z_stream::msg pointer is set to
+       * something.
+       */
+      if (output_len + prefix_len >= PNG_UINT_31_MAX)
+      {
+         png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");
+         ret = Z_MEM_ERROR;
+      }
+
+      else
+         png_zstream_error(png_ptr, ret);
+
+      /* Reset zlib for another zTXt/iTXt or image data */
+      png_ptr->zowner = 0;
+
+      /* The only success case is Z_STREAM_END, input_len must be 0; if not this
+       * is an internal error.
+       */
+      if (ret == Z_STREAM_END && input_len == 0)
+      {
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+         /* Fix up the deflate header, if required */
+         optimize_cmf(comp->output, comp->input_len);
+#endif
+         /* But Z_OK is returned, not Z_STREAM_END; this allows the claim
+          * function above to return Z_STREAM_END on an error (though it never
+          * does in the current versions of zlib.)
+          */
+         return Z_OK;
+      }
+
+      else
+         return ret;
+   }
+}
+
+/* Ship the compressed text out via chunk writes */
+static void
+png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
+{
+   png_uint_32 output_len = comp->output_len;
+   png_const_bytep output = comp->output;
+   png_uint_32 avail = (sizeof comp->output);
+   png_compression_buffer *next = png_ptr->zbuffer_list;
+
+   for (;;)
+   {
+      if (avail > output_len)
+         avail = output_len;
+
+      png_write_chunk_data(png_ptr, output, avail);
+
+      output_len -= avail;
+
+      if (output_len == 0 || next == NULL)
+         break;
+
+      avail = png_ptr->zbuffer_size;
+      output = next->output;
+      next = next->next;
+   }
+
+   /* This is an internal error; 'next' must have been NULL! */
+   if (output_len > 0)
+      png_error(png_ptr, "error writing ancillary chunked compressed data");
+}
+#endif /* WRITE_COMPRESSED_TEXT */
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
+ *
+ * The 'new_key' buffer must be 80 characters in size (for the keyword plus a
+ * trailing '\0').  If this routine returns 0 then there was no keyword, or a
+ * valid one could not be generated, and the caller must png_error.
+ */
+static png_uint_32
+png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
+{
+   png_const_charp orig_key = key;
+   png_uint_32 key_len = 0;
+   int bad_character = 0;
+   int space = 1;
+
+   png_debug(1, "in png_check_keyword");
+
+   if (key == NULL)
+   {
+      *new_key = 0;
+      return 0;
+   }
+
+   while (*key && key_len < 79)
+   {
+      png_byte ch = (png_byte)*key++;
+
+      if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))
+         *new_key++ = ch, ++key_len, space = 0;
+
+      else if (space == 0)
+      {
+         /* A space or an invalid character when one wasn't seen immediately
+          * before; output just a space.
+          */
+         *new_key++ = 32, ++key_len, space = 1;
+
+         /* If the character was not a space then it is invalid. */
+         if (ch != 32)
+            bad_character = ch;
+      }
+
+      else if (bad_character == 0)
+         bad_character = ch; /* just skip it, record the first error */
+   }
+
+   if (key_len > 0 && space != 0) /* trailing space */
+   {
+      --key_len, --new_key;
+      if (bad_character == 0)
+         bad_character = 32;
+   }
+
+   /* Terminate the keyword */
+   *new_key = 0;
+
+   if (key_len == 0)
+      return 0;
+
+#ifdef PNG_WARNINGS_SUPPORTED
+   /* Try to only output one warning per keyword: */
+   if (*key != 0) /* keyword too long */
+      png_warning(png_ptr, "keyword truncated");
+
+   else if (bad_character != 0)
+   {
+      PNG_WARNING_PARAMETERS(p)
+
+      png_warning_parameter(p, 1, orig_key);
+      png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character);
+
+      png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
+   }
+#endif /* WARNINGS */
+
+   return key_len;
+}
+#endif /* WRITE_TEXT || WRITE_pCAL || WRITE_iCCP || WRITE_sPLT */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.  Note that the rest of this code depends upon this
+ * information being correct.
+ */
+void /* PRIVATE */
+png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
+    int bit_depth, int color_type, int compression_type, int filter_type,
+    int interlace_type)
+{
+   png_byte buf[13]; /* Buffer to store the IHDR info */
+
+   png_debug(1, "in png_write_IHDR");
+
+   /* Check that we have valid input data from the application info */
+   switch (color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8:
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+            case 16:
+#endif
+               png_ptr->channels = 1; break;
+
+            default:
+               png_error(png_ptr,
+                   "Invalid bit depth for grayscale image");
+         }
+         break;
+
+      case PNG_COLOR_TYPE_RGB:
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         if (bit_depth != 8 && bit_depth != 16)
+#else
+         if (bit_depth != 8)
+#endif
+            png_error(png_ptr, "Invalid bit depth for RGB image");
+
+         png_ptr->channels = 3;
+         break;
+
+      case PNG_COLOR_TYPE_PALETTE:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8:
+               png_ptr->channels = 1;
+               break;
+
+            default:
+               png_error(png_ptr, "Invalid bit depth for paletted image");
+         }
+         break;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
+
+         png_ptr->channels = 2;
+         break;
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+         if (bit_depth != 8 && bit_depth != 16)
+#else
+         if (bit_depth != 8)
+#endif
+            png_error(png_ptr, "Invalid bit depth for RGBA image");
+
+         png_ptr->channels = 4;
+         break;
+
+      default:
+         png_error(png_ptr, "Invalid image color type specified");
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid compression type specified");
+      compression_type = PNG_COMPRESSION_TYPE_BASE;
+   }
+
+   /* Write filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not write a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if (
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+       !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+       ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
+       (color_type == PNG_COLOR_TYPE_RGB ||
+        color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+       (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
+#endif
+       filter_type != PNG_FILTER_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid filter type specified");
+      filter_type = PNG_FILTER_TYPE_BASE;
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   if (interlace_type != PNG_INTERLACE_NONE &&
+       interlace_type != PNG_INTERLACE_ADAM7)
+   {
+      png_warning(png_ptr, "Invalid interlace type specified");
+      interlace_type = PNG_INTERLACE_ADAM7;
+   }
+#else
+   interlace_type=PNG_INTERLACE_NONE;
+#endif
+
+   /* Save the relevant information */
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->color_type = (png_byte)color_type;
+   png_ptr->interlaced = (png_byte)interlace_type;
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+   png_ptr->filter_type = (png_byte)filter_type;
+#endif
+   png_ptr->compression_type = (png_byte)compression_type;
+   png_ptr->width = width;
+   png_ptr->height = height;
+
+   png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
+   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
+   /* Set the usr info, so any transformations can modify it */
+   png_ptr->usr_width = png_ptr->width;
+   png_ptr->usr_bit_depth = png_ptr->bit_depth;
+   png_ptr->usr_channels = png_ptr->channels;
+
+   /* Pack the header information into the buffer */
+   png_save_uint_32(buf, width);
+   png_save_uint_32(buf + 4, height);
+   buf[8] = (png_byte)bit_depth;
+   buf[9] = (png_byte)color_type;
+   buf[10] = (png_byte)compression_type;
+   buf[11] = (png_byte)filter_type;
+   buf[12] = (png_byte)interlace_type;
+
+   /* Write the chunk */
+   png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
+
+   if ((png_ptr->do_filter) == PNG_NO_FILTERS)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+          png_ptr->bit_depth < 8)
+         png_ptr->do_filter = PNG_FILTER_NONE;
+
+      else
+         png_ptr->do_filter = PNG_ALL_FILTERS;
+   }
+
+   png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */
+}
+
+/* Write the palette.  We are careful not to trust png_color to be in the
+ * correct order for PNG, so people can redefine it to any convenient
+ * structure.
+ */
+void /* PRIVATE */
+png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
+    png_uint_32 num_pal)
+{
+   png_uint_32 max_palette_length, i;
+   png_const_colorp pal_ptr;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_PLTE");
+
+   max_palette_length = (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
+      (1 << png_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
+
+   if ((
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+       (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 &&
+#endif
+       num_pal == 0) || num_pal > max_palette_length)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_error(png_ptr, "Invalid number of colors in palette");
+      }
+
+      else
+      {
+         png_warning(png_ptr, "Invalid number of colors in palette");
+         return;
+      }
+   }
+
+   if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0)
+   {
+      png_warning(png_ptr,
+          "Ignoring request to write a PLTE chunk in grayscale PNG");
+
+      return;
+   }
+
+   png_ptr->num_palette = (png_uint_16)num_pal;
+   png_debug1(3, "num_palette = %d", png_ptr->num_palette);
+
+   png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3));
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+
+   for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
+   {
+      buf[0] = pal_ptr->red;
+      buf[1] = pal_ptr->green;
+      buf[2] = pal_ptr->blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+
+#else
+   /* This is a little slower but some buggy compilers need to do this
+    * instead
+    */
+   pal_ptr=palette;
+
+   for (i = 0; i < num_pal; i++)
+   {
+      buf[0] = pal_ptr[i].red;
+      buf[1] = pal_ptr[i].green;
+      buf[2] = pal_ptr[i].blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+
+#endif
+   png_write_chunk_end(png_ptr);
+   png_ptr->mode |= PNG_HAVE_PLTE;
+}
+
+/* This is similar to png_text_compress, above, except that it does not require
+ * all of the data at once and, instead of buffering the compressed result,
+ * writes it as IDAT chunks.  Unlike png_text_compress it *can* png_error out
+ * because it calls the write interface.  As a result it does its own error
+ * reporting and does not return an error code.  In the event of error it will
+ * just call png_error.  The input data length may exceed 32-bits.  The 'flush'
+ * parameter is exactly the same as that to deflate, with the following
+ * meanings:
+ *
+ * Z_NO_FLUSH: normal incremental output of compressed data
+ * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
+ * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
+ *
+ * The routine manages the acquire and release of the png_ptr->zstream by
+ * checking and (at the end) clearing png_ptr->zowner; it does some sanity
+ * checks on the 'mode' flags while doing this.
+ */
+void /* PRIVATE */
+png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
+   png_alloc_size_t input_len, int flush)
+{
+   if (png_ptr->zowner != png_IDAT)
+   {
+      /* First time.   Ensure we have a temporary buffer for compression and
+       * trim the buffer list if it has more than one entry to free memory.
+       * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been
+       * created at this point, but the check here is quick and safe.
+       */
+      if (png_ptr->zbuffer_list == NULL)
+      {
+         png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
+            png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
+         png_ptr->zbuffer_list->next = NULL;
+      }
+
+      else
+         png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
+
+      /* It is a terminal error if we can't claim the zstream. */
+      if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg);
+
+      /* The output state is maintained in png_ptr->zstream, so it must be
+       * initialized here after the claim.
+       */
+      png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
+      png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
+   }
+
+   /* Now loop reading and writing until all the input is consumed or an error
+    * terminates the operation.  The _out values are maintained across calls to
+    * this function, but the input must be reset each time.
+    */
+   png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
+   png_ptr->zstream.avail_in = 0; /* set below */
+   for (;;)
+   {
+      int ret;
+
+      /* INPUT: from the row data */
+      uInt avail = ZLIB_IO_MAX;
+
+      if (avail > input_len)
+         avail = (uInt)input_len; /* safe because of the check */
+
+      png_ptr->zstream.avail_in = avail;
+      input_len -= avail;
+
+      ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
+
+      /* Include as-yet unconsumed input */
+      input_len += png_ptr->zstream.avail_in;
+      png_ptr->zstream.avail_in = 0;
+
+      /* OUTPUT: write complete IDAT chunks when avail_out drops to zero. Note
+       * that these two zstream fields are preserved across the calls, therefore
+       * there is no need to set these up on entry to the loop.
+       */
+      if (png_ptr->zstream.avail_out == 0)
+      {
+         png_bytep data = png_ptr->zbuffer_list->output;
+         uInt size = png_ptr->zbuffer_size;
+
+         /* Write an IDAT containing the data then reset the buffer.  The
+          * first IDAT may need deflate header optimization.
+          */
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+            if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
+                png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+               optimize_cmf(data, png_image_size(png_ptr));
+#endif
+
+         png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+         png_ptr->mode |= PNG_HAVE_IDAT;
+
+         png_ptr->zstream.next_out = data;
+         png_ptr->zstream.avail_out = size;
+
+         /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
+          * the same flush parameter until it has finished output, for NO_FLUSH
+          * it doesn't matter.
+          */
+         if (ret == Z_OK && flush != Z_NO_FLUSH)
+            continue;
+      }
+
+      /* The order of these checks doesn't matter much; it just affects which
+       * possible error might be detected if multiple things go wrong at once.
+       */
+      if (ret == Z_OK) /* most likely return code! */
+      {
+         /* If all the input has been consumed then just return.  If Z_FINISH
+          * was used as the flush parameter something has gone wrong if we get
+          * here.
+          */
+         if (input_len == 0)
+         {
+            if (flush == Z_FINISH)
+               png_error(png_ptr, "Z_OK on Z_FINISH with output space");
+
+            return;
+         }
+      }
+
+      else if (ret == Z_STREAM_END && flush == Z_FINISH)
+      {
+         /* This is the end of the IDAT data; any pending output must be
+          * flushed.  For small PNG files we may still be at the beginning.
+          */
+         png_bytep data = png_ptr->zbuffer_list->output;
+         uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
+
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+         if ((png_ptr->mode & PNG_HAVE_IDAT) == 0 &&
+             png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+            optimize_cmf(data, png_image_size(png_ptr));
+#endif
+
+         png_write_complete_chunk(png_ptr, png_IDAT, data, size);
+         png_ptr->zstream.avail_out = 0;
+         png_ptr->zstream.next_out = NULL;
+         png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
+
+         png_ptr->zowner = 0; /* Release the stream */
+         return;
+      }
+
+      else
+      {
+         /* This is an error condition. */
+         png_zstream_error(png_ptr, ret);
+         png_error(png_ptr, png_ptr->zstream.msg);
+      }
+   }
+}
+
+/* Write an IEND chunk */
+void /* PRIVATE */
+png_write_IEND(png_structrp png_ptr)
+{
+   png_debug(1, "in png_write_IEND");
+
+   png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0);
+   png_ptr->mode |= PNG_HAVE_IEND;
+}
+
+#ifdef PNG_WRITE_gAMA_SUPPORTED
+/* Write a gAMA chunk */
+void /* PRIVATE */
+png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma)
+{
+   png_byte buf[4];
+
+   png_debug(1, "in png_write_gAMA");
+
+   /* file_gamma is saved in 1/100,000ths */
+   png_save_uint_32(buf, (png_uint_32)file_gamma);
+   png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
+}
+#endif
+
+#ifdef PNG_WRITE_sRGB_SUPPORTED
+/* Write a sRGB chunk */
+void /* PRIVATE */
+png_write_sRGB(png_structrp png_ptr, int srgb_intent)
+{
+   png_byte buf[1];
+
+   png_debug(1, "in png_write_sRGB");
+
+   if (srgb_intent >= PNG_sRGB_INTENT_LAST)
+      png_warning(png_ptr,
+          "Invalid sRGB rendering intent specified");
+
+   buf[0]=(png_byte)srgb_intent;
+   png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);
+}
+#endif
+
+#ifdef PNG_WRITE_iCCP_SUPPORTED
+/* Write an iCCP chunk */
+void /* PRIVATE */
+png_write_iCCP(png_structrp png_ptr, png_const_charp name,
+    png_const_bytep profile)
+{
+   png_uint_32 name_len;
+   png_uint_32 profile_len;
+   png_byte new_name[81]; /* 1 byte for the compression byte */
+   compression_state comp;
+   png_uint_32 temp;
+
+   png_debug(1, "in png_write_iCCP");
+
+   /* These are all internal problems: the profile should have been checked
+    * before when it was stored.
+    */
+   if (profile == NULL)
+      png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */
+
+   profile_len = png_get_uint_32(profile);
+
+   if (profile_len < 132)
+      png_error(png_ptr, "ICC profile too short");
+
+   temp = (png_uint_32) (*(profile+8));
+   if (temp > 3 && (profile_len & 0x03))
+      png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
+
+   {
+      png_uint_32 embedded_profile_len = png_get_uint_32(profile);
+
+      if (profile_len != embedded_profile_len)
+         png_error(png_ptr, "Profile length does not match profile");
+   }
+
+   name_len = png_check_keyword(png_ptr, name, new_name);
+
+   if (name_len == 0)
+      png_error(png_ptr, "iCCP: invalid keyword");
+
+   new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;
+
+   /* Make sure we include the NULL after the name and the compression type */
+   ++name_len;
+
+   png_text_compress_init(&comp, profile, profile_len);
+
+   /* Allow for keyword terminator and compression byte */
+   if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
+      png_error(png_ptr, png_ptr->zstream.msg);
+
+   png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
+
+   png_write_chunk_data(png_ptr, new_name, name_len);
+
+   png_write_compressed_data_out(png_ptr, &comp);
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_sPLT_SUPPORTED
+/* Write a sPLT chunk */
+void /* PRIVATE */
+png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
+{
+   png_uint_32 name_len;
+   png_byte new_name[80];
+   png_byte entrybuf[10];
+   png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
+   png_size_t palette_size = entry_size * spalette->nentries;
+   png_sPLT_entryp ep;
+#ifndef PNG_POINTER_INDEXING_SUPPORTED
+   int i;
+#endif
+
+   png_debug(1, "in png_write_sPLT");
+
+   name_len = png_check_keyword(png_ptr, spalette->name, new_name);
+
+   if (name_len == 0)
+      png_error(png_ptr, "sPLT: invalid keyword");
+
+   /* Make sure we include the NULL after the name */
+   png_write_chunk_header(png_ptr, png_sPLT,
+       (png_uint_32)(name_len + 2 + palette_size));
+
+   png_write_chunk_data(png_ptr, (png_bytep)new_name,
+       (png_size_t)(name_len + 1));
+
+   png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);
+
+   /* Loop through each palette entry, writing appropriately */
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+   for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)
+   {
+      if (spalette->depth == 8)
+      {
+         entrybuf[0] = (png_byte)ep->red;
+         entrybuf[1] = (png_byte)ep->green;
+         entrybuf[2] = (png_byte)ep->blue;
+         entrybuf[3] = (png_byte)ep->alpha;
+         png_save_uint_16(entrybuf + 4, ep->frequency);
+      }
+
+      else
+      {
+         png_save_uint_16(entrybuf + 0, ep->red);
+         png_save_uint_16(entrybuf + 2, ep->green);
+         png_save_uint_16(entrybuf + 4, ep->blue);
+         png_save_uint_16(entrybuf + 6, ep->alpha);
+         png_save_uint_16(entrybuf + 8, ep->frequency);
+      }
+
+      png_write_chunk_data(png_ptr, entrybuf, entry_size);
+   }
+#else
+   ep=spalette->entries;
+   for (i = 0; i>spalette->nentries; i++)
+   {
+      if (spalette->depth == 8)
+      {
+         entrybuf[0] = (png_byte)ep[i].red;
+         entrybuf[1] = (png_byte)ep[i].green;
+         entrybuf[2] = (png_byte)ep[i].blue;
+         entrybuf[3] = (png_byte)ep[i].alpha;
+         png_save_uint_16(entrybuf + 4, ep[i].frequency);
+      }
+
+      else
+      {
+         png_save_uint_16(entrybuf + 0, ep[i].red);
+         png_save_uint_16(entrybuf + 2, ep[i].green);
+         png_save_uint_16(entrybuf + 4, ep[i].blue);
+         png_save_uint_16(entrybuf + 6, ep[i].alpha);
+         png_save_uint_16(entrybuf + 8, ep[i].frequency);
+      }
+
+      png_write_chunk_data(png_ptr, entrybuf, entry_size);
+   }
+#endif
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_sBIT_SUPPORTED
+/* Write the sBIT chunk */
+void /* PRIVATE */
+png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
+{
+   png_byte buf[4];
+   png_size_t size;
+
+   png_debug(1, "in png_write_sBIT");
+
+   /* Make sure we don't depend upon the order of PNG_COLOR_8 */
+   if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      png_byte maxbits;
+
+      maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
+          png_ptr->usr_bit_depth);
+
+      if (sbit->red == 0 || sbit->red > maxbits ||
+          sbit->green == 0 || sbit->green > maxbits ||
+          sbit->blue == 0 || sbit->blue > maxbits)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+
+      buf[0] = sbit->red;
+      buf[1] = sbit->green;
+      buf[2] = sbit->blue;
+      size = 3;
+   }
+
+   else
+   {
+      if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+
+      buf[0] = sbit->gray;
+      size = 1;
+   }
+
+   if ((color_type & PNG_COLOR_MASK_ALPHA) != 0)
+   {
+      if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+
+      buf[size++] = sbit->alpha;
+   }
+
+   png_write_complete_chunk(png_ptr, png_sBIT, buf, size);
+}
+#endif
+
+#ifdef PNG_WRITE_cHRM_SUPPORTED
+/* Write the cHRM chunk */
+void /* PRIVATE */
+png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy)
+{
+   png_byte buf[32];
+
+   png_debug(1, "in png_write_cHRM");
+
+   /* Each value is saved in 1/100,000ths */
+   png_save_int_32(buf,      xy->whitex);
+   png_save_int_32(buf +  4, xy->whitey);
+
+   png_save_int_32(buf +  8, xy->redx);
+   png_save_int_32(buf + 12, xy->redy);
+
+   png_save_int_32(buf + 16, xy->greenx);
+   png_save_int_32(buf + 20, xy->greeny);
+
+   png_save_int_32(buf + 24, xy->bluex);
+   png_save_int_32(buf + 28, xy->bluey);
+
+   png_write_complete_chunk(png_ptr, png_cHRM, buf, 32);
+}
+#endif
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+/* Write the tRNS chunk */
+void /* PRIVATE */
+png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha,
+    png_const_color_16p tran, int num_trans, int color_type)
+{
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_tRNS");
+
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
+      {
+         png_app_warning(png_ptr,
+             "Invalid number of transparent colors specified");
+         return;
+      }
+
+      /* Write the chunk out as it is */
+      png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha,
+         (png_size_t)num_trans);
+   }
+
+   else if (color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      /* One 16-bit value */
+      if (tran->gray >= (1 << png_ptr->bit_depth))
+      {
+         png_app_warning(png_ptr,
+             "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
+
+         return;
+      }
+
+      png_save_uint_16(buf, tran->gray);
+      png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);
+   }
+
+   else if (color_type == PNG_COLOR_TYPE_RGB)
+   {
+      /* Three 16-bit values */
+      png_save_uint_16(buf, tran->red);
+      png_save_uint_16(buf + 2, tran->green);
+      png_save_uint_16(buf + 4, tran->blue);
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+      if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0)
+#else
+      if ((buf[0] | buf[2] | buf[4]) != 0)
+#endif
+      {
+         png_app_warning(png_ptr,
+           "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
+         return;
+      }
+
+      png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);
+   }
+
+   else
+   {
+      png_app_warning(png_ptr, "Can't write tRNS with an alpha channel");
+   }
+}
+#endif
+
+#ifdef PNG_WRITE_bKGD_SUPPORTED
+/* Write the background chunk */
+void /* PRIVATE */
+png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)
+{
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_bKGD");
+
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+          (png_ptr->num_palette != 0 ||
+          (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) &&
+#endif
+         back->index >= png_ptr->num_palette)
+      {
+         png_warning(png_ptr, "Invalid background palette index");
+         return;
+      }
+
+      buf[0] = back->index;
+      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
+   }
+
+   else if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      png_save_uint_16(buf, back->red);
+      png_save_uint_16(buf + 2, back->green);
+      png_save_uint_16(buf + 4, back->blue);
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+      if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0)
+#else
+      if ((buf[0] | buf[2] | buf[4]) != 0)
+#endif
+      {
+         png_warning(png_ptr,
+             "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
+
+         return;
+      }
+
+      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);
+   }
+
+   else
+   {
+      if (back->gray >= (1 << png_ptr->bit_depth))
+      {
+         png_warning(png_ptr,
+             "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
+
+         return;
+      }
+
+      png_save_uint_16(buf, back->gray);
+      png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);
+   }
+}
+#endif
+
+#ifdef PNG_WRITE_hIST_SUPPORTED
+/* Write the histogram */
+void /* PRIVATE */
+png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist)
+{
+   int i;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_hIST");
+
+   if (num_hist > (int)png_ptr->num_palette)
+   {
+      png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,
+          png_ptr->num_palette);
+
+      png_warning(png_ptr, "Invalid number of histogram entries specified");
+      return;
+   }
+
+   png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));
+
+   for (i = 0; i < num_hist; i++)
+   {
+      png_save_uint_16(buf, hist[i]);
+      png_write_chunk_data(png_ptr, buf, (png_size_t)2);
+   }
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+/* Write a tEXt chunk */
+void /* PRIVATE */
+png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
+    png_size_t text_len)
+{
+   png_uint_32 key_len;
+   png_byte new_key[80];
+
+   png_debug(1, "in png_write_tEXt");
+
+   key_len = png_check_keyword(png_ptr, key, new_key);
+
+   if (key_len == 0)
+      png_error(png_ptr, "tEXt: invalid keyword");
+
+   if (text == NULL || *text == '\0')
+      text_len = 0;
+
+   else
+      text_len = strlen(text);
+
+   if (text_len > PNG_UINT_31_MAX - (key_len+1))
+      png_error(png_ptr, "tEXt: text too long");
+
+   /* Make sure we include the 0 after the key */
+   png_write_chunk_header(png_ptr, png_tEXt,
+       (png_uint_32)/*checked above*/(key_len + text_len + 1));
+   /*
+    * We leave it to the application to meet PNG-1.0 requirements on the
+    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
+    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    */
+   png_write_chunk_data(png_ptr, new_key, key_len + 1);
+
+   if (text_len != 0)
+      png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len);
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+/* Write a compressed text chunk */
+void /* PRIVATE */
+png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
+    int compression)
+{
+   png_uint_32 key_len;
+   png_byte new_key[81];
+   compression_state comp;
+
+   png_debug(1, "in png_write_zTXt");
+
+   if (compression == PNG_TEXT_COMPRESSION_NONE)
+   {
+      png_write_tEXt(png_ptr, key, text, 0);
+      return;
+   }
+
+   if (compression != PNG_TEXT_COMPRESSION_zTXt)
+      png_error(png_ptr, "zTXt: invalid compression type");
+
+   key_len = png_check_keyword(png_ptr, key, new_key);
+
+   if (key_len == 0)
+      png_error(png_ptr, "zTXt: invalid keyword");
+
+   /* Add the compression method and 1 for the keyword separator. */
+   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
+   ++key_len;
+
+   /* Compute the compressed data; do it now for the length */
+   png_text_compress_init(&comp, (png_const_bytep)text,
+      text == NULL ? 0 : strlen(text));
+
+   if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
+      png_error(png_ptr, png_ptr->zstream.msg);
+
+   /* Write start of chunk */
+   png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
+
+   /* Write key */
+   png_write_chunk_data(png_ptr, new_key, key_len);
+
+   /* Write the compressed data */
+   png_write_compressed_data_out(png_ptr, &comp);
+
+   /* Close the chunk */
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+/* Write an iTXt chunk */
+void /* PRIVATE */
+png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,
+    png_const_charp lang, png_const_charp lang_key, png_const_charp text)
+{
+   png_uint_32 key_len, prefix_len;
+   png_size_t lang_len, lang_key_len;
+   png_byte new_key[82];
+   compression_state comp;
+
+   png_debug(1, "in png_write_iTXt");
+
+   key_len = png_check_keyword(png_ptr, key, new_key);
+
+   if (key_len == 0)
+      png_error(png_ptr, "iTXt: invalid keyword");
+
+   /* Set the compression flag */
+   switch (compression)
+   {
+      case PNG_ITXT_COMPRESSION_NONE:
+      case PNG_TEXT_COMPRESSION_NONE:
+         compression = new_key[++key_len] = 0; /* no compression */
+         break;
+
+      case PNG_TEXT_COMPRESSION_zTXt:
+      case PNG_ITXT_COMPRESSION_zTXt:
+         compression = new_key[++key_len] = 1; /* compressed */
+         break;
+
+      default:
+         png_error(png_ptr, "iTXt: invalid compression");
+   }
+
+   new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
+   ++key_len; /* for the keywod separator */
+
+   /* We leave it to the application to meet PNG-1.0 requirements on the
+    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG, however,
+    * specifies that the text is UTF-8 and this really doesn't require any
+    * checking.
+    *
+    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    *
+    * TODO: validate the language tag correctly (see the spec.)
+    */
+   if (lang == NULL) lang = ""; /* empty language is valid */
+   lang_len = strlen(lang)+1;
+   if (lang_key == NULL) lang_key = ""; /* may be empty */
+   lang_key_len = strlen(lang_key)+1;
+   if (text == NULL) text = ""; /* may be empty */
+
+   prefix_len = key_len;
+   if (lang_len > PNG_UINT_31_MAX-prefix_len)
+      prefix_len = PNG_UINT_31_MAX;
+   else
+      prefix_len = (png_uint_32)(prefix_len + lang_len);
+
+   if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
+      prefix_len = PNG_UINT_31_MAX;
+   else
+      prefix_len = (png_uint_32)(prefix_len + lang_key_len);
+
+   png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));
+
+   if (compression != 0)
+   {
+      if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg);
+   }
+
+   else
+   {
+      if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
+         png_error(png_ptr, "iTXt: uncompressed text too long");
+
+      /* So the string will fit in a chunk: */
+      comp.output_len = (png_uint_32)/*SAFE*/comp.input_len;
+   }
+
+   png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
+
+   png_write_chunk_data(png_ptr, new_key, key_len);
+
+   png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);
+
+   png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);
+
+   if (compression != 0)
+      png_write_compressed_data_out(png_ptr, &comp);
+
+   else
+      png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.output_len);
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_oFFs_SUPPORTED
+/* Write the oFFs chunk */
+void /* PRIVATE */
+png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
+    int unit_type)
+{
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_oFFs");
+
+   if (unit_type >= PNG_OFFSET_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
+
+   png_save_int_32(buf, x_offset);
+   png_save_int_32(buf + 4, y_offset);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+}
+#endif
+#ifdef PNG_WRITE_pCAL_SUPPORTED
+/* Write the pCAL chunk (described in the PNG extensions document) */
+void /* PRIVATE */
+png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0,
+    png_int_32 X1, int type, int nparams, png_const_charp units,
+    png_charpp params)
+{
+   png_uint_32 purpose_len;
+   png_size_t units_len, total_len;
+   png_size_tp params_len;
+   png_byte buf[10];
+   png_byte new_purpose[80];
+   int i;
+
+   png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
+
+   if (type >= PNG_EQUATION_LAST)
+      png_error(png_ptr, "Unrecognized equation type for pCAL chunk");
+
+   purpose_len = png_check_keyword(png_ptr, purpose, new_purpose);
+
+   if (purpose_len == 0)
+      png_error(png_ptr, "pCAL: invalid keyword");
+
+   ++purpose_len; /* terminator */
+
+   png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
+   units_len = strlen(units) + (nparams == 0 ? 0 : 1);
+   png_debug1(3, "pCAL units length = %d", (int)units_len);
+   total_len = purpose_len + units_len + 10;
+
+   params_len = (png_size_tp)png_malloc(png_ptr,
+       (png_alloc_size_t)(nparams * (sizeof (png_size_t))));
+
+   /* Find the length of each parameter, making sure we don't count the
+    * null terminator for the last parameter.
+    */
+   for (i = 0; i < nparams; i++)
+   {
+      params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
+      png_debug2(3, "pCAL parameter %d length = %lu", i,
+          (unsigned long)params_len[i]);
+      total_len += params_len[i];
+   }
+
+   png_debug1(3, "pCAL total length = %d", (int)total_len);
+   png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
+   png_write_chunk_data(png_ptr, new_purpose, purpose_len);
+   png_save_int_32(buf, X0);
+   png_save_int_32(buf + 4, X1);
+   buf[8] = (png_byte)type;
+   buf[9] = (png_byte)nparams;
+   png_write_chunk_data(png_ptr, buf, (png_size_t)10);
+   png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
+
+   for (i = 0; i < nparams; i++)
+   {
+      png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);
+   }
+
+   png_free(png_ptr, params_len);
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_sCAL_SUPPORTED
+/* Write the sCAL chunk */
+void /* PRIVATE */
+png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width,
+    png_const_charp height)
+{
+   png_byte buf[64];
+   png_size_t wlen, hlen, total_len;
+
+   png_debug(1, "in png_write_sCAL_s");
+
+   wlen = strlen(width);
+   hlen = strlen(height);
+   total_len = wlen + hlen + 2;
+
+   if (total_len > 64)
+   {
+      png_warning(png_ptr, "Can't write sCAL (buffer too small)");
+      return;
+   }
+
+   buf[0] = (png_byte)unit;
+   memcpy(buf + 1, width, wlen + 1);      /* Append the '\0' here */
+   memcpy(buf + wlen + 2, height, hlen);  /* Do NOT append the '\0' here */
+
+   png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);
+   png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len);
+}
+#endif
+
+#ifdef PNG_WRITE_pHYs_SUPPORTED
+/* Write the pHYs chunk */
+void /* PRIVATE */
+png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit,
+    png_uint_32 y_pixels_per_unit,
+    int unit_type)
+{
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_pHYs");
+
+   if (unit_type >= PNG_RESOLUTION_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
+
+   png_save_uint_32(buf, x_pixels_per_unit);
+   png_save_uint_32(buf + 4, y_pixels_per_unit);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
+}
+#endif
+
+#ifdef PNG_WRITE_tIME_SUPPORTED
+/* Write the tIME chunk.  Use either png_convert_from_struct_tm()
+ * or png_convert_from_time_t(), or fill in the structure yourself.
+ */
+void /* PRIVATE */
+png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
+{
+   png_byte buf[7];
+
+   png_debug(1, "in png_write_tIME");
+
+   if (mod_time->month  > 12 || mod_time->month  < 1 ||
+       mod_time->day    > 31 || mod_time->day    < 1 ||
+       mod_time->hour   > 23 || mod_time->second > 60)
+   {
+      png_warning(png_ptr, "Invalid time specified for tIME chunk");
+      return;
+   }
+
+   png_save_uint_16(buf, mod_time->year);
+   buf[2] = mod_time->month;
+   buf[3] = mod_time->day;
+   buf[4] = mod_time->hour;
+   buf[5] = mod_time->minute;
+   buf[6] = mod_time->second;
+
+   png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
+}
+#endif
+
+/* Initializes the row writing capability of libpng */
+void /* PRIVATE */
+png_write_start_row(png_structrp png_ptr)
+{
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* Start of interlace block */
+   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* Offset to next interlace block */
+   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* Start of interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* Offset to next interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   png_alloc_size_t buf_size;
+   int usr_pixel_depth;
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+   png_byte filters;
+#endif
+
+   png_debug(1, "in png_write_start_row");
+
+   usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth;
+   buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1;
+
+   /* 1.5.6: added to allow checking in the row write code. */
+   png_ptr->transformed_pixel_depth = png_ptr->pixel_depth;
+   png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth;
+
+   /* Set up row buffer */
+   png_ptr->row_buf = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
+
+   png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+   filters = png_ptr->do_filter;
+
+   if (png_ptr->height == 1)
+      filters &= 0xff & ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
+
+   if (png_ptr->width == 1)
+      filters &= 0xff & ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
+
+   if (filters == 0)
+      filters = PNG_FILTER_NONE;
+
+   png_ptr->do_filter = filters;
+
+   if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG |
+       PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL)
+   {
+      int num_filters = 0;
+
+      png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
+
+      if (filters & PNG_FILTER_SUB)
+         num_filters++;
+
+      if (filters & PNG_FILTER_UP)
+         num_filters++;
+
+      if (filters & PNG_FILTER_AVG)
+         num_filters++;
+
+      if (filters & PNG_FILTER_PAETH)
+         num_filters++;
+
+      if (num_filters > 1)
+         png_ptr->tst_row = png_voidcast(png_bytep, png_malloc(png_ptr,
+             buf_size));
+   }
+
+   /* We only need to keep the previous row if we are using one of the following
+    * filters.
+    */
+   if ((filters & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) != 0)
+      png_ptr->prev_row = png_voidcast(png_bytep,
+         png_calloc(png_ptr, buf_size));
+#endif /* WRITE_FILTER */
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* If interlaced, we need to set up width and height of pass */
+   if (png_ptr->interlaced != 0)
+   {
+      if ((png_ptr->transformations & PNG_INTERLACE) == 0)
+      {
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+             png_pass_ystart[0]) / png_pass_yinc[0];
+
+         png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
+             png_pass_start[0]) / png_pass_inc[0];
+      }
+
+      else
+      {
+         png_ptr->num_rows = png_ptr->height;
+         png_ptr->usr_width = png_ptr->width;
+      }
+   }
+
+   else
+#endif
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->usr_width = png_ptr->width;
+   }
+}
+
+/* Internal use only.  Called when finished processing a row of data. */
+void /* PRIVATE */
+png_write_finish_row(png_structrp png_ptr)
+{
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* Start of interlace block */
+   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* Offset to next interlace block */
+   static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* Start of interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* Offset to next interlace block in the y direction */
+   static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   png_debug(1, "in png_write_finish_row");
+
+   /* Next row */
+   png_ptr->row_number++;
+
+   /* See if we are done */
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* If interlaced, go to next pass */
+   if (png_ptr->interlaced != 0)
+   {
+      png_ptr->row_number = 0;
+      if ((png_ptr->transformations & PNG_INTERLACE) != 0)
+      {
+         png_ptr->pass++;
+      }
+
+      else
+      {
+         /* Loop until we find a non-zero width or height pass */
+         do
+         {
+            png_ptr->pass++;
+
+            if (png_ptr->pass >= 7)
+               break;
+
+            png_ptr->usr_width = (png_ptr->width +
+                png_pass_inc[png_ptr->pass] - 1 -
+                png_pass_start[png_ptr->pass]) /
+                png_pass_inc[png_ptr->pass];
+
+            png_ptr->num_rows = (png_ptr->height +
+                png_pass_yinc[png_ptr->pass] - 1 -
+                png_pass_ystart[png_ptr->pass]) /
+                png_pass_yinc[png_ptr->pass];
+
+            if ((png_ptr->transformations & PNG_INTERLACE) != 0)
+               break;
+
+         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
+
+      }
+
+      /* Reset the row above the image for the next pass */
+      if (png_ptr->pass < 7)
+      {
+         if (png_ptr->prev_row != NULL)
+            memset(png_ptr->prev_row, 0,
+                (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
+                png_ptr->usr_bit_depth, png_ptr->width)) + 1);
+
+         return;
+      }
+   }
+#endif
+
+   /* If we get here, we've just written the last row, so we need
+      to flush the compressor */
+   png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);
+}
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+/* Pick out the correct pixels for the interlace pass.
+ * The basic idea here is to go through the row with a source
+ * pointer and a destination pointer (sp and dp), and copy the
+ * correct pixels for the pass.  As the row gets compacted,
+ * sp will always be >= dp, so we should never overwrite anything.
+ * See the default: case for the easiest code to understand.
+ */
+void /* PRIVATE */
+png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
+{
+   /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* Start of interlace block */
+   static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* Offset to next interlace block */
+   static PNG_CONST png_byte  png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   png_debug(1, "in png_do_write_interlace");
+
+   /* We don't have to do anything on the last pass (6) */
+   if (pass < 6)
+   {
+      /* Each pixel depth is handled separately */
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            unsigned int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            d = 0;
+            shift = 7;
+
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 3);
+               value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 7;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+
+               else
+                  shift--;
+
+            }
+            if (shift != 7)
+               *dp = (png_byte)d;
+
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            unsigned int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 6;
+            d = 0;
+
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 2);
+               value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+
+               else
+                  shift -= 2;
+            }
+            if (shift != 6)
+               *dp = (png_byte)d;
+
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            unsigned int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 4;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+                i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 1);
+               value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+
+               else
+                  shift -= 4;
+            }
+            if (shift != 4)
+               *dp = (png_byte)d;
+
+            break;
+         }
+
+         default:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            png_size_t pixel_bytes;
+
+            /* Start at the beginning */
+            dp = row;
+
+            /* Find out how many bytes each pixel takes up */
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            /* Loop through the row, only looking at the pixels that matter */
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               /* Find out where the original pixel is */
+               sp = row + (png_size_t)i * pixel_bytes;
+
+               /* Move the pixel */
+               if (dp != sp)
+                  memcpy(dp, sp, pixel_bytes);
+
+               /* Next pixel */
+               dp += pixel_bytes;
+            }
+            break;
+         }
+      }
+      /* Set new row width */
+      row_info->width = (row_info->width +
+          png_pass_inc[pass] - 1 -
+          png_pass_start[pass]) /
+          png_pass_inc[pass];
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+          row_info->width);
+   }
+}
+#endif
+
+
+/* This filters the row, chooses which filter to use, if it has not already
+ * been specified by the application, and then writes the row out with the
+ * chosen filter.
+ */
+static void /* PRIVATE */
+png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
+   png_size_t row_bytes);
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+static png_size_t /* PRIVATE */
+png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp,
+    const png_size_t row_bytes, const png_size_t lmins)
+{
+   png_bytep rp, dp, lp;
+   png_size_t i;
+   png_size_t sum = 0;
+   int v;
+
+   png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
+
+   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp;
+        i++, rp++, dp++)
+   {
+      v = *dp = *rp;
+      sum += (v < 128) ? v : 256 - v;
+   }
+
+   for (lp = png_ptr->row_buf + 1; i < row_bytes;
+      i++, rp++, lp++, dp++)
+   {
+      v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+      sum += (v < 128) ? v : 256 - v;
+
+      if (sum > lmins)  /* We are already worse, don't continue. */
+        break;
+   }
+
+   return (sum);
+}
+
+static png_size_t /* PRIVATE */
+png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes,
+    const png_size_t lmins)
+{
+   png_bytep rp, dp, pp;
+   png_size_t i;
+   png_size_t sum = 0;
+   int v;
+
+   png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
+
+   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
+       pp = png_ptr->prev_row + 1; i < row_bytes;
+       i++, rp++, pp++, dp++)
+   {
+      v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+      sum += (v < 128) ? v : 256 - v;
+
+      if (sum > lmins)  /* We are already worse, don't continue. */
+        break;
+   }
+
+   return (sum);
+}
+
+static png_size_t /* PRIVATE */
+png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp,
+      const png_size_t row_bytes, const png_size_t lmins)
+{
+   png_bytep rp, dp, pp, lp;
+   png_uint_32 i;
+   png_size_t sum = 0;
+   int v;
+
+   png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
+
+   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
+        pp = png_ptr->prev_row + 1; i < bpp; i++)
+   {
+      v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+
+      sum += (v < 128) ? v : 256 - v;
+   }
+
+   for (lp = png_ptr->row_buf + 1; i < row_bytes; i++)
+   {
+      v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
+          & 0xff);
+
+      sum += (v < 128) ? v : 256 - v;
+
+      if (sum > lmins)  /* We are already worse, don't continue. */
+        break;
+   }
+
+   return (sum);
+}
+
+static png_size_t /* PRIVATE */
+png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp,
+    const png_size_t row_bytes, const png_size_t lmins)
+{
+   png_bytep rp, dp, pp, cp, lp;
+   png_size_t i;
+   png_size_t sum = 0;
+   int v;
+
+   png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
+
+   for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
+       pp = png_ptr->prev_row + 1; i < bpp; i++)
+   {
+      v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+      sum += (v < 128) ? v : 256 - v;
+   }
+
+   for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
+        i++)
+   {
+      int a, b, c, pa, pb, pc, p;
+
+      b = *pp++;
+      c = *cp++;
+      a = *lp++;
+
+      p = b - c;
+      pc = a - c;
+
+#ifdef PNG_USE_ABS
+      pa = abs(p);
+      pb = abs(pc);
+      pc = abs(p + pc);
+#else
+      pa = p < 0 ? -p : p;
+      pb = pc < 0 ? -pc : pc;
+      pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+      p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+      v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+
+      sum += (v < 128) ? v : 256 - v;
+
+      if (sum > lmins)  /* We are already worse, don't continue. */
+        break;
+   }
+
+   return (sum);
+}
+#endif /* WRITE_FILTER */
+
+void /* PRIVATE */
+png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
+{
+#ifndef PNG_WRITE_FILTER_SUPPORTED
+   png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1);
+#else
+   png_byte filter_to_do = png_ptr->do_filter;
+   png_bytep row_buf;
+   png_bytep best_row;
+   png_uint_32 bpp;
+   png_size_t mins;
+   png_size_t row_bytes = row_info->rowbytes;
+
+   png_debug(1, "in png_write_find_filter");
+
+   /* Find out how many bytes offset each pixel is */
+   bpp = (row_info->pixel_depth + 7) >> 3;
+
+   row_buf = png_ptr->row_buf;
+   mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the
+                               running sum */;
+
+   /* The prediction method we use is to find which method provides the
+    * smallest value when summing the absolute values of the distances
+    * from zero, using anything >= 128 as negative numbers.  This is known
+    * as the "minimum sum of absolute differences" heuristic.  Other
+    * heuristics are the "weighted minimum sum of absolute differences"
+    * (experimental and can in theory improve compression), and the "zlib
+    * predictive" method (not implemented yet), which does test compressions
+    * of lines using different filter methods, and then chooses the
+    * (series of) filter(s) that give minimum compressed data size (VERY
+    * computationally expensive).
+    *
+    * GRR 980525:  consider also
+    *
+    *   (1) minimum sum of absolute differences from running average (i.e.,
+    *       keep running sum of non-absolute differences & count of bytes)
+    *       [track dispersion, too?  restart average if dispersion too large?]
+    *
+    *  (1b) minimum sum of absolute differences from sliding average, probably
+    *       with window size <= deflate window (usually 32K)
+    *
+    *   (2) minimum sum of squared differences from zero or running average
+    *       (i.e., ~ root-mean-square approach)
+    */
+
+
+   /* We don't need to test the 'no filter' case if this is the only filter
+    * that has been chosen, as it doesn't actually do anything to the data.
+    */
+   best_row = png_ptr->row_buf;
+
+
+   if ((filter_to_do & PNG_FILTER_NONE) != 0 && filter_to_do != PNG_FILTER_NONE)
+   {
+      png_bytep rp;
+      png_size_t sum = 0;
+      png_size_t i;
+      int v;
+
+      if (PNG_SIZE_MAX/128 <= row_bytes)
+      {
+         for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
+         {
+            /* Check for overflow */
+            if (sum > PNG_SIZE_MAX/128 - 256)
+               break;
+
+            v = *rp;
+            sum += (v < 128) ? v : 256 - v;
+         }
+      }
+      else /* Overflow is not possible */
+      {
+         for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
+         {
+            v = *rp;
+            sum += (v < 128) ? v : 256 - v;
+         }
+      }
+
+      mins = sum;
+   }
+
+   /* Sub filter */
+   if (filter_to_do == PNG_FILTER_SUB)
+   /* It's the only filter so no testing is needed */
+   {
+      (void) png_setup_sub_row(png_ptr, bpp, row_bytes, mins);
+      best_row = png_ptr->try_row;
+   }
+
+   else if ((filter_to_do & PNG_FILTER_SUB) != 0)
+   {
+      png_size_t sum;
+      png_size_t lmins = mins;
+
+      sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins);
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->try_row;
+         if (png_ptr->tst_row != NULL)
+         {
+            png_ptr->try_row = png_ptr->tst_row;
+            png_ptr->tst_row = best_row;
+         }
+      }
+   }
+
+   /* Up filter */
+   if (filter_to_do == PNG_FILTER_UP)
+   {
+      (void) png_setup_up_row(png_ptr, row_bytes, mins);
+      best_row = png_ptr->try_row;
+   }
+
+   else if ((filter_to_do & PNG_FILTER_UP) != 0)
+   {
+      png_size_t sum;
+      png_size_t lmins = mins;
+
+      sum = png_setup_up_row(png_ptr, row_bytes, lmins);
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->try_row;
+         if (png_ptr->tst_row != NULL)
+         {
+            png_ptr->try_row = png_ptr->tst_row;
+            png_ptr->tst_row = best_row;
+         }
+      }
+   }
+
+   /* Avg filter */
+   if (filter_to_do == PNG_FILTER_AVG)
+   {
+      (void) png_setup_avg_row(png_ptr, bpp, row_bytes, mins);
+      best_row = png_ptr->try_row;
+   }
+
+   else if ((filter_to_do & PNG_FILTER_AVG) != 0)
+   {
+      png_size_t sum;
+      png_size_t lmins = mins;
+
+      sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins);
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->try_row;
+         if (png_ptr->tst_row != NULL)
+         {
+            png_ptr->try_row = png_ptr->tst_row;
+            png_ptr->tst_row = best_row;
+         }
+      }
+   }
+
+   /* Paeth filter */
+   if ((filter_to_do == PNG_FILTER_PAETH) != 0)
+   {
+      (void) png_setup_paeth_row(png_ptr, bpp, row_bytes, mins);
+      best_row = png_ptr->try_row;
+   }
+
+   else if ((filter_to_do & PNG_FILTER_PAETH) != 0)
+   {
+      png_size_t sum;
+      png_size_t lmins = mins;
+
+      sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins);
+
+      if (sum < mins)
+      {
+         best_row = png_ptr->try_row;
+         if (png_ptr->tst_row != NULL)
+         {
+            png_ptr->try_row = png_ptr->tst_row;
+            png_ptr->tst_row = best_row;
+         }
+      }
+   }
+
+   /* Do the actual writing of the filtered row data from the chosen filter. */
+   png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1);
+
+#endif /* WRITE_FILTER */
+}
+
+
+/* Do the actual writing of a previously filtered row. */
+static void
+png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
+   png_size_t full_row_length/*includes filter byte*/)
+{
+   png_debug(1, "in png_write_filtered_row");
+
+   png_debug1(2, "filter = %d", filtered_row[0]);
+
+   png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+   /* Swap the current and previous rows */
+   if (png_ptr->prev_row != NULL)
+   {
+      png_bytep tptr;
+
+      tptr = png_ptr->prev_row;
+      png_ptr->prev_row = png_ptr->row_buf;
+      png_ptr->row_buf = tptr;
+   }
+#endif /* WRITE_FILTER */
+
+   /* Finish row - updates counters and flushes zlib if last row */
+   png_write_finish_row(png_ptr);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+   png_ptr->flush_rows++;
+
+   if (png_ptr->flush_dist > 0 &&
+       png_ptr->flush_rows >= png_ptr->flush_dist)
+   {
+      png_write_flush(png_ptr);
+   }
+#endif /* WRITE_FLUSH */
+}
+#endif /* WRITE */
diff --git a/third_party/third_party.gyp b/third_party/third_party.gyp
index 54b6aa3..f653760 100644
--- a/third_party/third_party.gyp
+++ b/third_party/third_party.gyp
@@ -269,22 +269,28 @@
       'target_name': 'fx_lpng',
       'type': 'static_library',
       'sources': [
-        'lpng_v163/png.h',
-        'lpng_v163/png.c',
-        'lpng_v163/pngerror.c',
-        'lpng_v163/pngget.c',
-        'lpng_v163/pngmem.c',
-        'lpng_v163/pngpread.c',
-        'lpng_v163/pngread.c',
-        'lpng_v163/pngrio.c',
-        'lpng_v163/pngrtran.c',
-        'lpng_v163/pngrutil.c',
-        'lpng_v163/pngset.c',
-        'lpng_v163/pngtrans.c',
-        'lpng_v163/pngwio.c',
-        'lpng_v163/pngwrite.c',
-        'lpng_v163/pngwtran.c',
-        'lpng_v163/pngwutil.c',
+        'libpng/png.c',
+        'libpng/png.h',
+        'libpng/pngconf.h',
+        'libpng/pngdebug.h',
+        'libpng/pngerror.c',
+        'libpng/pngget.c',
+        'libpng/pnginfo.h',
+        'libpng/pnglibconf.h',
+        'libpng/pngmem.c',
+        'libpng/pngpread.c',
+        'libpng/pngpriv.h',
+        'libpng/pngread.c',
+        'libpng/pngrio.c',
+        'libpng/pngrtran.c',
+        'libpng/pngrutil.c',
+        'libpng/pngset.c',
+        'libpng/pngstruct.h',
+        'libpng/pngtrans.c',
+        'libpng/pngwio.c',
+        'libpng/pngwrite.c',
+        'libpng/pngwtran.c',
+        'libpng/pngwutil.c',
       ],
     },
     {