]> git.saurik.com Git - wxWidgets.git/blobdiff - src/png/png.c
Make wxRichTextRectArray usable by other parts of wxRTC
[wxWidgets.git] / src / png / png.c
index 7393dfa57f4e09eb8fdadc091efbd5953c4f91d9..d429c527c3889b8e047fc34c71a5228f1fa50466 100644 (file)
@@ -1,8 +1,8 @@
 
 /* png.c - location for general purpose libpng functions
  *
- * Last changed in libpng 1.5.6 [November 3, 2011]
- * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.2 [April 25, 2013]
+ * Copyright (c) 1998-2013 Glenn Randers-Pehrson
  * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
  * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
  *
@@ -14,7 +14,7 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_5_6 Your_png_h_is_not_version_1_5_6;
+typedef png_libpng_version_1_6_2 Your_png_h_is_not_version_1_6_2;
 
 /* 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
@@ -24,7 +24,7 @@ typedef png_libpng_version_1_5_6 Your_png_h_is_not_version_1_5_6;
 
 #ifdef PNG_READ_SUPPORTED
 void PNGAPI
-png_set_sig_bytes(png_structp png_ptr, int num_bytes)
+png_set_sig_bytes(png_structrp png_ptr, int num_bytes)
 {
    png_debug(1, "in png_set_sig_bytes");
 
@@ -62,7 +62,7 @@ png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
    if (start + num_to_check > 8)
       num_to_check = 8 - start;
 
-   return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check)));
+   return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));
 }
 
 #endif /* PNG_READ_SUPPORTED */
@@ -72,40 +72,34 @@ png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
 PNG_FUNCTION(voidpf /* PRIVATE */,
 png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
 {
-   png_voidp ptr;
-   png_structp p=(png_structp)png_ptr;
-   png_uint_32 save_flags=p->flags;
-   png_alloc_size_t num_bytes;
+   png_alloc_size_t num_bytes = size;
 
    if (png_ptr == NULL)
-      return (NULL);
+      return NULL;
 
-   if (items > PNG_UINT_32_MAX/size)
+   if (items >= (~(png_alloc_size_t)0)/size)
    {
-     png_warning (p, "Potential overflow in png_zalloc()");
-     return (NULL);
+      png_warning (png_voidcast(png_structrp, png_ptr),
+         "Potential overflow in png_zalloc()");
+      return NULL;
    }
-   num_bytes = (png_alloc_size_t)items * size;
-
-   p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
-   ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes);
-   p->flags=save_flags;
 
-   return ((voidpf)ptr);
+   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_structp)png_ptr, (png_voidp)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_structp png_ptr)
+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);
@@ -117,11 +111,11 @@ png_reset_crc(png_structp png_ptr)
  * trouble of calculating it.
  */
 void /* PRIVATE */
-png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length)
+png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length)
 {
    int need_crc = 1;
 
-   if (PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name))
+   if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))
    {
       if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
           (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
@@ -134,10 +128,10 @@ png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length)
          need_crc = 0;
    }
 
-   /* 'uLong' is defined 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.
+   /* '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)
    {
@@ -145,18 +139,18 @@ png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length)
 
       do
       {
-         uInt safeLength = (uInt)length;
-         if (safeLength == 0)
-            safeLength = (uInt)-1; /* evil, but safe */
+         uInt safe_length = (uInt)length;
+         if (safe_length == 0)
+            safe_length = (uInt)-1; /* evil, but safe */
 
-         crc = crc32(crc, ptr, safeLength);
+         crc = crc32(crc, ptr, safe_length);
 
-         /* The following should never issue compiler warnings, if they do the
+         /* 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 += safeLength;
-         length -= safeLength;
+         ptr += safe_length;
+         length -= safe_length;
       }
       while (length > 0);
 
@@ -166,10 +160,10 @@ png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length)
 }
 
 /* Check a user supplied version number, called from both read and write
- * functions that create a png_struct
+ * functions that create a png_struct.
  */
 int
-png_user_version_check(png_structp png_ptr, png_const_charp user_png_ver)
+png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
 {
    if (user_png_ver)
    {
@@ -190,20 +184,23 @@ png_user_version_check(png_structp png_ptr, png_const_charp user_png_ver)
      /* 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 digit.
+      * 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[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);
+         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
@@ -220,43 +217,145 @@ png_user_version_check(png_structp png_ptr, png_const_charp user_png_ver)
    return 1;
 }
 
-/* Allocate the memory for an info_struct for the application.  We don't
- * really need the png_ptr, but it could potentially be useful in the
- * future.  This should be used in favour of malloc(png_sizeof(png_info))
- * and png_info_init() so that applications that want to use a shared
- * libpng don't have to be recompiled if png_info changes size.
+/* 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_structp png_ptr),PNG_ALLOCATED)
+png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
 {
-   png_infop info_ptr;
+   png_inforp info_ptr;
 
    png_debug(1, "in png_create_info_struct");
 
    if (png_ptr == NULL)
-      return (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)));
 
-#ifdef PNG_USER_MEM_SUPPORTED
-   info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO,
-      png_ptr->malloc_fn, png_ptr->mem_ptr);
-#else
-   info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
-#endif
    if (info_ptr != NULL)
-      png_info_init_3(&info_ptr, png_sizeof(png_info));
+      memset(info_ptr, 0, (sizeof *info_ptr));
 
-   return (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.
+ * 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_structp png_ptr, png_infopp info_ptr_ptr)
+png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr)
 {
-   png_infop info_ptr = NULL;
+   png_inforp info_ptr = NULL;
 
    png_debug(1, "in png_destroy_info_struct");
 
@@ -268,46 +367,57 @@ png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr)
 
    if (info_ptr != NULL)
    {
-      png_info_destroy(png_ptr, info_ptr);
-
-#ifdef PNG_USER_MEM_SUPPORTED
-      png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn,
-          png_ptr->mem_ptr);
-#else
-      png_destroy_struct((png_voidp)info_ptr);
-#endif
+      /* 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.
+ * 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.
  */
-
-void PNGAPI
-png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size)
+PNG_FUNCTION(void,PNGAPI
+png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size),
+   PNG_DEPRECATED)
 {
-   png_infop info_ptr = *ptr_ptr;
+   png_inforp info_ptr = *ptr_ptr;
 
    png_debug(1, "in png_info_init_3");
 
    if (info_ptr == NULL)
       return;
 
-   if (png_sizeof(png_info) > png_info_struct_size)
+   if ((sizeof (png_info)) > png_info_struct_size)
    {
-      png_destroy_struct(info_ptr);
-      info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+      *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 */
-   png_memset(info_ptr, 0, png_sizeof(png_info));
+   memset(info_ptr, 0, (sizeof *info_ptr));
 }
 
+/* The following API is not called internally */
 void PNGAPI
-png_data_freer(png_structp png_ptr, png_infop info_ptr,
+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");
@@ -322,12 +432,11 @@ png_data_freer(png_structp png_ptr, png_infop info_ptr,
       info_ptr->free_me &= ~mask;
 
    else
-      png_warning(png_ptr,
-         "Unknown freer parameter in png_data_freer");
+      png_error(png_ptr, "Unknown freer parameter in png_data_freer");
 }
 
 void PNGAPI
-png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
+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");
@@ -392,8 +501,8 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
       info_ptr->pcal_units = NULL;
       if (info_ptr->pcal_params != NULL)
          {
-            int i;
-            for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
+            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;
@@ -406,7 +515,7 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
 #endif
 
 #ifdef PNG_iCCP_SUPPORTED
-   /* Free any iCCP entry */
+   /* Free any profile entry */
    if ((mask & PNG_FREE_ICCP) & info_ptr->free_me)
    {
       png_free(png_ptr, info_ptr->iccp_name);
@@ -437,8 +546,8 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
          if (info_ptr->splt_palettes_num)
          {
             int i;
-            for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
-               png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, 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;
@@ -449,13 +558,7 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
    }
 #endif
 
-#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
-   if (png_ptr->unknown_chunk.data)
-   {
-      png_free(png_ptr, png_ptr->unknown_chunk.data);
-      png_ptr->unknown_chunk.data = NULL;
-   }
-
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
    if ((mask & PNG_FREE_UNKN) & info_ptr->free_me)
    {
       if (num != -1)
@@ -474,7 +577,7 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
          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, 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;
@@ -497,7 +600,7 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
    /* Free any PLTE entry that was internally allocated */
    if ((mask & PNG_FREE_PLTE) & info_ptr->free_me)
    {
-      png_zfree(png_ptr, info_ptr->palette);
+      png_free(png_ptr, info_ptr->palette);
       info_ptr->palette = NULL;
       info_ptr->valid &= ~PNG_INFO_PLTE;
       info_ptr->num_palette = 0;
@@ -509,8 +612,8 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
    {
       if (info_ptr->row_pointers)
       {
-         int row;
-         for (row = 0; row < (int)info_ptr->height; row++)
+         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;
@@ -527,29 +630,6 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
 
    info_ptr->free_me &= ~mask;
 }
-
-/* This is an internal routine to free any memory that the info struct is
- * pointing to before re-using it or freeing the struct itself.  Recall
- * that png_free() checks for NULL pointers for us.
- */
-void /* PRIVATE */
-png_info_destroy(png_structp png_ptr, png_infop info_ptr)
-{
-   png_debug(1, "in png_info_destroy");
-
-   png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
-
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-   if (png_ptr->num_chunk_list)
-   {
-      png_free(png_ptr, png_ptr->chunk_list);
-      png_ptr->chunk_list = NULL;
-      png_ptr->num_chunk_list = 0;
-   }
-#endif
-
-   png_info_init_3(&info_ptr, png_sizeof(png_info));
-}
 #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
 
 /* This function returns a pointer to the io_ptr associated with the user
@@ -557,7 +637,7 @@ png_info_destroy(png_structp png_ptr, png_infop info_ptr)
  * pointer before png_write_destroy() or png_read_destroy() are called.
  */
 png_voidp PNGAPI
-png_get_io_ptr(png_structp png_ptr)
+png_get_io_ptr(png_const_structrp png_ptr)
 {
    if (png_ptr == NULL)
       return (NULL);
@@ -574,7 +654,7 @@ png_get_io_ptr(png_structp png_ptr)
  * function of your own because "FILE *" isn't necessarily available.
  */
 void PNGAPI
-png_init_io(png_structp png_ptr, png_FILE_p fp)
+png_init_io(png_structrp png_ptr, png_FILE_p fp)
 {
    png_debug(1, "in png_init_io");
 
@@ -585,44 +665,64 @@ png_init_io(png_structp png_ptr, png_FILE_p 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.
  */
-png_const_charp PNGAPI
-png_convert_to_rfc1123(png_structp png_ptr, png_const_timep ptime)
+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 (png_ptr == NULL)
-      return (NULL);
+   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 */
+      char number_buf[5]; /* enough for a four-digit year */
 
-#     define APPEND_STRING(string)\
-         pos = png_safecat(png_ptr->time_buffer, sizeof png_ptr->time_buffer,\
-            pos, (string))
+#     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 < (sizeof png_ptr->time_buffer)-1)\
-            png_ptr->time_buffer[pos++] = (ch)
+#     define APPEND(ch) if (pos < 28) out[pos++] = (ch)
 
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day % 32);
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day);
       APPEND(' ');
-      APPEND_STRING(short_months[(ptime->month - 1) % 12]);
+      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 % 24);
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour);
       APPEND(':');
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute % 60);
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute);
       APPEND(':');
-      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second % 61);
+      APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second);
       APPEND_STRING(" +0000"); /* This reliably terminates the buffer */
 
 #     undef APPEND
@@ -630,14 +730,37 @@ png_convert_to_rfc1123(png_structp png_ptr, png_const_timep ptime)
 #     undef APPEND_STRING
    }
 
-   return png_ptr->time_buffer;
+   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_structp png_ptr)
+png_get_copyright(png_const_structrp png_ptr)
 {
    PNG_UNUSED(png_ptr)  /* Silence compiler warning about unused png_ptr */
 #ifdef PNG_STRING_COPYRIGHT
@@ -645,14 +768,14 @@ png_get_copyright(png_const_structp png_ptr)
 #else
 #  ifdef __STDC__
    return PNG_STRING_NEWLINE \
-     "libpng version 1.5.6 - November 3, 2011" PNG_STRING_NEWLINE \
-     "Copyright (c) 1998-2011 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
+     "libpng version 1.6.2 - April 25, 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.5.6 - November 3, 2011\
-      Copyright (c) 1998-2011 Glenn Randers-Pehrson\
+      return "libpng version 1.6.2 - April 25, 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
@@ -668,14 +791,14 @@ png_get_copyright(png_const_structp png_ptr)
  * it is guaranteed that png.c uses the correct version of png.h.
  */
 png_const_charp PNGAPI
-png_get_libpng_ver(png_const_structp png_ptr)
+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_structp png_ptr)
+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 */
@@ -683,7 +806,7 @@ png_get_header_ver(png_const_structp png_ptr)
 }
 
 png_const_charp PNGAPI
-png_get_header_version(png_const_structp png_ptr)
+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 */
@@ -698,52 +821,61 @@ png_get_header_version(png_const_structp png_ptr)
 #endif
 }
 
-#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
 int PNGAPI
-png_handle_as_unknown(png_structp png_ptr, png_const_bytep chunk_name)
+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)
+   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, so it should continue
-    * to do so in case there are duplicated entries.
+    * 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 (!png_memcmp(chunk_name, p, 4))
+
+      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_structp png_ptr, png_uint_32 chunk_name)
+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
+#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_structp png_ptr)
+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 */
@@ -759,114 +891,271 @@ png_access_version_number(void)
 
 
 #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_CHECK_cHRM_SUPPORTED
-
-int /* PRIVATE */
-png_check_cHRM_fixed(png_structp png_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)
+#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
+    */
 {
-   int ret = 1;
-   unsigned long xy_hi,xy_lo,yx_hi,yx_lo;
+   png_fixed_point gtest;
 
-   png_debug(1, "in function png_check_cHRM_fixed");
+   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;
+      }
 
-   if (png_ptr == NULL)
-      return 0;
+      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;
+}
 
-   /* (x,y,z) values are first limited to 0..100000 (PNG_FP_1), the white
-    * y must also be greater than 0.  To test for the upper limit calculate
-    * (PNG_FP_1-y) - x must be <= to this for z to be >= 0 (and the expression
-    * cannot overflow.)  At this point we know x and y are >= 0 and (x+y) is
-    * <= PNG_FP_1.  The previous test on PNG_MAX_UINT_31 is removed because it
-    * pointless (and it produces compiler warnings!)
+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.
     */
-   if (white_x < 0 || white_y <= 0 ||
-         red_x < 0 ||   red_y <  0 ||
-       green_x < 0 || green_y <  0 ||
-        blue_x < 0 ||  blue_y <  0)
-   {
-      png_warning(png_ptr,
-        "Ignoring attempt to set negative chromaticity value");
-      ret = 0;
-   }
-   /* And (x+y) must be <= PNG_FP_1 (so z is >= 0) */
-   if (white_x > PNG_FP_1 - white_y)
-   {
-      png_warning(png_ptr, "Invalid cHRM white point");
-      ret = 0;
-   }
+   png_const_charp errmsg;
 
-   if (red_x > PNG_FP_1 - red_y)
+   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
    {
-      png_warning(png_ptr, "Invalid cHRM red point");
-      ret = 0;
+      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;
    }
 
-   if (green_x > PNG_FP_1 - green_y)
+   /* 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)
    {
-      png_warning(png_ptr, "Invalid cHRM green point");
-      ret = 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
    }
 
-   if (blue_x > PNG_FP_1 - blue_y)
+   else
    {
-      png_warning(png_ptr, "Invalid cHRM blue point");
-      ret = 0;
-   }
+#     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;
 
-   png_64bit_product(green_x - red_x, blue_y - red_y, &xy_hi, &xy_lo);
-   png_64bit_product(green_y - red_y, blue_x - red_x, &yx_hi, &yx_lo);
+         if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)
+            info_ptr->valid |= PNG_INFO_cHRM;
 
-   if (xy_hi == yx_hi && xy_lo == yx_lo)
-   {
-      png_warning(png_ptr,
-         "Ignoring attempt to set cHRM RGB triangle with zero area");
-      ret = 0;
+         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;
 
-   return ret;
+   info_ptr->colorspace = png_ptr->colorspace;
+   png_colorspace_sync_info(png_ptr, info_ptr);
 }
-#  endif /* PNG_CHECK_cHRM_SUPPORTED */
+#endif
+#endif
 
-#ifdef PNG_cHRM_SUPPORTED
+#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.
  */
-int png_xy_from_XYZ(png_xy *xy, png_XYZ XYZ)
+static int
+png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ)
 {
    png_int_32 d, dwhite, whiteX, whiteY;
 
-   d = XYZ.redX + XYZ.redY + XYZ.redZ;
-   if (!png_muldiv(&xy->redx, XYZ.redX, PNG_FP_1, d)) return 1;
-   if (!png_muldiv(&xy->redy, XYZ.redY, PNG_FP_1, d)) return 1;
+   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.redX;
-   whiteY = XYZ.redY;
+   whiteX = XYZ->red_X;
+   whiteY = XYZ->red_Y;
 
-   d = XYZ.greenX + XYZ.greenY + XYZ.greenZ;
-   if (!png_muldiv(&xy->greenx, XYZ.greenX, PNG_FP_1, d)) return 1;
-   if (!png_muldiv(&xy->greeny, XYZ.greenY, PNG_FP_1, d)) return 1;
+   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.greenX;
-   whiteY += XYZ.greenY;
+   whiteX += XYZ->green_X;
+   whiteY += XYZ->green_Y;
 
-   d = XYZ.blueX + XYZ.blueY + XYZ.blueZ;
-   if (!png_muldiv(&xy->bluex, XYZ.blueX, PNG_FP_1, d)) return 1;
-   if (!png_muldiv(&xy->bluey, XYZ.blueY, PNG_FP_1, d)) return 1;
+   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.blueX;
-   whiteY += XYZ.blueY;
+   whiteX += XYZ->blue_X;
+   whiteY += XYZ->blue_Y;
 
-   /* The reference white is simply the same of the end-point (X,Y,Z) vectors,
+   /* 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;
@@ -875,7 +1164,8 @@ int png_xy_from_XYZ(png_xy *xy, png_XYZ XYZ)
    return 0;
 }
 
-int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy)
+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;
@@ -884,14 +1174,14 @@ int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy)
     * 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;
+   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
@@ -959,8 +1249,8 @@ int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy)
     * and it is certain that it becomes unstable where the end points are close
     * together.
     *
-    * So this code uses the perhaps slighly less optimal but more understandable
-    * and totally obvious approach of calculating color-scale.
+    * 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
@@ -1069,94 +1359,1004 @@ int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy)
     *  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.
+   /* 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.
     */
-   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;
+   temp = png_get_uint_32(profile+20);
+   switch (temp)
+   {
+      case 0x58595A20: /* 'XYZ ' */
+      case 0x4C616220: /* 'Lab ' */
+         break;
 
-   /* 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;
+      default:
+         return png_icc_profile_error(png_ptr, colorspace, name, temp,
+            "unexpected ICC PCS encoding");
+   }
 
-   /* 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.
+   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').
     */
-   if (!png_muldiv(&red_inverse, xy.whitey, denominator, left-right) ||
-       red_inverse <= xy.whitey /* r+g+b scales = white scale */)
-      return 1;
+   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");
+      }
 
-   /* 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;
+      /* 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");
+   }
 
-   /* And the blue scale, the checks above guarantee this can't overflow but it
-    * can still produce 0 for extreme cHRM values.
+   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.
     */
-   blue_scale = png_reciprocal(xy.whitey) - png_reciprocal(red_inverse) -
-      png_reciprocal(green_inverse);
-   if (blue_scale <= 0) return 1;
+   /* 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")
+};
 
-   /* And fill in the png_XYZ: */
-   if (!png_muldiv(&XYZ->redX, xy.redx, PNG_FP_1, red_inverse)) return 1;
-   if (!png_muldiv(&XYZ->redY, xy.redy, PNG_FP_1, red_inverse)) return 1;
-   if (!png_muldiv(&XYZ->redZ, PNG_FP_1 - xy.redx - xy.redy, PNG_FP_1,
-      red_inverse))
-      return 1;
+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;
 
-   if (!png_muldiv(&XYZ->greenX, xy.greenx, PNG_FP_1, green_inverse)) return 1;
-   if (!png_muldiv(&XYZ->greenY, xy.greeny, PNG_FP_1, green_inverse)) return 1;
-   if (!png_muldiv(&XYZ->greenZ, PNG_FP_1 - xy.greenx - xy.greeny, PNG_FP_1,
-      green_inverse))
-      return 1;
+   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
 
-   if (!png_muldiv(&XYZ->blueX, xy.bluex, blue_scale, PNG_FP_1)) return 1;
-   if (!png_muldiv(&XYZ->blueY, xy.bluey, blue_scale, PNG_FP_1)) return 1;
-   if (!png_muldiv(&XYZ->blueZ, PNG_FP_1 - xy.bluex - xy.bluey, blue_scale,
-      PNG_FP_1))
-      return 1;
+         /* 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);
+         }
 
-   return 0; /*success*/
+         /* 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
 
-int png_XYZ_from_xy_checked(png_structp png_ptr, png_XYZ *XYZ, png_xy xy)
+#ifdef PNG_sRGB_SUPPORTED
+void /* PRIVATE */
+png_icc_set_sRGB(png_const_structrp png_ptr,
+   png_colorspacerp colorspace, png_const_bytep profile, uLong adler)
 {
-   switch (png_XYZ_from_xy(XYZ, xy))
-   {
-      case 0: /* success */
-         return 1;
+   /* 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 */
 
-      case 1:
-         /* The chunk may be technically valid, but we got png_fixed_point
-          * overflow while trying to get XYZ values out of it.  This is
-          * entirely benign - the cHRM chunk is pretty extreme.
-          */
-         png_warning(png_ptr,
-            "extreme cHRM chunk cannot be converted to tristimulus values");
-         break;
+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;
 
-      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.
-          */
-         png_error(png_ptr, "internal error in png_XYZ_from_xy");
-         break;
+   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;
    }
 
-   /* ERROR RETURN */
+   /* 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_structp png_ptr,
+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)
@@ -1448,7 +2648,7 @@ png_check_fp_string(png_const_charp string, png_size_t size)
 }
 #endif /* pCAL or sCAL */
 
-#ifdef PNG_READ_sCAL_SUPPORTED
+#ifdef PNG_sCAL_SUPPORTED
 #  ifdef PNG_FLOATING_POINT_SUPPORTED
 /* Utility used below - a simple accurate power of ten from an integral
  * exponent.
@@ -1491,7 +2691,7 @@ png_pow10(int power)
  * precision.
  */
 void /* PRIVATE */
-png_ascii_from_fp(png_structp png_ptr, png_charp ascii, png_size_t size,
+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
@@ -1805,8 +3005,8 @@ png_ascii_from_fp(png_structp png_ptr, png_charp ascii, png_size_t size,
 /* Function to format a fixed point value in ASCII.
  */
 void /* PRIVATE */
-png_ascii_from_fixed(png_structp png_ptr, png_charp ascii, png_size_t size,
-    png_fixed_point fp)
+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:
@@ -1876,9 +3076,14 @@ png_ascii_from_fixed(png_structp png_ptr, png_charp ascii, png_size_t size,
 #endif /* READ_SCAL */
 
 #if defined(PNG_FLOATING_POINT_SUPPORTED) && \
-   !defined(PNG_FIXED_POINT_MACRO_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_structp png_ptr, double fp, png_const_charp text)
+png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text)
 {
    double r = floor(100000 * fp + .5);
 
@@ -1890,7 +3095,7 @@ png_fixed(png_structp png_ptr, double fp, png_const_charp text)
 #endif
 
 #if defined(PNG_READ_GAMMA_SUPPORTED) || \
-    defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG__READ_pHYs_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
@@ -2017,7 +3222,7 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
  * result.
  */
 png_fixed_point
-png_muldiv_warn(png_structp png_ptr, png_fixed_point a, png_int_32 times,
+png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times,
     png_int_32 divisor)
 {
    png_fixed_point result;
@@ -2030,7 +3235,7 @@ png_muldiv_warn(png_structp png_ptr, png_fixed_point a, png_int_32 times,
 }
 #endif
 
-#ifdef PNG_READ_GAMMA_SUPPORTED /* more fixed point functions for gammma */
+#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)
@@ -2050,6 +3255,18 @@ png_reciprocal(png_fixed_point a)
    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)
@@ -2100,73 +3317,28 @@ png_reciprocal2(png_fixed_point a, png_fixed_point b)
 }
 #endif /* READ_GAMMA */
 
-#ifdef PNG_CHECK_cHRM_SUPPORTED
-/* Added at libpng version 1.2.34 (Dec 8, 2008) and 1.4.0 (Jan 2,
- * 2010: moved from pngset.c) */
-/*
- *    Multiply two 32-bit numbers, V1 and V2, using 32-bit
- *    arithmetic, to produce a 64-bit result in the HI/LO words.
- *
- *                  A B
- *                x C D
- *               ------
- *              AD || BD
- *        AC || CB || 0
- *
- *    where A and B are the high and low 16-bit words of V1,
- *    C and D are the 16-bit words of V2, AD is the product of
- *    A and D, and X || Y is (X << 16) + Y.
-*/
-
-void /* PRIVATE */
-png_64bit_product (long v1, long v2, unsigned long *hi_product,
-    unsigned long *lo_product)
-{
-   int a, b, c, d;
-   long lo, hi, x, y;
-
-   a = (v1 >> 16) & 0xffff;
-   b = v1 & 0xffff;
-   c = (v2 >> 16) & 0xffff;
-   d = v2 & 0xffff;
-
-   lo = b * d;                   /* BD */
-   x = a * d + c * b;            /* AD + CB */
-   y = ((lo >> 16) & 0xffff) + x;
-
-   lo = (lo & 0xffff) | ((y & 0xffff) << 16);
-   hi = (y >> 16) & 0xffff;
-
-   hi += a * c;                  /* AC */
-
-   *hi_product = (unsigned long)hi;
-   *lo_product = (unsigned long)lo;
-}
-#endif /* CHECK_cHRM */
-
 #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.  The programs are given
- * at the head of each table.
+ * 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 png_uint_32
+static const png_uint_32
 png_8bit_l2[128] =
 {
-#  if PNG_DO_BC
-      for (i=128;i<256;++i) { .5 - l(i/255)/l(2)*65536*65536; }
-#  endif
    4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
    3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
    3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
@@ -2189,6 +3361,7 @@ png_8bit_l2[128] =
    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
@@ -2210,18 +3383,18 @@ png_8bit_l2[128] =
 #endif
 };
 
-PNG_STATIC png_int_32
+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 7.99998 for the overflow (log 0) case - so the result is
+    * input), return -1 for the overflow (log 0) case, - so the result is
     * always at most 19 bits.
     */
    if ((x &= 0xff) == 0)
-      return 0xffffffff;
+      return -1;
 
    if ((x & 0xf0) == 0)
       lg2  = 4, x <<= 4;
@@ -2266,14 +3439,14 @@ png_log8bit(unsigned int x)
  * Zero  (257):      0
  * End   (258):  23499
  */
-PNG_STATIC png_int_32
+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 0xffffffff;
+      return -1;
 
    if ((x & 0xff00) == 0)
       lg2  = 8, x <<= 8;
@@ -2331,12 +3504,9 @@ png_log16bit(png_uint_32 x)
  * 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 png_uint_32
+static const png_uint_32
 png_32bit_exp[16] =
 {
-#  if PNG_DO_BC
-      for (i=0;i<16;++i) { .5 + e(-i/16*l(2))*2^32; }
-#  endif
    /* 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,
@@ -2344,7 +3514,7 @@ png_32bit_exp[16] =
 };
 
 /* Adjustment table; provided to explain the numbers in the code below. */
-#if PNG_DO_BC
+#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
@@ -2360,7 +3530,7 @@ for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
     0 45425.85339951654943850496
 #endif
 
-PNG_STATIC png_uint_32
+static png_uint_32
 png_exp(png_fixed_point x)
 {
    if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
@@ -2408,7 +3578,7 @@ png_exp(png_fixed_point x)
    return 0;
 }
 
-PNG_STATIC png_byte
+static png_byte
 png_exp8bit(png_fixed_point lg2)
 {
    /* Get a 32-bit value: */
@@ -2422,7 +3592,7 @@ png_exp8bit(png_fixed_point lg2)
    return (png_byte)((x + 0x7fffffU) >> 24);
 }
 
-PNG_STATIC png_uint_16
+static png_uint_16
 png_exp16bit(png_fixed_point lg2)
 {
    /* Get a 32-bit value: */
@@ -2486,7 +3656,7 @@ png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)
  * 8-bit (as are the arguments.)
  */
 png_uint_16 /* PRIVATE */
-png_gamma_correct(png_structp png_ptr, unsigned int value,
+png_gamma_correct(png_structrp png_ptr, unsigned int value,
     png_fixed_point gamma_val)
 {
    if (png_ptr->bit_depth == 8)
@@ -2496,16 +3666,6 @@ png_gamma_correct(png_structp png_ptr, unsigned int value,
       return png_gamma_16bit_correct(value, gamma_val);
 }
 
-/* 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;
-}
-
 /* 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).
@@ -2515,7 +3675,7 @@ png_gamma_significant(png_fixed_point gamma_val)
  * should be somewhere that will be cleaned.
  */
 static void
-png_build_16bit_table(png_structp png_ptr, png_uint_16pp *ptable,
+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': */
@@ -2525,12 +3685,12 @@ png_build_16bit_table(png_structp png_ptr, png_uint_16pp *ptable,
    unsigned int i;
 
    png_uint_16pp table = *ptable =
-       (png_uint_16pp)png_calloc(png_ptr, num * png_sizeof(png_uint_16p));
+       (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 * png_sizeof(png_uint_16));
+          (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.
@@ -2583,7 +3743,7 @@ png_build_16bit_table(png_structp png_ptr, png_uint_16pp *ptable,
  * required.
  */
 static void
-png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
+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);
@@ -2592,7 +3752,7 @@ png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
    png_uint_32 last;
 
    png_uint_16pp table = *ptable =
-       (png_uint_16pp)png_calloc(png_ptr, num * png_sizeof(png_uint_16p));
+       (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
@@ -2600,7 +3760,7 @@ png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
     */
    for (i = 0; i < num; i++)
       table[i] = (png_uint_16p)png_malloc(png_ptr,
-          256 * png_sizeof(png_uint_16));
+          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.
@@ -2650,7 +3810,7 @@ png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
  * (apparently contrary to the spec) so a 256 entry table is always generated.
  */
 static void
-png_build_8bit_table(png_structp png_ptr, png_bytepp ptable,
+png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable,
    PNG_CONST png_fixed_point gamma_val)
 {
    unsigned int i;
@@ -2667,7 +3827,7 @@ png_build_8bit_table(png_structp png_ptr, png_bytepp ptable,
  * tables.
  */
 void /* PRIVATE */
-png_destroy_gamma_table(png_structp png_ptr)
+png_destroy_gamma_table(png_structrp png_ptr)
 {
    png_free(png_ptr, png_ptr->gamma_table);
    png_ptr->gamma_table = NULL;
@@ -2723,7 +3883,7 @@ png_destroy_gamma_table(png_structp png_ptr)
  * we don't need to allocate > 64K chunks for a full 16-bit table.
  */
 void /* PRIVATE */
-png_build_gamma_table(png_structp png_ptr, int bit_depth)
+png_build_gamma_table(png_structrp png_ptr, int bit_depth)
 {
   png_debug(1, "in png_build_gamma_table");
 
@@ -2742,7 +3902,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
   if (bit_depth <= 8)
   {
      png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
-         png_ptr->screen_gamma > 0 ?  png_reciprocal2(png_ptr->gamma,
+         png_ptr->screen_gamma > 0 ?  png_reciprocal2(png_ptr->colorspace.gamma,
          png_ptr->screen_gamma) : PNG_FP_1);
 
 #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
@@ -2751,11 +3911,11 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
      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->gamma));
+            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->gamma/* Probably doing rgb_to_gray */);
+            png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */);
      }
 #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
   }
@@ -2824,13 +3984,13 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
      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->gamma,
+         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->gamma,
+         png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma,
          png_ptr->screen_gamma) : PNG_FP_1);
 #endif
 
@@ -2840,7 +4000,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
      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->gamma));
+            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.
@@ -2848,10 +4008,291 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
          */
         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->gamma/* Probably doing rgb_to_gray */);
+            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) */