DO NOT MERGE Update libpng to 1.6.20

BUG:23265085
Change-Id: I28912577d10ea5bafd139c4ca1baf3b543bf36a9
diff --git a/contrib/README.txt b/contrib/README.txt
new file mode 100644
index 0000000..97963c6
--- /dev/null
+++ b/contrib/README.txt
@@ -0,0 +1,5 @@
+
+This "contrib" directory contains contributions which are not necessarily under
+the libpng license, although all are open source.  They are not part of
+libpng proper and are not used for building the library, although some are used
+for testing the library via "make check".
diff --git a/contrib/arm-neon/README b/contrib/arm-neon/README
new file mode 100644
index 0000000..b4248cf
--- /dev/null
+++ b/contrib/arm-neon/README
@@ -0,0 +1,83 @@
+OPERATING SYSTEM SPECIFIC ARM NEON DETECTION
+--------------------------------------------
+
+Detection of the ability to execute ARM NEON on an ARM processor requires
+operating system support.  (The information is not available in user mode.)
+
+HOW TO USE THIS
+---------------
+
+This directory contains C code fragments that can be included in arm/arm_init.c
+by setting the macro PNG_ARM_NEON_FILE to the file name in "" or <> at build
+time.  This setting is not recorded in pnglibconf.h and can be changed simply by
+rebuilding arm/arm_init.o with the required macro definition.
+
+For any of this code to be used the ARM NEON code must be enabled and run time
+checks must be supported.  I.e.:
+
+#if PNG_ARM_NEON_OPT > 0
+#ifdef PNG_ARM_NEON_CHECK_SUPPORTED
+
+This is done in a 'configure' build by passing configure the argument:
+
+   --enable-arm-neon=check
+
+Apart from the basic Linux implementation in contrib/arm-neon/linux.c this code
+is unsupported.  That means that it is not even compiled on a regular basis and
+may be broken in any given minor release.
+
+FILE FORMAT
+-----------
+
+Each file documents its testing status as of the last time it was tested (which
+may have been a long time ago):
+
+STATUS: one of:
+   SUPPORTED: This indicates that the file is included in the regularly
+         performed test builds and bugs are fixed when discovered.
+   COMPILED: This indicates that the code did compile at least once.  See the
+         more detailed description for the extent to which the result was
+         successful.
+   TESTED: This means the code was fully compiled into the libpng test programs
+         and these were run at least once.
+
+BUG REPORTS: an email address to which to send reports of problems
+
+The file is a fragment of C code. It should not define any 'extern' symbols;
+everything should be static.  It must define the function:
+
+static int png_have_neon(png_structp png_ptr);
+
+That function must return 1 if ARM NEON instructions are supported, 0 if not.
+It must not execute png_error unless it detects a bug.  A png_error will prevent
+the reading of the PNG and in the future, writing too.
+
+BUG REPORTS
+-----------
+
+If you mail a bug report for any file that is not SUPPORTED there may only be
+limited response.  Consider fixing it and sending a patch to fix the problem -
+this is more likely to result in action.
+
+CONTRIBUTIONS
+-------------
+
+You may send contributions of new implementations to
+png-mng-implement@sourceforge.net.  Please write code in strict C90 C where
+possible.  Obviously OS dependencies are to be expected.  If you submit code you
+must have the authors permission and it must have a license that is acceptable
+to the current maintainer; in particular that license must permit modification
+and redistribution.
+
+Please try to make the contribution a single file and give the file a clear and
+unambiguous name that identifies the target OS.  If multiple files really are
+required put them all in a sub-directory.
+
+You must also be prepared to handle bug reports from users of the code, either
+by joining the png-mng-implement mailing list or by providing an email for the
+"BUG REPORTS" entry or both.  Please make sure that the header of the file
+contains the STATUS and BUG REPORTS fields as above.
+
+Please list the OS requirements as precisely as possible.  Ideally you should
+also list the environment in which the code has been tested and certainly list
+any environments where you suspect it might not work.
diff --git a/contrib/arm-neon/android-ndk.c b/contrib/arm-neon/android-ndk.c
new file mode 100644
index 0000000..7240123
--- /dev/null
+++ b/contrib/arm-neon/android-ndk.c
@@ -0,0 +1,39 @@
+/* contrib/arm-neon/android-ndk.c
+ *
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by John Bowler, 2014.
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/arm-neon/README before reporting bugs
+ *
+ * STATUS: COMPILED, UNTESTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_neon implemented for the Android NDK, see:
+ *
+ * Documentation:
+ *    http://www.kandroid.org/ndk/docs/CPU-ARM-NEON.html
+ *    http://code.google.com/p/android/issues/detail?id=49065
+ *
+ * NOTE: this requires that libpng is built against the Android NDK and linked
+ * with an implementation of the Android ARM 'cpu-features' library.  The code
+ * has been compiled only, not linked: no version of the library has been found,
+ * only the header files exist in the NDK.
+ */
+#include <cpu-features.h>
+
+static int
+png_have_neon(png_structp png_ptr)
+{
+   /* This is a whole lot easier than the linux code, however it is probably
+    * implemented as below, therefore it is better to cache the result (these
+    * function calls may be slow!)
+    */
+   PNG_UNUSED(png_ptr)
+   return android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
+      (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+}
diff --git a/contrib/arm-neon/linux-auxv.c b/contrib/arm-neon/linux-auxv.c
new file mode 100644
index 0000000..4d26bd3
--- /dev/null
+++ b/contrib/arm-neon/linux-auxv.c
@@ -0,0 +1,120 @@
+/* contrib/arm-neon/linux-auxv.c
+ *
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by Mans Rullgard, 2011.
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/arm-neon/README before reporting bugs
+ *
+ * STATUS: COMPILED, TESTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_neon implemented for Linux versions which allow access to
+ * /proc/self/auxv.  This is probably faster, cleaner and safer than the code to
+ * read /proc/cpuinfo in contrib/arm-neon/linux, however it is yet another piece
+ * of potentially untested code and has more complex dependencies than the code
+ * to read cpuinfo.
+ *
+ * This generic __linux__ implementation requires reading /proc/self/auxv and
+ * looking at each element for one that records NEON capabilities.
+ */
+#include <unistd.h> /* for POSIX 1003.1 */
+#include <errno.h>  /* for EINTR */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <asm/hwcap.h>
+
+/* A read call may be interrupted, in which case it returns -1 and sets errno to
+ * EINTR if nothing was done, otherwise (if something was done) a partial read
+ * may result.
+ */
+static size_t
+safe_read(png_structp png_ptr, int fd, void *buffer_in, size_t nbytes)
+{
+   size_t ntotal = 0;
+   char *buffer = png_voidcast(char*, buffer_in);
+
+   while (nbytes > 0)
+   {
+      unsigned int nread;
+      int iread;
+
+      /* Passing nread > INT_MAX to read is implementation defined in POSIX
+       * 1003.1, therefore despite the unsigned argument portable code must
+       * limit the value to INT_MAX!
+       */
+      if (nbytes > INT_MAX)
+         nread = INT_MAX;
+
+      else
+         nread = (unsigned int)/*SAFE*/nbytes;
+
+      iread = read(fd, buffer, nread);
+
+      if (iread == -1)
+      {
+         /* This is the devil in the details, a read can terminate early with 0
+          * bytes read because of EINTR, yet it still returns -1 otherwise end
+          * of file cannot be distinguished.
+          */
+         if (errno != EINTR)
+         {
+            png_warning(png_ptr, "/proc read failed");
+            return 0; /* I.e., a permanent failure */
+         }
+      }
+
+      else if (iread < 0)
+      {
+         /* Not a valid 'read' result: */
+         png_warning(png_ptr, "OS /proc read bug");
+         return 0;
+      }
+
+      else if (iread > 0)
+      {
+         /* Continue reading until a permanent failure, or EOF */
+         buffer += iread;
+         nbytes -= (unsigned int)/*SAFE*/iread;
+         ntotal += (unsigned int)/*SAFE*/iread;
+      }
+
+      else
+         return ntotal;
+   }
+
+   return ntotal; /* nbytes == 0 */
+}
+
+static int
+png_have_neon(png_structp png_ptr)
+{
+   int fd = open("/proc/self/auxv", O_RDONLY);
+   Elf32_auxv_t aux;
+
+   /* Failsafe: failure to open means no NEON */
+   if (fd == -1)
+   {
+      png_warning(png_ptr, "/proc/self/auxv open failed");
+      return 0;
+   }
+
+   while (safe_read(png_ptr, fd, &aux, sizeof aux) == sizeof aux)
+   {
+      if (aux.a_type == AT_HWCAP && (aux.a_un.a_val & HWCAP_NEON) != 0)
+      {
+         close(fd);
+         return 1;
+      }
+   }
+
+   close(fd);
+   return 0;
+}
diff --git a/contrib/arm-neon/linux.c b/contrib/arm-neon/linux.c
new file mode 100644
index 0000000..94f9bb1
--- /dev/null
+++ b/contrib/arm-neon/linux.c
@@ -0,0 +1,161 @@
+/* contrib/arm-neon/linux.c
+ *
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by John Bowler, 2014.
+ * Last changed in libpng 1.6.16 [December 22, 2014]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/arm-neon/README before reporting bugs
+ *
+ * STATUS: SUPPORTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_neon implemented for Linux by reading the widely available
+ * pseudo-file /proc/cpuinfo.
+ *
+ * This code is strict ANSI-C and is probably moderately portable; it does
+ * however use <stdio.h> and it assumes that /proc/cpuinfo is never localized.
+ */
+#include <stdio.h>
+
+static int
+png_have_neon(png_structp png_ptr)
+{
+   FILE *f = fopen("/proc/cpuinfo", "rb");
+
+   if (f != NULL)
+   {
+      /* This is a simple state machine which reads the input byte-by-byte until
+       * it gets a match on the 'neon' feature or reaches the end of the stream.
+       */
+      static const char ch_feature[] = { 70, 69, 65, 84, 85, 82, 69, 83 };
+      static const char ch_neon[] = { 78, 69, 79, 78 };
+
+      enum
+      {
+         StartLine, Feature, Colon, StartTag, Neon, HaveNeon, SkipTag, SkipLine
+      }  state;
+      int counter;
+
+      for (state=StartLine, counter=0;;)
+      {
+         int ch = fgetc(f);
+
+         if (ch == EOF)
+         {
+            /* EOF means error or end-of-file, return false; neon at EOF is
+             * assumed to be a mistake.
+             */
+            fclose(f);
+            return 0;
+         }
+
+         switch (state)
+         {
+            case StartLine:
+               /* Match spaces at the start of line */
+               if (ch <= 32) /* skip control characters and space */
+                  break;
+
+               counter=0;
+               state = Feature;
+               /* FALL THROUGH */
+
+            case Feature:
+               /* Match 'FEATURE', ASCII case insensitive. */
+               if ((ch & ~0x20) == ch_feature[counter])
+               {
+                  if (++counter == (sizeof ch_feature))
+                     state = Colon;
+                  break;
+               }
+
+               /* did not match 'feature' */
+               state = SkipLine;
+               /* FALL THROUGH */
+
+            case SkipLine:
+            skipLine:
+               /* Skip everything until we see linefeed or carriage return */
+               if (ch != 10 && ch != 13)
+                  break;
+
+               state = StartLine;
+               break;
+
+            case Colon:
+               /* Match any number of space or tab followed by ':' */
+               if (ch == 32 || ch == 9)
+                  break;
+
+               if (ch == 58) /* i.e. ':' */
+               {
+                  state = StartTag;
+                  break;
+               }
+
+               /* Either a bad line format or a 'feature' prefix followed by
+                * other characters.
+                */
+               state = SkipLine;
+               goto skipLine;
+
+            case StartTag:
+               /* Skip space characters before a tag */
+               if (ch == 32 || ch == 9)
+                  break;
+
+               state = Neon;
+               counter = 0;
+               /* FALL THROUGH */
+
+            case Neon:
+               /* Look for 'neon' tag */
+               if ((ch & ~0x20) == ch_neon[counter])
+               {
+                  if (++counter == (sizeof ch_neon))
+                     state = HaveNeon;
+                  break;
+               }
+
+               state = SkipTag;
+               /* FALL THROUGH */
+
+            case SkipTag:
+               /* Skip non-space characters */
+               if (ch == 10 || ch == 13)
+                  state = StartLine;
+
+               else if (ch == 32 || ch == 9)
+                  state = StartTag;
+               break;
+
+            case HaveNeon:
+               /* Have seen a 'neon' prefix, but there must be a space or new
+                * line character to terminate it.
+                */
+               if (ch == 10 || ch == 13 || ch == 32 || ch == 9)
+               {
+                  fclose(f);
+                  return 1;
+               }
+
+               state = SkipTag;
+               break;
+
+            default:
+               png_error(png_ptr, "png_have_neon: internal error (bug)");
+         }
+      }
+   }
+
+#ifdef PNG_WARNINGS_SUPPORTED
+   else
+      png_warning(png_ptr, "/proc/cpuinfo open failed");
+#endif
+
+   return 0;
+}
diff --git a/contrib/conftest/README b/contrib/conftest/README
new file mode 100644
index 0000000..0f47279
--- /dev/null
+++ b/contrib/conftest/README
@@ -0,0 +1,49 @@
+This directory contains test configuration files, currently always '.dfa' files
+intended to be used in the build by setting the make macro DFA_XTRA to the name
+of the file.
+
+These files are used in release validation of the 'configure' builds of libpng
+by building 'make check', or 'make all-am' for cross-builds, with each .dfa
+file.
+
+The files in this directory may change between minor releases, however
+contributions describing specific builds of libpng are welcomed.  There is no
+guarantee that libpng will continue to build with such configurations; support
+for given configurations can be, and has been, dropped between successive minor
+releases.  However if a .dfa file describing a configuration is not in this
+directory it is very unlikely that it will be tested before a minor release!
+
+You can use these .dfa files as the basis of new configurations.  Files in this
+directory should not have any use restrictions or restrictive licenses.
+
+This directory is not included in the .zip and .7z distributions, which do
+not contain 'configure' scripts.
+
+DOCUMENTATION
+=============
+
+Examples:
+   ${srcdir}/pngusr.dfa
+   ${srcdir}/contrib/pngminim/*/pngusr.dfa
+
+Documentation of the options:
+   ${srcdir}/scripts/pnglibconf.dfa
+
+Documentation of the file format:
+   ${srcdir}/scripts/options.awk
+
+FILE NAMING
+===========
+
+File names in this directory may NOT contain any of the five characters:
+
+   - , + * ?
+
+Neither may they contain any space character.
+
+While other characters may be used it is strongly suggested that file names be
+limited to lower case Latiin alphabetic characters (a-z), digits (0-9) and, if
+necessary the underscore (_) character.  File names should be about 8 characters
+long (excluding the .dfa extension).  Submitted .dfa files should have names
+between 7 and 16 characters long, shorter names (6 characters or less) are
+reserved for standard tests.
diff --git a/contrib/conftest/read.dfa b/contrib/conftest/read.dfa
new file mode 100644
index 0000000..21e88d0
--- /dev/null
+++ b/contrib/conftest/read.dfa
@@ -0,0 +1,58 @@
+# read.dfa
+#  Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+#  To the extent possible under law, the author has waived all copyright and
+#  related or neighboring rights to this work.  This work is published from:
+#  United States.
+#
+# Build libpng with basic read support.  This enables the lowest level libpng
+# read API - the one where the calling code has to use a loop to read each row.
+# At present this is the API used by most programs.
+#
+# Support is enabled only for those chunks and transformations that are
+# typically required - others can be added easily.
+#
+
+everything = off
+
+# The sequential read code is enabled here; the progressive code can be used
+# instead but there is no point enabling both.
+
+option SEQUENTIAL_READ on
+
+# Likewise it is pointless enabling both fixed and floating point APIs.  Choose
+# one or the other for both the API and the internal math.
+
+#Fixed point:
+#option FIXED_POINT on
+#option FLOATING_ARITHMETIC off
+
+#Floating point:
+option FLOATING_POINT on
+option FLOATING_ARITHMETIC on
+
+# Basic error handling, IO and user memory support.  The latter allows the
+# application program to provide its own implementations of 'malloc' and 'free'.
+option SETJMP on
+option STDIO on
+option USER_MEM on
+
+# To read the full set of PNG images correctly interlace, transparency and
+# 16-bit support is required.  The application can implement interlace itself,
+# but very few do and it's no longer possible to disable it when READ is
+# enabled.
+option READ_tRNS on
+option READ_16BIT on
+
+# Everything else is application dependent.  This file assumes the app handles
+# all the native PNG bit layouts, so it doesn't need any of layout change
+# transforms, but needs libpng to perform gamma correction.  It doesn't do any
+# colorspace stuff and ignores the 'significant bit' information.
+#
+# If your app always expands the image to a limited set of bit layouts you
+# probably want to consider using the simplified API instead of the low level
+# one - see png.h and s_read.dfa.
+option READ_GAMMA on
diff --git a/contrib/conftest/s_read.dfa b/contrib/conftest/s_read.dfa
new file mode 100644
index 0000000..cb1ce0b
--- /dev/null
+++ b/contrib/conftest/s_read.dfa
@@ -0,0 +1,35 @@
+# s_read.dfa
+#  Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+#  To the extent possible under law, the author has waived all copyright and
+#  related or neighboring rights to this work.  This work is published from:
+#  United States.
+#
+# Build libpng with simplified read support (only).  This builds a minimal
+# libpng able to read all PNG formats and convert them into a small number of
+# well understood memory formats.
+#
+
+everything = off
+
+option SIMPLIFIED_READ on
+
+# It isn't necessary to chose fixed or floating point for the APIs because the
+# simplified API doesn't need fixed or floating point numbers.  It is necessary
+# to chose an internal math implementation.  The default (because of 'everything
+# = off') is fixed point - turn the floating point implementation on if you have
+# hardware floating point or prefer your software floating point implementation.
+option FLOATING_ARITHMETIC on
+
+# This is not strictly necessary, but without it the message strings in the API
+# will not be filled in
+option ERROR_TEXT on
+
+# Switching these options on enables the 'AFIRST' and 'BGR' formats - you don't
+# need this if you don't use them, they just allow the in-memory layout to be
+# changed to match common hardware formats.
+option SIMPLIFIED_READ_AFIRST on
+option SIMPLIFIED_READ_BGR on
diff --git a/contrib/conftest/s_write.dfa b/contrib/conftest/s_write.dfa
new file mode 100644
index 0000000..e540a46
--- /dev/null
+++ b/contrib/conftest/s_write.dfa
@@ -0,0 +1,33 @@
+# s_write.dfa
+#  Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+#  To the extent possible under law, the author has waived all copyright and
+#  related or neighboring rights to this work.  This work is published from:
+#  United States.
+#
+# Build libpng with (just) simplified write support
+#
+
+everything = off
+
+option SIMPLIFIED_WRITE on
+
+# It isn't necessary to chose fixed or floating point for the APIs because the
+# simplified API doesn't need fixed or floating point numbers.  It is necessary
+# to chose an internal math implementation.  The default (because of 'everything
+# = off') is fixed point - turn the floating point implementation on if you have
+# hardware floating point or prefer your software floating point implementation.
+option FLOATING_ARITHMETIC on
+
+# This is not strictly necessary, but without it the message strings in the API
+# will not be filled in
+option ERROR_TEXT on
+
+# Switching these options on enables the 'AFIRST' and 'BGR' formats - you don't
+# need this if you don't use them, they just allow the in-memory layout to be
+# changed to match common hardware formats.
+option SIMPLIFIED_WRITE_AFIRST on
+option SIMPLIFIED_WRITE_BGR on
diff --git a/contrib/conftest/simple.dfa b/contrib/conftest/simple.dfa
new file mode 100644
index 0000000..0419333
--- /dev/null
+++ b/contrib/conftest/simple.dfa
@@ -0,0 +1,36 @@
+# simple.dfa
+#  Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+#  To the extent possible under law, the author has waived all copyright and
+#  related or neighboring rights to this work.  This work is published from:
+#  United States.
+#
+# Build libpng with just the simplified APIs (read and write).
+#
+
+everything = off
+
+option SIMPLIFIED_WRITE on
+option SIMPLIFIED_READ on
+
+# It isn't necessary to chose fixed or floating point for the APIs because the
+# simplified API doesn't need fixed or floating point numbers.  It is necessary
+# to chose an internal math implementation.  The default (because of 'everything
+# = off') is fixed point - turn the floating point implementation on if you have
+# hardware floating point or prefer your software floating point implementation.
+option FLOATING_ARITHMETIC on
+
+# This is not strictly necessary, but without it the message strings in the API
+# will not be filled in
+option ERROR_TEXT on
+
+# Switching these options on enables the 'AFIRST' and 'BGR' formats - you don't
+# need this if you don't use them, they just allow the in-memory layout to be
+# changed to match common hardware formats.
+option SIMPLIFIED_READ_AFIRST on
+option SIMPLIFIED_READ_BGR on
+option SIMPLIFIED_WRITE_AFIRST on
+option SIMPLIFIED_WRITE_BGR on
diff --git a/contrib/conftest/write.dfa b/contrib/conftest/write.dfa
new file mode 100644
index 0000000..3319aab
--- /dev/null
+++ b/contrib/conftest/write.dfa
@@ -0,0 +1,45 @@
+# write.dfa
+#  Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+#  To the extent possible under law, the author has waived all copyright and
+#  related or neighboring rights to this work.  This work is published from:
+#  United States.
+#
+# Build libpng with no read support and minimal write support.
+#
+
+everything = off
+
+# Switch on the write code - this makes a minimalist encoder
+
+option WRITE on
+
+# Choose fixed or floating point APIs and arithmetic.  The choices are
+# independent but normally they will match.  It is typically better to use the
+# floating point if you have floating point hardware.  If you don't know, or
+# (perhaps) to make libpng smaller used fixed point throughout.
+
+#Fixed point:
+#option FIXED_POINT on
+#option FLOATING_ARITHMETIC off
+
+#Floating point:
+option FLOATING_POINT on
+option FLOATING_ARITHMETIC on
+
+# Basic error handling, IO and user memory support.  The latter allows the
+# application program to provide its own implementations of 'malloc' and 'free'.
+option SETJMP on
+option STDIO on
+option USER_MEM on
+
+# Everything else is optional.  Unlike the read code in libpng the write code
+# does not need to deal with arbitrary formats, so only add support for things
+# you really do write!  For example you might only write sRGB images, sometimes
+# with transparency and never write 16 bit images, so:
+option WRITE_sRGB on
+option WRITE_tRNS on
+#option WRITE_16BIT off (this is the default with 'everything = off')
diff --git a/contrib/examples/README.txt b/contrib/examples/README.txt
new file mode 100644
index 0000000..0525c9d
--- /dev/null
+++ b/contrib/examples/README.txt
@@ -0,0 +1,24 @@
+
+This directory (contrib/examples) contains examples of libpng usage.
+
+NO COPYRIGHT RIGHTS ARE CLAIMED TO ANY OF THE FILES IN THIS DIRECTORY.
+
+To the extent possible under law, the authors have waived all copyright and
+related or neighboring rights to this work.  This work is published from:
+United States.
+
+The files may be used freely in any way.  The intention is that appropriate
+parts of the files be used in other libpng-using programs without any need for
+the authors of the using code to seek copyright or license from the original
+authors.
+
+The source code and comments in this directory are the original work of the
+people named below.  No other person or organization has made contributions to
+the work in this directory.
+
+ORIGINAL AUTHORS
+    The following people have contributed to the code in this directory.  None
+    of the people below claim any rights with regard to the contents of this
+    directory.
+
+    John Bowler <jbowler@acm.org>
diff --git a/contrib/examples/iccfrompng.c b/contrib/examples/iccfrompng.c
new file mode 100644
index 0000000..603037e
--- /dev/null
+++ b/contrib/examples/iccfrompng.c
@@ -0,0 +1,185 @@
+/*- iccfrompng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Extract any icc profiles found in the given PNG files.  This is a simple
+ * example of a program that extracts information from the header of a PNG file
+ * without processing the image.  Notice that some header information may occur
+ * after the image data. Textual data and comments are an example; the approach
+ * in this file won't work reliably for such data because it only looks for the
+ * information in the section of the file that preceeds the image data.
+ *
+ * Compile and link against libpng and zlib, plus anything else required on the
+ * system you use.
+ *
+ * To use supply a list of PNG files containing iCCP chunks, the chunks will be
+ * extracted to a similarly named file with the extension replaced by 'icc',
+ * which will be overwritten without warning.
+ */
+#include <stdlib.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <png.h>
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) && \
+    defined (PNG_iCCP_SUPPORTED)
+
+
+static int verbose = 1;
+static png_byte no_profile[] = "no profile";
+
+static png_bytep
+extract(FILE *fp, png_uint_32 *proflen)
+{
+   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+   png_infop info_ptr = NULL;
+   png_bytep result = NULL;
+
+   /* Initialize for error or no profile: */
+   *proflen = 0;
+
+   if (png_ptr == NULL)
+   {
+      fprintf(stderr, "iccfrompng: version library mismatch?\n");
+      return 0;
+   }
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      return 0;
+   }
+
+   png_init_io(png_ptr, fp);
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+      png_error(png_ptr, "OOM allocating info structure");
+
+   png_read_info(png_ptr, info_ptr);
+
+   {
+      png_charp name;
+      int compression_type;
+      png_bytep profile;
+
+      if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile,
+         proflen) & PNG_INFO_iCCP)
+      {
+         result = malloc(*proflen);
+         if (result != NULL)
+            memcpy(result, profile, *proflen);
+
+         else
+            png_error(png_ptr, "OOM allocating profile buffer");
+      }
+
+      else
+	result = no_profile;
+   }
+
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+   return result;
+}
+
+static int
+extract_one_file(const char *filename)
+{
+   int result = 0;
+   FILE *fp = fopen(filename, "rb");
+
+   if (fp != NULL)
+   {
+      png_uint_32 proflen = 0;
+      png_bytep profile = extract(fp, &proflen);
+
+      if (profile != NULL && profile != no_profile)
+      {
+         size_t len;
+         char *output;
+
+         {
+            const char *ep = strrchr(filename, '.');
+
+            if (ep != NULL)
+               len = ep-filename;
+
+            else
+               len = strlen(filename);
+         }
+
+         output = malloc(len + 5);
+         if (output != NULL)
+         {
+            FILE *of;
+
+            memcpy(output, filename, len);
+            strcpy(output+len, ".icc");
+
+            of = fopen(output, "wb");
+            if (of != NULL)
+            {
+               if (fwrite(profile, proflen, 1, of) == 1 &&
+                  fflush(of) == 0 &&
+                  fclose(of) == 0)
+               {
+                  if (verbose)
+                     printf("%s -> %s\n", filename, output);
+                  /* Success return */
+                  result = 1;
+               }
+
+               else
+               {
+                  fprintf(stderr, "%s: error writing profile\n", output);
+                  if (remove(output))
+                     fprintf(stderr, "%s: could not remove file\n", output);
+               }
+            }
+
+            else
+               fprintf(stderr, "%s: failed to open output file\n", output);
+
+            free(output);
+         }
+
+         else
+            fprintf(stderr, "%s: OOM allocating string!\n", filename);
+
+         free(profile);
+      }
+
+      else if (verbose && profile == no_profile)
+	printf("%s has no profile\n", filename);
+   }
+
+   else
+      fprintf(stderr, "%s: could not open file\n", filename);
+
+   return result;
+}
+
+int
+main(int argc, char **argv)
+{
+   int i;
+   int extracted = 0;
+
+   for (i=1; i<argc; ++i)
+   {
+      if (strcmp(argv[i], "-q") == 0)
+         verbose = 0;
+
+      else if (extract_one_file(argv[i]))
+         extracted = 1;
+   }
+
+   /* Exit code is true if any extract succeeds */
+   return extracted == 0;
+}
+#endif /* READ && STDIO && iCCP */
diff --git a/contrib/examples/pngpixel.c b/contrib/examples/pngpixel.c
new file mode 100644
index 0000000..f762379
--- /dev/null
+++ b/contrib/examples/pngpixel.c
@@ -0,0 +1,371 @@
+/*- pngpixel
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Read a single pixel value from a PNG file.
+ *
+ * This code illustrates basic 'by-row' reading of a PNG file using libpng.
+ * Rows are read until a particular pixel is found; the value of this pixel is
+ * then printed on stdout.
+ *
+ * The code illustrates how to do this on interlaced as well as non-interlaced
+ * images.  Normally you would call png_set_interlace_handling() to have libpng
+ * deal with the interlace for you, but that obliges you to buffer half of the
+ * image to assemble the interlaced rows.  In this code
+ * png_set_interlace_handling() is not called and, instead, the code handles the
+ * interlace passes directly looking for the required pixel.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h> /* required for error handling */
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)
+
+/* Return component 'c' of pixel 'x' from the given row. */
+static unsigned int
+component(png_const_bytep row, png_uint_32 x, unsigned int c,
+   unsigned int bit_depth, unsigned int channels)
+{
+   /* PNG images can be up to 2^31 pixels wide, but this means they can be up to
+    * 2^37 bits wide (for a 64-bit pixel - the largest possible) and hence 2^34
+    * bytes wide.  Since the row fitted into memory, however, the following must
+    * work:
+    */
+   png_uint_32 bit_offset_hi = bit_depth * ((x >> 6) * channels);
+   png_uint_32 bit_offset_lo = bit_depth * ((x & 0x3f) * channels + c);
+
+   row = (png_const_bytep)(((PNG_CONST png_byte (*)[8])row) + bit_offset_hi);
+   row += bit_offset_lo >> 3;
+   bit_offset_lo &= 0x07;
+
+   /* PNG pixels are packed into bytes to put the first pixel in the highest
+    * bits of the byte and into two bytes for 16-bit values with the high 8 bits
+    * first, so:
+    */
+   switch (bit_depth)
+   {
+      case 1: return (row[0] >> (7-bit_offset_lo)) & 0x01;
+      case 2: return (row[0] >> (6-bit_offset_lo)) & 0x03;
+      case 4: return (row[0] >> (4-bit_offset_lo)) & 0x0f;
+      case 8: return row[0];
+      case 16: return (row[0] << 8) + row[1];
+      default:
+         /* This should never happen; it indicates a bug in this program or in
+          * libpng itself:
+          */
+         fprintf(stderr, "pngpixel: invalid bit depth %u\n", bit_depth);
+         exit(1);
+   }
+}
+
+/* Print a pixel from a row returned by libpng; determine the row format, find
+ * the pixel, and print the relevant information to stdout.
+ */
+static void
+print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row,
+   png_uint_32 x)
+{
+   PNG_CONST unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+
+   switch (png_get_color_type(png_ptr, info_ptr))
+   {
+      case PNG_COLOR_TYPE_GRAY:
+         printf("GRAY %u\n", component(row, x, 0, bit_depth, 1));
+         return;
+
+      /* The palette case is slightly more difficult - the palette and, if
+       * present, the tRNS ('transparency', though the values are really
+       * opacity) data must be read to give the full picture:
+       */
+      case PNG_COLOR_TYPE_PALETTE:
+         {
+            PNG_CONST int index = component(row, x, 0, bit_depth, 1);
+            png_colorp palette = NULL;
+            int num_palette = 0;
+
+            if ((png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) &
+               PNG_INFO_PLTE) && num_palette > 0 && palette != NULL)
+            {
+               png_bytep trans_alpha = NULL;
+               int num_trans = 0;
+               if ((png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans,
+                  NULL) & PNG_INFO_tRNS) && num_trans > 0 &&
+                  trans_alpha != NULL)
+                  printf("INDEXED %u = %d %d %d %d\n", index,
+                     palette[index].red, palette[index].green,
+                     palette[index].blue,
+                     index < num_trans ? trans_alpha[index] : 255);
+
+               else /* no transparency */
+                  printf("INDEXED %u = %d %d %d\n", index,
+                     palette[index].red, palette[index].green,
+                     palette[index].blue);
+            }
+
+            else
+               printf("INDEXED %u = invalid index\n", index);
+         }
+         return;
+
+      case PNG_COLOR_TYPE_RGB:
+         printf("RGB %u %u %u\n", component(row, x, 0, bit_depth, 3),
+            component(row, x, 1, bit_depth, 3),
+            component(row, x, 2, bit_depth, 3));
+         return;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         printf("GRAY+ALPHA %u %u\n", component(row, x, 0, bit_depth, 2),
+            component(row, x, 1, bit_depth, 2));
+         return;
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         printf("RGBA %u %u %u %u\n", component(row, x, 0, bit_depth, 4),
+            component(row, x, 1, bit_depth, 4),
+            component(row, x, 2, bit_depth, 4),
+            component(row, x, 3, bit_depth, 4));
+         return;
+
+      default:
+         png_error(png_ptr, "pngpixel: invalid color type");
+   }
+}
+
+int main(int argc, const char **argv)
+{
+   /* This program uses the default, <setjmp.h> based, libpng error handling
+    * mechanism, therefore any local variable that exists before the call to
+    * setjmp and is changed after the call to setjmp returns successfully must
+    * be declared with 'volatile' to ensure that their values don't get
+    * destroyed by longjmp:
+    */
+   volatile int result = 1/*fail*/;
+
+   if (argc == 4)
+   {
+      long x = atol(argv[1]);
+      long y = atol(argv[2]);
+      FILE *f = fopen(argv[3], "rb");
+      volatile png_bytep row = NULL;
+
+      if (f != NULL)
+      {
+         /* libpng requires a callback function for handling errors; this
+          * callback must not return.  The default callback function uses a
+          * stored <setjmp.h> style jmp_buf which is held in a png_struct and
+          * writes error messages to stderr.  Creating the png_struct is a
+          * little tricky; just copy the following code.
+          */
+         png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+            NULL, NULL, NULL);
+
+         if (png_ptr != NULL)
+         {
+            png_infop info_ptr = png_create_info_struct(png_ptr);
+
+            if (info_ptr != NULL)
+            {
+               /* Declare stack variables to hold pointers to locally allocated
+                * data.
+                */
+
+               /* Initialize the error control buffer: */
+               if (setjmp(png_jmpbuf(png_ptr)) == 0)
+               {
+                  png_uint_32 width, height;
+                  int bit_depth, color_type, interlace_method,
+                     compression_method, filter_method;
+                  png_bytep row_tmp;
+
+                  /* Now associate the recently opened (FILE*) with the default
+                   * libpng initialization functions.  Sometimes libpng is
+                   * compiled without stdio support (it can be difficult to do
+                   * in some environments); in that case you will have to write
+                   * your own read callback to read data from the (FILE*).
+                   */
+                  png_init_io(png_ptr, f);
+
+                  /* And read the first part of the PNG file - the header and
+                   * all the information up to the first pixel.
+                   */
+                  png_read_info(png_ptr, info_ptr);
+
+                  /* This fills in enough information to tell us the width of
+                   * each row in bytes, allocate the appropriate amount of
+                   * space.  In this case png_malloc is used - it will not
+                   * return if memory isn't available.
+                   */
+                  row = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
+                     info_ptr));
+
+                  /* To avoid the overhead of using a volatile auto copy row_tmp
+                   * to a local here - just use row for the png_free below.
+                   */
+                  row_tmp = row;
+
+                  /* All the information we need is in the header is returned by
+                   * png_get_IHDR, if this fails we can now use 'png_error' to
+                   * signal the error and return control to the setjmp above.
+                   */
+                  if (png_get_IHDR(png_ptr, info_ptr, &width, &height,
+                     &bit_depth, &color_type, &interlace_method,
+                     &compression_method, &filter_method))
+                  {
+                     int passes, pass;
+
+                     /* png_set_interlace_handling returns the number of
+                      * passes required as well as turning on libpng's
+                      * handling, but since we do it ourselves this is
+                      * necessary:
+                      */
+                     switch (interlace_method)
+                     {
+                        case PNG_INTERLACE_NONE:
+                           passes = 1;
+                           break;
+
+                        case PNG_INTERLACE_ADAM7:
+                           passes = PNG_INTERLACE_ADAM7_PASSES;
+                           break;
+
+                        default:
+                           png_error(png_ptr, "pngpixel: unknown interlace");
+                     }
+
+                     /* Now read the pixels, pass-by-pass, row-by-row: */
+                     png_start_read_image(png_ptr);
+
+                     for (pass=0; pass<passes; ++pass)
+                     {
+                        png_uint_32 ystart, xstart, ystep, xstep;
+                        png_uint_32 py;
+
+                        if (interlace_method == PNG_INTERLACE_ADAM7)
+                        {
+                           /* Sometimes the whole pass is empty because the
+                            * image is too narrow or too short.  libpng
+                            * expects to be called for each row that is
+                            * present in the pass, so it may be necessary to
+                            * skip the loop below (over py) if the image is
+                            * too narrow.
+                            */
+                           if (PNG_PASS_COLS(width, pass) == 0)
+                              continue;
+
+                           /* We need the starting pixel and the offset
+                            * between each pixel in this pass; use the macros
+                            * in png.h:
+                            */
+                           xstart = PNG_PASS_START_COL(pass);
+                           ystart = PNG_PASS_START_ROW(pass);
+                           xstep = PNG_PASS_COL_OFFSET(pass);
+                           ystep = PNG_PASS_ROW_OFFSET(pass);
+                        }
+
+                        else
+                        {
+                           ystart = xstart = 0;
+                           ystep = xstep = 1;
+                        }
+
+                        /* To find the pixel, loop over 'py' for each pass
+                         * reading a row and then checking to see if it
+                         * contains the pixel.
+                         */
+                        for (py = ystart; py < height; py += ystep)
+                        {
+                           png_uint_32 px, ppx;
+
+                           /* png_read_row takes two pointers.  When libpng
+                            * handles the interlace the first is filled in
+                            * pixel-by-pixel, and the second receives the same
+                            * pixels but they are replicated across the
+                            * unwritten pixels so far for each pass.  When we
+                            * do the interlace, however, they just contain
+                            * the pixels from the interlace pass - giving
+                            * both is wasteful and pointless, so we pass a
+                            * NULL pointer.
+                            */
+                           png_read_row(png_ptr, row_tmp, NULL);
+
+                           /* Now find the pixel if it is in this row; there
+                            * are, of course, much better ways of doing this
+                            * than using a for loop:
+                            */
+                           if (y == py) for (px = xstart, ppx = 0;
+                              px < width; px += xstep, ++ppx) if (x == px)
+                           {
+                              /* 'ppx' is the index of the pixel in the row
+                               * buffer.
+                               */
+                              print_pixel(png_ptr, info_ptr, row_tmp, ppx);
+
+                              /* Now terminate the loops early - we have
+                               * found and handled the required data.
+                               */
+                              goto pass_loop_end;
+                           } /* x loop */
+                        } /* y loop */
+                     } /* pass loop */
+
+                     /* Finally free the temporary buffer: */
+                  pass_loop_end:
+                     row = NULL;
+                     png_free(png_ptr, row_tmp);
+                  }
+
+                  else
+                     png_error(png_ptr, "pngpixel: png_get_IHDR failed");
+
+               }
+
+               else
+               {
+                  /* Else libpng has raised an error.  An error message has
+                   * already been output, so it is only necessary to clean up
+                   * locally allocated data:
+                   */
+                  if (row != NULL)
+                  {
+                     /* The default implementation of png_free never errors out
+                      * (it just crashes if something goes wrong), but the safe
+                      * way of using it is still to clear 'row' before calling
+                      * png_free:
+                      */
+                     png_bytep row_tmp = row;
+                     row = NULL;
+                     png_free(png_ptr, row_tmp);
+                  }
+               }
+
+               png_destroy_info_struct(png_ptr, &info_ptr);
+            }
+
+            else
+               fprintf(stderr, "pngpixel: out of memory allocating png_info\n");
+
+            png_destroy_read_struct(&png_ptr, NULL, NULL);
+         }
+
+         else
+            fprintf(stderr, "pngpixel: out of memory allocating png_struct\n");
+      }
+
+      else
+         fprintf(stderr, "pngpixel: %s: could not open file\n", argv[3]);
+   }
+
+   else
+      /* Wrong number of arguments */
+      fprintf(stderr, "pngpixel: usage: pngpixel x y png-file\n");
+
+   return result;
+}
+#endif /* READ && SEQUENTIAL_READ */
diff --git a/contrib/examples/pngtopng.c b/contrib/examples/pngtopng.c
new file mode 100644
index 0000000..4acf6b3
--- /dev/null
+++ b/contrib/examples/pngtopng.c
@@ -0,0 +1,95 @@
+/*- pngtopng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Read a PNG and write it out in a fixed format, using the 'simplified API'
+ * that was introduced in libpng-1.6.0.
+ *
+ * This sample code is just the code from the top of 'example.c' with some error
+ * handling added.  See example.c for more comments.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && \
+    defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+
+int main(int argc, const char **argv)
+{
+   int result = 1;
+
+   if (argc == 3)
+   {
+      png_image image;
+
+      /* Only the image structure version number needs to be set. */
+      memset(&image, 0, sizeof image);
+      image.version = PNG_IMAGE_VERSION;
+
+      if (png_image_begin_read_from_file(&image, argv[1]))
+      {
+         png_bytep buffer;
+
+         /* Change this to try different formats!  If you set a colormap format
+          * then you must also supply a colormap below.
+          */
+         image.format = PNG_FORMAT_RGBA;
+
+         buffer = malloc(PNG_IMAGE_SIZE(image));
+
+         if (buffer != NULL)
+         {
+            if (png_image_finish_read(&image, NULL/*background*/, buffer,
+               0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
+            {
+               if (png_image_write_to_file(&image, argv[2],
+                  0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+                  NULL/*colormap*/))
+                  result = 0;
+
+               else
+                  fprintf(stderr, "pngtopng: write %s: %s\n", argv[2],
+                      image.message);
+
+               free(buffer);
+            }
+
+            else
+            {
+               fprintf(stderr, "pngtopng: read %s: %s\n", argv[1],
+                   image.message);
+
+               /* This is the only place where a 'free' is required; libpng does
+                * the cleanup on error and success, but in this case we couldn't
+                * complete the read because of running out of memory.
+                */
+               png_image_free(&image);
+            }
+         }
+
+         else
+            fprintf(stderr, "pngtopng: out of memory: %lu bytes\n",
+               (unsigned long)PNG_IMAGE_SIZE(image));
+      }
+
+      else
+         /* Failed to read the first argument: */
+         fprintf(stderr, "pngtopng: %s: %s\n", argv[1], image.message);
+   }
+
+   else
+      /* Wrong number of arguments */
+      fprintf(stderr, "pngtopng: usage: pngtopng input-file output-file\n");
+
+   return result;
+}
+#endif /* READ && WRITE */
diff --git a/contrib/examples/simpleover.c b/contrib/examples/simpleover.c
new file mode 100644
index 0000000..59dd313
--- /dev/null
+++ b/contrib/examples/simpleover.c
@@ -0,0 +1,648 @@
+/*- simpleover
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2015.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Read several PNG files, which should have an alpha channel or transparency
+ * information, and composite them together to produce one or more 16-bit linear
+ * RGBA intermediates.  This involves doing the correct 'over' composition to
+ * combine the alpha channels and corresponding data.
+ *
+ * Finally read an output (background) PNG using the 24-bit RGB format (the
+ * PNG will be composited on green (#00ff00) by default if it has an alpha
+ * channel), and apply the intermediate image generated above to specified
+ * locations in the image.
+ *
+ * The command line has the general format:
+ *
+ *    simpleover <background.png> [output.png]
+ *        {--sprite=width,height,name {[--at=x,y] {sprite.png}}}
+ *        {--add=name {x,y}}
+ *
+ * The --sprite and --add options may occur multiple times. They are executed
+ * in order.  --add may refer to any sprite already read.
+ *
+ * This code is intended to show how to composite multiple images together
+ * correctly.  Apart from the libpng Simplified API the only work done in here
+ * is to combine multiple input PNG images into a single sprite; this involves
+ * a Porter-Duff 'over' operation and the input PNG images may, as a result,
+ * be regarded as being layered one on top of the other with the first (leftmost
+ * on the command line) being at the bottom and the last on the top.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation, so long as this
+ * file is linked against a sufficiently recent libpng (1.6+) it is ok to
+ * change this to <png.h>:
+ */
+#include "../../png.h"
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+
+#define sprite_name_chars 15
+struct sprite {
+   FILE         *file;
+   png_uint_16p  buffer;
+   unsigned int  width;
+   unsigned int  height;
+   char          name[sprite_name_chars+1];
+};
+
+#if 0 /* div by 65535 test program */
+#include <math.h>
+#include <stdio.h>
+
+int main(void) {
+   double err = 0;
+   unsigned int xerr = 0;
+   unsigned int r = 32769;
+   {
+      unsigned int x = 0;
+
+      do {
+         unsigned int t = x + (x >> 16) /*+ (x >> 31)*/ + r;
+         double v = x, errtest;
+
+         if (t < x) {
+            fprintf(stderr, "overflow: %u+%u -> %u\n", x, r, t);
+            return 1;
+         }
+
+         v /= 65535;
+         errtest = v;
+         t >>= 16;
+         errtest -= t;
+
+         if (errtest > err) {
+            err = errtest;
+            xerr = x;
+
+            if (errtest >= .5) {
+               fprintf(stderr, "error: %u/65535 = %f, not %u, error %f\n",
+                     x, v, t, errtest);
+               return 0;
+            }
+         }
+      } while (++x <= 65535U*65535U);
+   }
+
+   printf("error %f @ %u\n", err, xerr);
+
+   return 0;
+}
+#endif /* div by 65535 test program */
+
+static void
+sprite_op(const struct sprite *sprite, int x_offset, int y_offset,
+   png_imagep image, const png_uint_16 *buffer)
+{
+   /* This is where the Porter-Duff 'Over' operator is evaluated; change this
+    * code to change the operator (this could be parameterized).  Any other
+    * image processing operation could be used here.
+    */
+
+
+   /* Check for an x or y offset that pushes any part of the image beyond the
+    * right or bottom of the sprite:
+    */
+   if ((y_offset < 0 || (unsigned)/*SAFE*/y_offset < sprite->height) &&
+       (x_offset < 0 || (unsigned)/*SAFE*/x_offset < sprite->width))
+   {
+      unsigned int y = 0;
+
+      if (y_offset < 0)
+         y = -y_offset; /* Skip to first visible row */
+
+      do
+      {
+         unsigned int x = 0;
+
+         if (x_offset < 0)
+            x = -x_offset;
+
+         do
+         {
+            /* In and out are RGBA values, so: */
+            const png_uint_16 *in_pixel = buffer + (y * image->width + x)*4;
+            png_uint_32 in_alpha = in_pixel[3];
+
+            /* This is the optimized Porter-Duff 'Over' operation, when the
+             * input alpha is 0 the output is not changed.
+             */
+            if (in_alpha > 0)
+            {
+               png_uint_16 *out_pixel = sprite->buffer +
+                  ((y+y_offset) * sprite->width + (x+x_offset))*4;
+
+               /* This is the weight to apply to the output: */
+               in_alpha = 65535-in_alpha;
+
+               if (in_alpha > 0)
+               {
+                  /* The input must be composed onto the output. This means
+                   * multiplying the current output pixel value by the inverse
+                   * of the input alpha (1-alpha). A division is required but
+                   * it is by the constant 65535.  Approximate this as:
+                   *
+                   *     (x + (x >> 16) + 32769) >> 16;
+                   *
+                   * This is exact (and does not overflow) for all values of
+                   * x in the range 0..65535*65535.  (Note that the calculation
+                   * produces the closest integer; the maximum error is <0.5).
+                   */
+                  png_uint_32 tmp;
+
+#                 define compose(c)\
+                     tmp = out_pixel[c] * in_alpha;\
+                     tmp = (tmp + (tmp >> 16) + 32769) >> 16;\
+                     out_pixel[c] = tmp + in_pixel[c]
+
+                  /* The following is very vectorizable... */
+                  compose(0);
+                  compose(1);
+                  compose(2);
+                  compose(3);
+               }
+
+               else
+                  out_pixel[0] = in_pixel[0],
+                  out_pixel[1] = in_pixel[1],
+                  out_pixel[2] = in_pixel[2],
+                  out_pixel[3] = in_pixel[3];
+            }
+         }
+         while (++x < image->width);
+      }
+      while (++y < image->height);
+   }
+}
+
+static int
+create_sprite(struct sprite *sprite, int *argc, const char ***argv)
+{
+   /* Read the arguments and create this sprite. The sprite buffer has already
+    * been allocated. This reads the input PNGs one by one in linear format,
+    * composes them onto the sprite buffer (the code in the function above)
+    * then saves the result, converting it on the fly to PNG RGBA 8-bit format.
+    */
+   while (*argc > 0)
+   {
+      char tombstone;
+      int x = 0, y = 0;
+
+      if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
+      {
+         /* The only supported option is --at. */
+         if (sscanf((*argv)[0], "--at=%d,%d%c", &x, &y, &tombstone) != 2)
+            break; /* success; caller will parse this option */
+
+         ++*argv, --*argc;
+      }
+
+      else
+      {
+         /* The argument has to be a file name */
+         png_image image;
+
+         image.version = PNG_IMAGE_VERSION;
+         image.opaque = NULL;
+
+         if (png_image_begin_read_from_file(&image, (*argv)[0]))
+         {
+            png_uint_16p buffer;
+
+            image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+
+            buffer = malloc(PNG_IMAGE_SIZE(image));
+
+            if (buffer != NULL)
+            {
+               if (png_image_finish_read(&image, NULL/*background*/, buffer,
+                  0/*row_stride*/,
+                  NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
+               {
+                  /* This is the place where the Porter-Duff 'Over' operator
+                   * needs to be done by this code.  In fact, any image
+                   * processing required can be done here; the data is in
+                   * the correct format (linear, 16-bit) and source and
+                   * destination are in memory.
+                   */
+                  sprite_op(sprite, x, y, &image, buffer);
+                  free(buffer);
+                  ++*argv, --*argc;
+                  /* And continue to the next argument */
+                  continue;
+               }
+
+               else
+               {
+                  free(buffer);
+                  fprintf(stderr, "simpleover: read %s: %s\n", (*argv)[0],
+                      image.message);
+               }
+            }
+
+            else
+            {
+               fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
+                  (unsigned long)PNG_IMAGE_SIZE(image));
+
+               /* png_image_free must be called if we abort the Simplified API
+                * read because of a problem detected in this code.  If problems
+                * are detected in the Simplified API it cleans up itself.
+                */
+               png_image_free(&image);
+            }
+         }
+
+         else
+         {
+            /* Failed to read the first argument: */
+            fprintf(stderr, "simpleover: %s: %s\n", (*argv)[0], image.message);
+         }
+
+         return 0; /* failure */
+      }
+   }
+
+   /* All the sprite operations have completed successfully. Save the RGBA
+    * buffer as a PNG using the simplified write API.
+    */
+   sprite->file = tmpfile();
+
+   if (sprite->file != NULL)
+   {
+      png_image save;
+
+      memset(&save, 0, sizeof save);
+      save.version = PNG_IMAGE_VERSION;
+      save.opaque = NULL;
+      save.width = sprite->width;
+      save.height = sprite->height;
+      save.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+      save.flags = PNG_IMAGE_FLAG_FAST;
+      save.colormap_entries = 0;
+
+      if (png_image_write_to_stdio(&save, sprite->file, 1/*convert_to_8_bit*/,
+          sprite->buffer, 0/*row_stride*/, NULL/*colormap*/))
+      {
+         /* Success; the buffer is no longer needed: */
+         free(sprite->buffer);
+         sprite->buffer = NULL;
+         return 1; /* ok */
+      }
+
+      else
+         fprintf(stderr, "simpleover: write sprite %s: %s\n", sprite->name,
+            save.message);
+   }
+
+   else
+      fprintf(stderr, "simpleover: sprite %s: could not allocate tmpfile: %s\n",
+         sprite->name, strerror(errno));
+
+   return 0; /* fail */
+}
+
+static int
+add_sprite(png_imagep output, png_bytep out_buf, struct sprite *sprite,
+   int *argc, const char ***argv)
+{
+   /* Given a --add argument naming this sprite, perform the operations listed
+    * in the following arguments.  The arguments are expected to have the form
+    * (x,y), which is just an offset at which to add the sprite to the
+    * output.
+    */
+   while (*argc > 0)
+   {
+      char tombstone;
+      int x, y;
+
+      if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
+         return 1; /* success */
+
+      if (sscanf((*argv)[0], "%d,%d%c", &x, &y, &tombstone) == 2)
+      {
+         /* Now add the new image into the sprite data, but only if it
+          * will fit.
+          */
+         if (x < 0 || y < 0 ||
+             (unsigned)/*SAFE*/x >= output->width ||
+             (unsigned)/*SAFE*/y >= output->height ||
+             sprite->width > output->width-x ||
+             sprite->height > output->height-y)
+         {
+            fprintf(stderr, "simpleover: sprite %s @ (%d,%d) outside image\n",
+               sprite->name, x, y);
+            /* Could just skip this, but for the moment it is an error */
+            return 0; /* error */
+         }
+
+         else
+         {
+            /* Since we know the sprite fits we can just read it into the
+             * output using the simplified API.
+             */
+            png_image in;
+
+            in.version = PNG_IMAGE_VERSION;
+            rewind(sprite->file);
+
+            if (png_image_begin_read_from_stdio(&in, sprite->file))
+            {
+               in.format = PNG_FORMAT_RGB; /* force compose */
+
+               if (png_image_finish_read(&in, NULL/*background*/,
+                  out_buf + (y*output->width + x)*3/*RGB*/,
+                  output->width*3/*row_stride*/,
+                  NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
+               {
+                  ++*argv, --*argc;
+                  continue;
+               }
+            }
+
+            /* The read failed: */
+            fprintf(stderr, "simpleover: add sprite %s: %s\n", sprite->name,
+                in.message);
+            return 0; /* error */
+         }
+      }
+
+      else
+      {
+         fprintf(stderr, "simpleover: --add='%s': invalid position %s\n",
+               sprite->name, (*argv)[0]);
+         return 0; /* error */
+      }
+   }
+
+   return 1; /* ok */
+}
+
+static int
+simpleover_process(png_imagep output, png_bytep out_buf, int argc,
+   const char **argv)
+{
+   int result = 1; /* success */
+#  define csprites 10/*limit*/
+#  define str(a) #a
+   int nsprites = 0;
+   struct sprite sprites[csprites];
+
+   while (argc > 0)
+   {
+      result = 0; /* fail */
+
+      if (strncmp(argv[0], "--sprite=", 9) == 0)
+      {
+         char tombstone;
+
+         if (nsprites < csprites)
+         {
+            int n;
+
+            sprites[nsprites].width = sprites[nsprites].height = 0;
+            sprites[nsprites].name[0] = 0;
+
+            n = sscanf(argv[0], "--sprite=%u,%u,%" str(sprite_name_chars) "s%c",
+                &sprites[nsprites].width, &sprites[nsprites].height,
+                sprites[nsprites].name, &tombstone);
+
+            if ((n == 2 || n == 3) &&
+                sprites[nsprites].width > 0 && sprites[nsprites].height > 0)
+            {
+               size_t buf_size, tmp;
+
+               /* Default a name if not given. */
+               if (sprites[nsprites].name[0] == 0)
+                  sprintf(sprites[nsprites].name, "sprite-%d", nsprites+1);
+
+               /* Allocate a buffer for the sprite and calculate the buffer
+                * size:
+                */
+               buf_size = sizeof (png_uint_16 [4]);
+               buf_size *= sprites[nsprites].width;
+               buf_size *= sprites[nsprites].height;
+
+               /* This can overflow a (size_t); check for this: */
+               tmp = buf_size;
+               tmp /= sprites[nsprites].width;
+               tmp /= sprites[nsprites].height;
+
+               if (tmp == sizeof (png_uint_16 [4]))
+               {
+                  sprites[nsprites].buffer = malloc(buf_size);
+                  /* This buffer must be initialized to transparent: */
+                  memset(sprites[nsprites].buffer, 0, buf_size);
+
+                  if (sprites[nsprites].buffer != NULL)
+                  {
+                     sprites[nsprites].file = NULL;
+                     ++argv, --argc;
+
+                     if (create_sprite(sprites+nsprites++, &argc, &argv))
+                     {
+                        result = 1; /* still ok */
+                        continue;
+                     }
+
+                     break; /* error */
+                  }
+               }
+
+               /* Overflow, or OOM */
+               fprintf(stderr, "simpleover: %s: sprite too large\n", argv[0]);
+               break;
+            }
+
+            else
+            {
+               fprintf(stderr, "simpleover: %s: invalid sprite (%u,%u)\n",
+                  argv[0], sprites[nsprites].width, sprites[nsprites].height);
+               break;
+            }
+         }
+
+         else
+         {
+            fprintf(stderr, "simpleover: %s: too many sprites\n", argv[0]);
+            break;
+         }
+      }
+
+      else if (strncmp(argv[0], "--add=", 6) == 0)
+      {
+         const char *name = argv[0]+6;
+         int isprite = nsprites;
+
+         ++argv, --argc;
+
+         while (--isprite >= 0)
+         {
+            if (strcmp(sprites[isprite].name, name) == 0)
+            {
+               if (!add_sprite(output, out_buf, sprites+isprite, &argc, &argv))
+                  goto out; /* error in add_sprite */
+
+               break;
+            }
+         }
+
+         if (isprite < 0) /* sprite not found */
+         {
+            fprintf(stderr, "simpleover: --add='%s': sprite not found\n", name);
+            break;
+         }
+      }
+
+      else
+      {
+         fprintf(stderr, "simpleover: %s: unrecognized operation\n", argv[0]);
+         break;
+      }
+
+      result = 1; /* ok  */
+   }
+
+   /* Clean up the cache of sprites: */
+out:
+   while (--nsprites >= 0)
+   {
+      if (sprites[nsprites].buffer != NULL)
+         free(sprites[nsprites].buffer);
+
+      if (sprites[nsprites].file != NULL)
+         (void)fclose(sprites[nsprites].file);
+   }
+
+   return result;
+}
+
+int main(int argc, const char **argv)
+{
+   int result = 1; /* default to fail */
+
+   if (argc >= 2)
+   {
+      int argi = 2;
+      const char *output = NULL;
+      png_image image;
+
+      if (argc > 2 && argv[2][0] != '-'/*an operation*/)
+      {
+         output = argv[2];
+         argi = 3;
+      }
+
+      image.version = PNG_IMAGE_VERSION;
+      image.opaque = NULL;
+
+      if (png_image_begin_read_from_file(&image, argv[1]))
+      {
+         png_bytep buffer;
+
+         image.format = PNG_FORMAT_RGB; /* 24-bit RGB */
+
+         buffer = malloc(PNG_IMAGE_SIZE(image));
+
+         if (buffer != NULL)
+         {
+            png_color background = {0, 0xff, 0}; /* fully saturated green */
+
+            if (png_image_finish_read(&image, &background, buffer,
+               0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
+            {
+               /* At this point png_image_finish_read has cleaned up the
+                * allocated data in png_image, and only the buffer needs to be
+                * freed.
+                *
+                * Perform the remaining operations:
+                */
+               if (simpleover_process(&image, buffer, argc-argi, argv+argi))
+               {
+                  /* Write the output: */
+                  if ((output != NULL &&
+                       png_image_write_to_file(&image, output,
+                        0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+                        NULL/*colormap*/)) ||
+                      (output == NULL &&
+                       png_image_write_to_stdio(&image, stdout,
+                        0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+                        NULL/*colormap*/)))
+                     result = 0;
+
+                  else
+                     fprintf(stderr, "simpleover: write %s: %s\n",
+                        output == NULL ? "stdout" : output, image.message);
+               }
+
+               /* else simpleover_process writes an error message */
+            }
+
+            else
+               fprintf(stderr, "simpleover: read %s: %s\n", argv[1],
+                   image.message);
+
+            free(buffer);
+         }
+
+         else
+         {
+            fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
+               (unsigned long)PNG_IMAGE_SIZE(image));
+
+            /* This is the only place where a 'free' is required; libpng does
+             * the cleanup on error and success, but in this case we couldn't
+             * complete the read because of running out of memory.
+             */
+            png_image_free(&image);
+         }
+      }
+
+      else
+      {
+         /* Failed to read the first argument: */
+         fprintf(stderr, "simpleover: %s: %s\n", argv[1], image.message);
+      }
+   }
+
+   else
+   {
+      /* Usage message */
+      fprintf(stderr,
+         "simpleover: usage: simpleover background.png [output.png]\n"
+         "  Output 'background.png' as a 24-bit RGB PNG file in 'output.png'\n"
+         "   or, if not given, stdout.  'background.png' will be composited\n"
+         "   on fully saturated green.\n"
+         "\n"
+         "  Optionally, before output, process additional PNG files:\n"
+         "\n"
+         "   --sprite=width,height,name {[--at=x,y] {sprite.png}}\n"
+         "    Produce a transparent sprite of size (width,height) and with\n"
+         "     name 'name'.\n"
+         "    For each sprite.png composite it using a Porter-Duff 'Over'\n"
+         "     operation at offset (x,y) in the sprite (defaulting to (0,0)).\n"
+         "     Input PNGs will be truncated to the area of the sprite.\n"
+         "\n"
+         "   --add='name' {x,y}\n"
+         "    Optionally, before output, composite a sprite, 'name', which\n"
+         "     must have been previously produced using --sprite, at each\n"
+         "     offset (x,y) in the output image.  Each sprite must fit\n"
+         "     completely within the output image.\n"
+         "\n"
+         "  PNG files are processed in the order they occur on the command\n"
+         "  line and thus the first PNG processed appears as the bottommost\n"
+         "  in the output image.\n");
+   }
+
+   return result;
+}
+#endif /* SIMPLIFIED_READ */
diff --git a/contrib/gregbook/COPYING b/contrib/gregbook/COPYING
index d60c31a..a3e9774 100644
--- a/contrib/gregbook/COPYING
+++ b/contrib/gregbook/COPYING
@@ -1,12 +1,12 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
 
  Copyright (C) 1989, 1991 Free Software Foundation, Inc.
      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
-			    Preamble
+                            Preamble
 
   The licenses for most software are designed to take away your
 freedom to share and change it.  By contrast, the GNU General Public
@@ -56,7 +56,7 @@
   The precise terms and conditions for copying, distribution and
 modification follow.
 
-		    GNU GENERAL PUBLIC LICENSE
+                    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
   0. This License applies to any program or other work which contains
@@ -255,7 +255,7 @@
 of preserving the free status of all derivatives of our free software and
 of promoting the sharing and reuse of software generally.
 
-			    NO WARRANTY
+                            NO WARRANTY
 
   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
@@ -277,9 +277,9 @@
 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
 
-		     END OF TERMS AND CONDITIONS
+                     END OF TERMS AND CONDITIONS
 
-	    How to Apply These Terms to Your New Programs
+            How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
 possible use to the public, the best way to achieve this is to make it
diff --git a/contrib/gregbook/Makefile.mingw32 b/contrib/gregbook/Makefile.mingw32
index e70a59a..3a3ff60 100644
--- a/contrib/gregbook/Makefile.mingw32
+++ b/contrib/gregbook/Makefile.mingw32
@@ -47,7 +47,8 @@
 #CC = i386-mingw32msvc-gcc #	e.g., Linux -> Win32 cross-compilation
 LD = $(CC)
 RM = rm -f
-CFLAGS = -O -Wall $(INCS) $(MINGW_CCFLAGS)
+CPPFLAGS = $(INCS)
+CFLAGS = -O -Wall $(MINGW_CCFLAGS)
 # [note that -Wall is a gcc-specific compilation flag ("most warnings on")]
 # [-ansi, -pedantic and -W can also be used]
 LDFLAGS = $(MINGW_LDFLAGS)
@@ -85,10 +86,10 @@
 # implicit make rules -------------------------------------------------------
 
 .c$(O):
-	$(CC) -c $(CFLAGS) $<
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 %.pic$(O): %.c
-	$(CC) -c $(CFLAGS) -DPNG_BUILD_DLL -o $@ $<
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) -DPNG_BUILD_DLL -o $@ $<
 
 
 # dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/Makefile.sgi b/contrib/gregbook/Makefile.sgi
index e3ca6ce..94d61b4 100644
--- a/contrib/gregbook/Makefile.sgi
+++ b/contrib/gregbook/Makefile.sgi
@@ -23,9 +23,9 @@
 
 # macros --------------------------------------------------------------------
 
-PNGINC = -I/usr/local/include/libpng12
-PNGLIB = -L/usr/local/lib -lpng12	  # dynamically linked against libpng
-#PNGLIB = /usr/local/lib/libpng12.a # statically linked against libpng
+PNGINC = -I/usr/local/include/libpng16
+PNGLIB = -L/usr/local/lib -lpng16	  # dynamically linked against libpng
+#PNGLIB = /usr/local/lib/libpng16.a # statically linked against libpng
 # or:
 #PNGINC = -I../..
 #PNGLIB = -L../.. -lpng
@@ -53,7 +53,8 @@
 LD = cc
 RM = rm -f
 # ABI must be the same as that used to build libpng.
-ABI=
+ABI =
+CPPFLAGS =
 CFLAGS = $(ABI) -O -fullwarn $(INCS)
 LDFLAGS = $(ABI)
 O = .o
@@ -73,7 +74,7 @@
 # implicit make rules -------------------------------------------------------
 
 .c$(O):
-	$(CC) -c $(CFLAGS) $<
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 
 # dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/Makefile.unx b/contrib/gregbook/Makefile.unx
index 7ff65bf..1a73e03 100644
--- a/contrib/gregbook/Makefile.unx
+++ b/contrib/gregbook/Makefile.unx
@@ -26,14 +26,14 @@
 # macros --------------------------------------------------------------------
 
 #PNGDIR = /usr/local/lib
-#PNGINC = -I/usr/local/include/libpng12
-#PNGLIBd = -L$(PNGDIR) -lpng12 # dynamically linked, installed libpng
-#PNGLIBs = $(PNGDIR)/libpng12.a # statically linked, installed libpng
+#PNGINC = -I/usr/local/include/libpng16
+#PNGLIBd = -L$(PNGDIR) -lpng16 # dynamically linked, installed libpng
+#PNGLIBs = $(PNGDIR)/libpng16.a # statically linked, installed libpng
 # or:
 PNGDIR = ../..#	this one is for libpng-x.y.z/contrib/gregbook builds
 #PNGDIR = ../libpng
 PNGINC = -I$(PNGDIR)
-PNGLIBd = -Wl,-rpath,$(PNGDIR) -L$(PNGDIR) -lpng12	# dynamically linked
+PNGLIBd = -Wl,-rpath,$(PNGDIR) -L$(PNGDIR) -lpng16	# dynamically linked
 PNGLIBs = $(PNGDIR)/libpng.a		# statically linked, local libpng
 
 ZDIR = /usr/local/lib
@@ -59,14 +59,16 @@
 RLIBSd = $(PNGLIBd) $(ZLIBd) $(XLIB) -lm
 RLIBSs = $(PNGLIBs) $(ZLIBs) $(XLIB) -lm
 WLIBSd = $(PNGLIBd) $(ZLIBd) -lm
-WLIBSs = $(PNGLIBs) $(ZLIBs)
+WLIBSs = $(PNGLIBs) $(ZLIBs) -lm
 
 CC = gcc
 LD = gcc
 RM = rm -f
-CFLAGS = -O -Wall $(INCS) -DFEATURE_LOOP
+CPPFLAGS = $(INCS) -DFEATURE_LOOP
+CFLAGS = -O -Wall
+#CFLAGS = -O -W -Wall -Wextra -pedantic -ansi
 # [note that -Wall is a gcc-specific compilation flag ("most warnings on")]
-# [-ansi, -pedantic and -W can also be used]
+# [-ansi, -pedantic, -Wextra, and -W can also be used]
 LDFLAGS =
 O = .o
 E =
@@ -92,7 +94,7 @@
 # implicit make rules -------------------------------------------------------
 
 .c$(O):
-	$(CC) -c $(CFLAGS) $<
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 
 # dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/Makefile.w32 b/contrib/gregbook/Makefile.w32
index 3c08085..ab7dcf7 100644
--- a/contrib/gregbook/Makefile.w32
+++ b/contrib/gregbook/Makefile.w32
@@ -18,7 +18,7 @@
 # "c:\windows\command.com /e:4096" as the program command line and set the
 # working directory to this directory.  Then double-click to open the new
 # DOS-prompt window with a bigger environment and retry the commands above.
-# 
+#
 # This makefile assumes libpng and zlib have already been built or downloaded
 # and are in subdirectories at the same level as the current subdirectory
 # (as indicated by the PNGPATH and ZPATH macros below).  Edit as appropriate.
@@ -53,7 +53,8 @@
 CC = cl
 LD = link
 RM = del
-CFLAGS = -nologo -O -W3 $(INCS) $(cvars)
+CPPFLAGS = $(INCS)
+CFLAGS = -nologo -O -W3 $(cvars)
 # [note that -W3 is an MSVC-specific compilation flag ("all warnings on")]
 # [see %devstudio%\vc\include\win32.mak for cvars macro definition]
 O = .obj
@@ -76,7 +77,7 @@
 # implicit make rules -------------------------------------------------------
 
 .c$(O):
-	$(CC) -c $(CFLAGS) $<
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 
 # dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/README b/contrib/gregbook/README
index cb6e525..7b1f6a3 100644
--- a/contrib/gregbook/README
+++ b/contrib/gregbook/README
@@ -63,9 +63,9 @@
 
  - Prerequisites (in order of compilation):
 
-      - zlib		http://zlib.net/
-      - libpng		http://www.libpng.org/pub/png/libpng.html
-      - pngbook		http://www.libpng.org/pub/png/book/sources.html
+      - zlib            http://zlib.net/
+      - libpng          http://www.libpng.org/pub/png/libpng.html
+      - pngbook         http://www.libpng.org/pub/png/book/sources.html
 
      The pngbook demo programs are explicitly designed to demonstrate proper
      coding techniques for using the libpng reference library.  As a result,
@@ -104,7 +104,7 @@
      unpacked the source code.
 
      For MSVC, set up the necessary environment variables by invoking
- 
+
         %devstudio%\vc\bin\vcvars32.bat
 
      where where %devstudio% is the installation directory for MSVC /
@@ -165,19 +165,19 @@
      rpng is a simple PNG viewer that can display transparent PNGs with a
      specified background color; for example,
 
-	rpng -bgcolor #ff0000 toucan.png
+        rpng -bgcolor \#ff0000 toucan.png
 
      would display the image with a red background.  rpng2 is a progressive
      viewer that simulates a web browser in some respects; it can display
      images against either a background color or a dynamically generated
      background image.  For example:
 
-	rpng2 -bgpat 16 toucan.png
+        rpng2 -bgpat 16 toucan.png
 
      wpng is a purely command-line image converter from binary PBMPLUS/NetPBM
      format (.pgm or .ppm) to PNG; for example,
 
-	wpng -time < toucan-notrans.ppm > toucan-notrans.png
+        wpng -time < toucan-notrans.ppm > toucan-notrans.png
 
      would convert the specified PPM file (using redirection) to PNG, auto-
      matically setting the PNG modification-time chunk.
diff --git a/contrib/gregbook/makevms.com b/contrib/gregbook/makevms.com
index bd37dc0..f32bcab 100644
--- a/contrib/gregbook/makevms.com
+++ b/contrib/gregbook/makevms.com
@@ -20,13 +20,13 @@
 $ then
 $   write sys$output "zlib include not found. Exiting..."
 $   exit 2
-$ endif 
+$ endif
 $!
 $ if pngpath .eqs. ""
 $ then
 $   write sys$output "libpng include not found. Exiting..."
 $   exit 2
-$ endif 
+$ endif
 $!
 $!    Look for the compiler used.
 $!
diff --git a/contrib/gregbook/readpng.c b/contrib/gregbook/readpng.c
index abfc1fb..9167403 100644
--- a/contrib/gregbook/readpng.c
+++ b/contrib/gregbook/readpng.c
@@ -55,8 +55,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <zlib.h>
 
-#include "png.h"        /* libpng header; includes zlib.h */
+#include "png.h"        /* libpng header */
 #include "readpng.h"    /* typedefs, common macros, public prototypes */
 
 /* future versions of libpng will provide this macro: */
@@ -99,7 +100,8 @@
 
     /* could pass pointers to user-defined error handlers instead of NULLs: */
 
-    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), NULL, NULL,
+        NULL);
     if (!png_ptr)
         return 4;   /* out of memory */
 
@@ -214,6 +216,10 @@
      * libpng function */
 
     if (setjmp(png_jmpbuf(png_ptr))) {
+        free(image_data);
+        image_data = NULL;
+        free(row_pointers);
+        row_pointers = NULL;
         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
         return NULL;
     }
@@ -229,8 +235,14 @@
         png_set_expand(png_ptr);
     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
         png_set_expand(png_ptr);
+#ifdef PNG_READ_16_TO_8_SUPPORTED
     if (bit_depth == 16)
+#  ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+        png_set_scale_16(png_ptr);
+#  else
         png_set_strip_16(png_ptr);
+#  endif
+#endif
     if (color_type == PNG_COLOR_TYPE_GRAY ||
         color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
         png_set_gray_to_rgb(png_ptr);
@@ -263,7 +275,8 @@
         return NULL;
     }
 
-    Trace((stderr, "readpng_get_image:  channels = %d, rowbytes = %ld, height = %ld\n", *pChannels, rowbytes, height));
+    Trace((stderr, "readpng_get_image:  channels = %d, rowbytes = %ld, height = %ld\n",
+        *pChannels, rowbytes, height));
 
 
     /* set the individual row_pointers to point at the correct offsets */
diff --git a/contrib/gregbook/readpng2.c b/contrib/gregbook/readpng2.c
index 2ee6b68..a9e7ba5 100644
--- a/contrib/gregbook/readpng2.c
+++ b/contrib/gregbook/readpng2.c
@@ -4,7 +4,7 @@
 
   ---------------------------------------------------------------------------
 
-      Copyright (c) 1998-2007 Greg Roelofs.  All rights reserved.
+      Copyright (c) 1998-2015 Greg Roelofs.  All rights reserved.
 
       This software is provided "as is," without warranty of any kind,
       express or implied.  In no event shall the author or contributors
@@ -51,12 +51,19 @@
       along with this program; if not, write to the Free Software Foundation,
       Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+  ---------------------------------------------------------------------------
+
+   Changelog:
+     %RDATE% - Check return value of png_get_bKGD() (Glenn R-P)
+
   ---------------------------------------------------------------------------*/
 
 
 #include <stdlib.h>     /* for exit() prototype */
+#include <setjmp.h>
 
-#include "png.h"        /* libpng header; includes zlib.h and setjmp.h */
+#include <zlib.h>
+#include "png.h"        /* libpng header from the local directory */
 #include "readpng2.h"   /* typedefs, common macros, public prototypes */
 
 
@@ -67,90 +74,17 @@
                                  png_uint_32 row_num, int pass);
 static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr);
 static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg);
+static void readpng2_warning_handler(png_structp png_ptr, png_const_charp msg);
 
 
 
 
 void readpng2_version_info(void)
 {
-#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
-    (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
-    defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
-    /*
-     * WARNING:  This preprocessor approach means that the following code
-     *           cannot be used with a libpng DLL older than 1.2.0--the
-     *           compiled-in symbols for the new functions will not exist.
-     *           (Could use dlopen() and dlsym() on Unix and corresponding
-     *           calls for Windows, but not portable...)
-     */
-    {
-        int mmxsupport = png_mmx_support();
-        if (mmxsupport < 0)
-            fprintf(stderr, "   Compiled with libpng %s; using libpng %s "
-              "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
-        else {
-            int compilerID;
-            png_uint_32 mmx_mask = png_get_mmx_flagmask(
-              PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
+    fprintf(stderr, "   Compiled with libpng %s; using libpng %s\n",
+      PNG_LIBPNG_VER_STRING, png_libpng_ver);
 
-            fprintf(stderr, "   Compiled with libpng %s; using libpng %s "
-              "with MMX support\n   (%s version).", PNG_LIBPNG_VER_STRING,
-              png_libpng_ver, compilerID == 1? "MSVC++" :
-              (compilerID == 2? "GNU C" : "unknown"));
-            fprintf(stderr, "  Processor (x86%s) %s MMX instructions.\n",
-#if defined(__x86_64__)
-              "_64",
-#else
-              "",
-#endif
-              mmxsupport? "supports" : "does not support");
-            if (mmxsupport > 0) {
-                int num_optims = 0;
-
-                fprintf(stderr,
-                  "      Potential MMX optimizations supported by libpng:\n");
-                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)
-                    ++num_optims;
-                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_UP)
-                    ++num_optims;
-                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)
-                    ++num_optims;
-                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)
-                    ++num_optims;
-                if (num_optims)
-                    fprintf(stderr,
-                      "         decoding %s row filters (reading)\n",
-                      (num_optims == 4)? "all non-trivial" : "some");
-                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) {
-                    fprintf(stderr, "         combining rows (reading)\n");
-                    ++num_optims;
-                }
-                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_INTERLACE) {
-                    fprintf(stderr,
-                      "         expanding interlacing (reading)\n");
-                    ++num_optims;
-                }
-                mmx_mask &= ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
-                             | PNG_ASM_FLAG_MMX_READ_INTERLACE    \
-                             | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
-                             | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
-                             | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
-                             | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
-                if (mmx_mask) {
-                    fprintf(stderr, "         other (unknown)\n");
-                    ++num_optims;
-                }
-                if (num_optims == 0)
-                    fprintf(stderr, "         (none)\n");
-            }
-        }
-    }
-#else
-    fprintf(stderr, "   Compiled with libpng %s; using libpng %s "
-      "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
-#endif
-
-    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+    fprintf(stderr, "   and with zlib %s; using zlib %s.\n",
       ZLIB_VERSION, zlib_version);
 }
 
@@ -175,8 +109,8 @@
 
     /* could also replace libpng warning-handler (final NULL), but no need: */
 
-    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
-      readpng2_error_handler, NULL);
+    png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), mainprog_ptr,
+      readpng2_error_handler, readpng2_warning_handler);
     if (!png_ptr)
         return 4;   /* out of memory */
 
@@ -203,36 +137,30 @@
     }
 
 
-#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
     /* prepare the reader to ignore all recognized chunks whose data won't be
      * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT,
      * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */
     {
-        /* These byte strings were copied from png.h.  If a future libpng
-         * version recognizes more chunks, add them to this list.  If a
-         * future version of readpng2.c recognizes more chunks, delete them
-         * from this list. */
-        static const png_byte chunks_to_ignore[] = {
-             99,  72,  82,  77, '\0',  /* cHRM */
-            104,  73,  83,  84, '\0',  /* hIST */
-            105,  67,  67,  80, '\0',  /* iCCP */
-            105,  84,  88, 116, '\0',  /* iTXt */
-            111,  70,  70, 115, '\0',  /* oFFs */
-            112,  67,  65,  76, '\0',  /* pCAL */
-            112,  72,  89, 115, '\0',  /* pHYs */
-            115,  66,  73,  84, '\0',  /* sBIT */
-            115,  67,  65,  76, '\0',  /* sCAL */
-            115,  80,  76,  84, '\0',  /* sPLT */
-            115,  84,  69,  82, '\0',  /* sTER */
-            116,  69,  88, 116, '\0',  /* tEXt */
-            116,  73,  77,  69, '\0',  /* tIME */
-            122,  84,  88, 116, '\0'   /* zTXt */
-        };
+        /* These byte strings were copied from png.h.  If a future version
+         * of readpng2.c recognizes more chunks, add them to this list.
+         */
+        static PNG_CONST png_byte chunks_to_process[] = {
+            98,  75,  71,  68, '\0',  /* bKGD */
+           103,  65,  77,  65, '\0',  /* gAMA */
+           115,  82,  71,  66, '\0',  /* sRGB */
+           };
 
-        png_set_keep_unknown_chunks(png_ptr, 1 /* PNG_HANDLE_CHUNK_NEVER */,
-          chunks_to_ignore, sizeof(chunks_to_ignore)/5);
+       /* Ignore all chunks except for IHDR, PLTE, tRNS, IDAT, and IEND */
+       png_set_keep_unknown_chunks(png_ptr, -1 /* PNG_HANDLE_CHUNK_NEVER */,
+          NULL, -1);
+
+       /* But do not ignore chunks in the "chunks_to_process" list */
+       png_set_keep_unknown_chunks(png_ptr,
+          0 /* PNG_HANDLE_CHUNK_AS_DEFAULT */, chunks_to_process,
+          sizeof(chunks_to_process)/5);
     }
-#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
+#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */
 
 
     /* instead of doing png_init_io() here, now we set up our callback
@@ -242,96 +170,6 @@
       readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);
 
 
-    /*
-     * may as well enable or disable MMX routines here, if supported;
-     *
-     * to enable all:  mask = png_get_mmx_flagmask (
-     *                   PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
-     *                 flags = png_get_asm_flags (png_ptr);
-     *                 flags |= mask;
-     *                 png_set_asm_flags (png_ptr, flags);
-     *
-     * to disable all:  mask = png_get_mmx_flagmask (
-     *                   PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
-     *                  flags = png_get_asm_flags (png_ptr);
-     *                  flags &= ~mask;
-     *                  png_set_asm_flags (png_ptr, flags);
-     */
-
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
-    defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
-    /*
-     * WARNING:  This preprocessor approach means that the following code
-     *           cannot be used with a libpng DLL older than 1.2.0--the
-     *           compiled-in symbols for the new functions will not exist.
-     *           (Could use dlopen() and dlsym() on Unix and corresponding
-     *           calls for Windows, but not portable...)
-     */
-    {
-#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
-        png_uint_32 mmx_disable_mask = 0;
-        png_uint_32 asm_flags, mmx_mask;
-        int compilerID;
-
-        if (mainprog_ptr->nommxfilters)
-            mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
-                                | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
-                                | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
-                                | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
-        if (mainprog_ptr->nommxcombine)
-            mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_COMBINE_ROW;
-        if (mainprog_ptr->nommxinterlace)
-            mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_INTERLACE;
-        asm_flags = png_get_asm_flags(png_ptr);
-        png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask);
-
-
-        /* Now query libpng's asm settings, just for yuks.  Note that this
-         * differs from the querying of its *potential* MMX capabilities
-         * in readpng2_version_info(); this is true runtime verification. */
-
-        asm_flags = png_get_asm_flags(png_ptr);
-        mmx_mask = png_get_mmx_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE,
-          &compilerID);
-        if (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED)
-            fprintf(stderr,
-              "  MMX support (%s version) is compiled into libpng\n",
-              compilerID == 1? "MSVC++" :
-              (compilerID == 2? "GNU C" : "unknown"));
-        else
-            fprintf(stderr, "  MMX support is not compiled into libpng\n");
-        fprintf(stderr, "  MMX instructions are %ssupported by CPU\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)? "" : "not ");
-        fprintf(stderr, "  MMX read support for combining rows is %sabled\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)? "en" : "dis");
-        fprintf(stderr,
-          "  MMX read support for expanding interlacing is %sabled\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)? "en" : "dis");
-        fprintf(stderr, "  MMX read support for \"sub\" filter is %sabled\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "en" : "dis");
-        fprintf(stderr, "  MMX read support for \"up\" filter is %sabled\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "en" : "dis");
-        fprintf(stderr, "  MMX read support for \"avg\" filter is %sabled\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "en" : "dis");
-        fprintf(stderr, "  MMX read support for \"Paeth\" filter is %sabled\n",
-          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "en" : "dis");
-        asm_flags &= (mmx_mask & ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
-                                  | PNG_ASM_FLAG_MMX_READ_INTERLACE    \
-                                  | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
-                                  | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
-                                  | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
-                                  | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ));
-        if (asm_flags)
-            fprintf(stderr,
-              "  additional MMX support is also enabled (0x%02lx)\n",
-              asm_flags);
-#else  /* !PNG_ASSEMBLER_CODE_SUPPORTED */
-        fprintf(stderr, "  MMX querying is disabled in libpng.\n");
-#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
-    }
-#endif
-
-
     /* make sure we save our pointers for use in readpng2_decode_data() */
 
     mainprog_ptr->png_ptr = png_ptr;
@@ -380,7 +218,11 @@
     mainprog_info  *mainprog_ptr;
     int  color_type, bit_depth;
     png_uint_32 width, height;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
     double  gamma;
+#else
+    png_fixed_point gamma;
+#endif
 
 
     /* setjmp() doesn't make sense here, because we'd either have to exit(),
@@ -424,36 +266,38 @@
     /* since we know we've read all of the PNG file's "header" (i.e., up
      * to IDAT), we can check for a background color here */
 
-    if (mainprog_ptr->need_bgcolor &&
-        png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
+    if (mainprog_ptr->need_bgcolor)
     {
         png_color_16p pBackground;
 
         /* it is not obvious from the libpng documentation, but this function
          * takes a pointer to a pointer, and it always returns valid red,
          * green and blue values, regardless of color_type: */
-        png_get_bKGD(png_ptr, info_ptr, &pBackground);
+        if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
+        {
 
-        /* however, it always returns the raw bKGD data, regardless of any
-         * bit-depth transformations, so check depth and adjust if necessary */
-        if (bit_depth == 16) {
-            mainprog_ptr->bg_red   = pBackground->red   >> 8;
-            mainprog_ptr->bg_green = pBackground->green >> 8;
-            mainprog_ptr->bg_blue  = pBackground->blue  >> 8;
-        } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
-            if (bit_depth == 1)
-                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
-                  mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
-            else if (bit_depth == 2)
-                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
-                  mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
-            else /* bit_depth == 4 */
-                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
-                  mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
-        } else {
-            mainprog_ptr->bg_red   = (uch)pBackground->red;
-            mainprog_ptr->bg_green = (uch)pBackground->green;
-            mainprog_ptr->bg_blue  = (uch)pBackground->blue;
+           /* however, it always returns the raw bKGD data, regardless of any
+            * bit-depth transformations, so check depth and adjust if necessary
+            */
+           if (bit_depth == 16) {
+               mainprog_ptr->bg_red   = pBackground->red   >> 8;
+               mainprog_ptr->bg_green = pBackground->green >> 8;
+               mainprog_ptr->bg_blue  = pBackground->blue  >> 8;
+           } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+               if (bit_depth == 1)
+                   mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+                     mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
+               else if (bit_depth == 2)
+                   mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+                     mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
+               else /* bit_depth == 4 */
+                   mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+                     mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
+           } else {
+               mainprog_ptr->bg_red   = (uch)pBackground->red;
+               mainprog_ptr->bg_green = (uch)pBackground->green;
+               mainprog_ptr->bg_blue  = (uch)pBackground->blue;
+           }
         }
     }
 
@@ -469,8 +313,14 @@
         png_set_expand(png_ptr);
     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
         png_set_expand(png_ptr);
+#ifdef PNG_READ_16_TO_8_SUPPORTED
     if (bit_depth == 16)
+#  ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+        png_set_scale_16(png_ptr);
+#  else
         png_set_strip_16(png_ptr);
+#  endif
+#endif
     if (color_type == PNG_COLOR_TYPE_GRAY ||
         color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
         png_set_gray_to_rgb(png_ptr);
@@ -491,11 +341,19 @@
      * "gamma" value for the entire display system, i.e., the product of
      * LUT_exponent and CRT_exponent. */
 
+#ifdef PNG_FLOATING_POINT_SUPPORTED
     if (png_get_gAMA(png_ptr, info_ptr, &gamma))
         png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma);
     else
         png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455);
-
+#else
+    if (png_get_gAMA_fixed(png_ptr, info_ptr, &gamma))
+        png_set_gamma_fixed(png_ptr,
+            (png_fixed_point)(100000*mainprog_ptr->display_exponent+.5), gamma);
+    else
+        png_set_gamma_fixed(png_ptr,
+            (png_fixed_point)(100000*mainprog_ptr->display_exponent+.5), 45455);
+#endif
 
     /* we'll let libpng expand interlaced images, too */
 
@@ -597,6 +455,8 @@
 
     /* all done */
 
+    (void)info_ptr; /* Unused */
+
     return;
 }
 
@@ -617,7 +477,12 @@
 }
 
 
-
+static void readpng2_warning_handler(png_structp png_ptr, png_const_charp msg)
+{
+    fprintf(stderr, "readpng2 libpng warning: %s\n", msg);
+    fflush(stderr);
+    (void)png_ptr; /* Unused */
+}
 
 
 static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg)
@@ -644,5 +509,12 @@
         exit(99);
     }
 
+    /* Now we have our data structure we can use the information in it
+     * to return control to our own higher level code (all the points
+     * where 'setjmp' is called in this file.)  This will work with other
+     * error handling mechanisms as well - libpng always calls png_error
+     * when it can proceed no further, thus, so long as the error handler
+     * is intercepted, application code can do its own error recovery.
+     */
     longjmp(mainprog_ptr->jmpbuf, 1);
 }
diff --git a/contrib/gregbook/readpng2.h b/contrib/gregbook/readpng2.h
index fbfffb4..6b3660d 100644
--- a/contrib/gregbook/readpng2.h
+++ b/contrib/gregbook/readpng2.h
@@ -96,11 +96,6 @@
     int rowbytes;
     int channels;
     int need_bgcolor;
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-    int nommxfilters;
-    int nommxcombine;
-    int nommxinterlace;
-#endif
     int state;
     uch bg_red;
     uch bg_green;
diff --git a/contrib/gregbook/readppm.c b/contrib/gregbook/readppm.c
index be9a56d..7fefc39 100644
--- a/contrib/gregbook/readppm.c
+++ b/contrib/gregbook/readppm.c
@@ -163,8 +163,12 @@
 
     /* now we can go ahead and just read the whole image */
 
-    fread(image_data, 1L, rowbytes*height, saved_infile);
-
+    if (fread(image_data, 1L, rowbytes*height, saved_infile) <
+       rowbytes*height) {
+        free (image_data);
+        image_data = NULL;
+        return NULL;
+    }
 
     return image_data;
 }
diff --git a/contrib/gregbook/rpng-win.c b/contrib/gregbook/rpng-win.c
index 2020961..f53ddc8 100644
--- a/contrib/gregbook/rpng-win.c
+++ b/contrib/gregbook/rpng-win.c
@@ -85,7 +85,33 @@
 #include <string.h>
 #include <time.h>
 #include <windows.h>
+#ifdef __CYGWIN__
+/* getch replacement. Turns out, we don't really need this,
+ * but leave it here if we ever enable any of the uses of
+ * _getch in the main code
+ */
+#include <unistd.h>
+#include <termio.h>
+#include <sys/ioctl.h>
+int repl_getch( void )
+{
+  char ch;
+  int fd = fileno(stdin);
+  struct termio old_tty, new_tty;
+
+  ioctl(fd, TCGETA, &old_tty);
+  new_tty = old_tty;
+  new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
+  ioctl(fd, TCSETA, &new_tty);
+  fread(&ch, 1, sizeof(ch), stdin);
+  ioctl(fd, TCSETA, &old_tty);
+
+  return ch;
+}
+#define _getch repl_getch
+#else
 #include <conio.h>      /* only for _getch() */
+#endif
 
 /* #define DEBUG  :  this enables the Trace() macros */
 
@@ -153,7 +179,7 @@
 
     filename = (char *)NULL;
 
-
+#ifndef __CYGWIN__
     /* First reenable console output, which normally goes to the bit bucket
      * for windowed apps.  Closing the console window will terminate the
      * app.  Thanks to David.Geldreich@realviz.com for supplying the magical
@@ -162,6 +188,7 @@
     AllocConsole();
     freopen("CONOUT$", "a", stderr);
     freopen("CONOUT$", "a", stdout);
+#endif
 
 
     /* Next set the default value for our display-system exponent, i.e.,
@@ -279,7 +306,9 @@
     /* print usage screen if any errors up to this point */
 
     if (error) {
+#ifndef __CYGWIN__
         int ch;
+#endif
 
         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
         readpng_version_info();
@@ -293,11 +322,15 @@
           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
           "\t\t  used with transparent images\n"
           "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+#ifndef __CYGWIN__
           "Press Q or Esc to quit this usage screen.\n"
+#endif
           "\n", PROGNAME, default_display_exponent);
+#ifndef __CYGWIN__
         do
             ch = _getch();
         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
         exit(1);
     }
 
@@ -333,18 +366,24 @@
 
 
     if (error) {
+#ifndef __CYGWIN__
         int ch;
+#endif
 
         fprintf(stderr, PROGNAME ":  aborting.\n");
+#ifndef __CYGWIN__
         do
             ch = _getch();
         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
         exit(2);
     } else {
         fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+#ifndef __CYGWIN__
         fprintf(stderr,
           "\n   [console window:  closing this window will terminate %s]\n\n",
           PROGNAME);
+#endif
     }
 
 
@@ -417,7 +456,12 @@
     /* wait for the user to tell us when to quit */
 
     printf(
-      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
+#ifndef __CYGWIN__
+      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n"
+#else
+      "Done.  Press mouse button 1 (within image window) to quit.\n"
+#endif
+    );
     fflush(stdout);
 
     while (GetMessage(&msg, NULL, 0, 0)) {
diff --git a/contrib/gregbook/rpng-x.c b/contrib/gregbook/rpng-x.c
index 0a98170..ddd7c58 100644
--- a/contrib/gregbook/rpng-x.c
+++ b/contrib/gregbook/rpng-x.c
@@ -26,6 +26,8 @@
     - 1.14:  added support for X resources (thanks to Gerhard Niklasch)
     - 2.00:  dual-licensed (added GNU GPL)
     - 2.01:  fixed improper display of usage screen on PNG error(s)
+    - 2.02:  Added "void(argc);" statement to quiet pedantic compiler warnings
+             about unused variable (GR-P)
 
   ---------------------------------------------------------------------------
 
@@ -80,9 +82,9 @@
 
 #define PROGNAME  "rpng-x"
 #define LONGNAME  "Simple PNG Viewer for X"
-#define VERSION   "2.01 of 16 March 2008"
-#define RESNAME   "rpng"	/* our X resource application name */
-#define RESCLASS  "Rpng"	/* our X resource class name */
+#define VERSION   "2.02 of 15 June 2014"
+#define RESNAME   "rpng"        /* our X resource application name */
+#define RESCLASS  "Rpng"        /* our X resource class name */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -252,8 +254,8 @@
             else {
                 bgstr = *argv;
                 if (strlen(bgstr) != 7 || bgstr[0] != '#')
-                    ++error; 
-                else 
+                    ++error;
+                else
                     have_bg = TRUE;
             }
         } else {
@@ -279,15 +281,17 @@
           "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n"
           "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
-          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
+          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n",
+          PROGNAME, default_display_exponent);
+
+        fprintf(stderr, "\n"
           "\t\t  to the product of the lookup-table exponent (varies)\n"
           "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
           "    bg  \tdesired background color in 7-character hex RGB format\n"
           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
           "\t\t  used with transparent images\n"
           "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
-          "is displayed) to quit.\n"
-          "\n", PROGNAME, default_display_exponent);
+          "is displayed) to quit.\n");
         exit(1);
     }
 
@@ -419,6 +423,8 @@
 
     rpng_x_cleanup();
 
+    (void)argc; /* Unused */
+
     return 0;
 }
 
diff --git a/contrib/gregbook/rpng2-win.c b/contrib/gregbook/rpng2-win.c
index 9f7ad78..a7b9dfa 100644
--- a/contrib/gregbook/rpng2-win.c
+++ b/contrib/gregbook/rpng2-win.c
@@ -32,6 +32,9 @@
     - 2.01:  fixed 64-bit typo in readpng2.c
     - 2.02:  fixed improper display of usage screen on PNG error(s); fixed
               unexpected-EOF and file-read-error cases
+    - 2.03:  removed runtime MMX-enabling/disabling and obsolete -mmx* options
+    - 2.04:
+             (GR-P)
 
   ---------------------------------------------------------------------------
 
@@ -95,7 +98,33 @@
 #include <time.h>
 #include <math.h>      /* only for PvdM background code */
 #include <windows.h>
+#ifdef __CYGWIN__
+/* getch replacement. Turns out, we don't really need this,
+ * but leave it here if we ever enable any of the uses of
+ * _getch in the main code
+ */
+#include <unistd.h>
+#include <termio.h>
+#include <sys/ioctl.h>
+int repl_getch( void )
+{
+  char ch;
+  int fd = fileno(stdin);
+  struct termio old_tty, new_tty;
+
+  ioctl(fd, TCGETA, &old_tty);
+  new_tty = old_tty;
+  new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
+  ioctl(fd, TCSETA, &new_tty);
+  fread(&ch, 1, sizeof(ch), stdin);
+  ioctl(fd, TCSETA, &old_tty);
+
+  return ch;
+}
+#define _getch repl_getch
+#else
 #include <conio.h>     /* only for _getch() */
+#endif
 
 /* all for PvdM background code: */
 #ifndef PI
@@ -269,7 +298,7 @@
     filename = (char *)NULL;
     memset(&rpng2_info, 0, sizeof(mainprog_info));
 
-
+#ifndef __CYGWIN__
     /* Next reenable console output, which normally goes to the bit bucket
      * for windowed apps.  Closing the console window will terminate the
      * app.  Thanks to David.Geldreich@realviz.com for supplying the magical
@@ -278,7 +307,7 @@
     AllocConsole();
     freopen("CONOUT$", "a", stderr);
     freopen("CONOUT$", "a", stdout);
-
+#endif
 
     /* Set the default value for our display-system exponent, i.e., the
      * product of the CRT exponent and the exponent corresponding to
@@ -395,18 +424,6 @@
             }
         } else if (!strncmp(*argv, "-timing", 2)) {
             timing = TRUE;
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-        } else if (!strncmp(*argv, "-nommxfilters", 7)) {
-            rpng2_info.nommxfilters = TRUE;
-        } else if (!strncmp(*argv, "-nommxcombine", 7)) {
-            rpng2_info.nommxcombine = TRUE;
-        } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
-            rpng2_info.nommxinterlace = TRUE;
-        } else if (!strcmp(*argv, "-nommx")) {
-            rpng2_info.nommxfilters = TRUE;
-            rpng2_info.nommxcombine = TRUE;
-            rpng2_info.nommxinterlace = TRUE;
-#endif
         } else {
             if (**argv != '-') {
                 filename = *argv;
@@ -424,15 +441,14 @@
     /* print usage screen if any errors up to this point */
 
     if (error) {
+#ifndef __CYGWIN__
         int ch;
+#endif
 
         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
         readpng2_version_info();
         fprintf(stderr, "\n"
           "Usage:  %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n"
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-          "        %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
-#endif
           "        %*s file.png\n\n"
           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
           "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
@@ -445,21 +461,24 @@
           "\t\t  transparent images; overrides -bgcolor option\n"
           "    -timing\tenables delay for every block read, to simulate modem\n"
           "\t\t  download of image (~36 Kbps)\n"
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-          "    -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
-          "\t\t  combining rows, and expanding interlacing, respectively\n"
-#endif
           "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+#ifndef __CYGWIN__
           "Press Q or Esc to quit this usage screen. ",
+#else
+          ,
+#endif
           PROGNAME,
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
+#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
+    !(defined(__CYGWIN__) || defined(__MINGW32__))
           (int)strlen(PROGNAME), " ",
 #endif
           (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
         fflush(stderr);
+#ifndef __CYGWIN__
         do
             ch = _getch();
         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
         exit(1);
     }
 
@@ -496,18 +515,24 @@
 
 
     if (error) {
+#ifndef __CYGWIN__
         int ch;
+#endif
 
         fprintf(stderr, PROGNAME ":  aborting.\n");
+#ifndef __CYGWIN__
         do
             ch = _getch();
         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
         exit(2);
     } else {
         fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+#ifndef __CYGWIN__
         fprintf(stderr,
           "\n   [console window:  closing this window will terminate %s]\n\n",
           PROGNAME);
+#endif
         fflush(stderr);
     }
 
@@ -567,7 +592,7 @@
                   "(unexpectedly) while reading PNG image file\n");
                 exit(3);
             } else /* if (error) */ {
-                // will print error message below
+                /* will print error message below */
             }
             break;
         }
@@ -1150,7 +1175,12 @@
 
     rpng2_info.state = kDone;
     printf(
-      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
+#ifndef __CYGWIN__
+      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n"
+#else
+      "Done.  Press mouse button 1 (within image window) to quit.\n"
+#endif
+    );
     fflush(stdout);
 }
 
diff --git a/contrib/gregbook/rpng2-x.c b/contrib/gregbook/rpng2-x.c
index 873af23..0c8ddeb 100644
--- a/contrib/gregbook/rpng2-x.c
+++ b/contrib/gregbook/rpng2-x.c
@@ -40,10 +40,13 @@
     - 2.02:  fixed improper display of usage screen on PNG error(s); fixed
               unexpected-EOF and file-read-error cases; fixed Trace() cut-and-
               paste bugs
-
+    - 2.03:  deleted runtime MMX-enabling/disabling and obsolete -mmx* options
+    - 2.04:  Added "void(foo);" statements to quiet pedantic compiler warnings
+             about unused variables (GR-P)
+    - 2.05:  Use nanosleep() instead of usleep(), which is deprecated (GR-P).
   ---------------------------------------------------------------------------
 
-      Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
+      Copyright (c) 1998-2010, 2014-2015 Greg Roelofs.  All rights reserved.
 
       This software is provided "as is," without warranty of any kind,
       express or implied.  In no event shall the author or contributors
@@ -94,9 +97,9 @@
 
 #define PROGNAME  "rpng2-x"
 #define LONGNAME  "Progressive PNG Viewer for X"
-#define VERSION   "2.02 of 16 March 2008"
-#define RESNAME   "rpng2"	/* our X resource application name */
-#define RESCLASS  "Rpng"	/* our X resource class name */
+#define VERSION   "2.04 of 15 June 2014"
+#define RESNAME   "rpng2"       /* our X resource application name */
+#define RESCLASS  "Rpng"       /* our X resource class name */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -110,6 +113,19 @@
 #include <X11/Xos.h>
 #include <X11/keysym.h>   /* defines XK_* macros */
 
+#if _POSIX_C_SOURCE >= 199309L /* have nanosleep() */
+# undef usleep
+# define usleep(usec) {        \
+   struct timespec ts;         \
+   ts.tv_sec = 0;              \
+   ts.tv_nsec = (usec) * 1000; \
+   nanosleep(&ts, NULL); }
+#  endif
+
+#ifndef usleep /* have neither nanosleep() nor usleep() */
+#  define usleep(x) sleep(((x)+499999)/1000000)
+#endif
+
 #ifdef VMS
 #  include <unistd.h>
 #endif
@@ -131,7 +147,7 @@
                   (e.type == KeyPress &&   /*  v--- or 1 for shifted keys */  \
                   ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
 
-#define NO_24BIT_MASKS	/* undef case not fully written--only for redisplay() */
+#define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
 
 #define rgb1_max   bg_freq
 #define rgb1_min   bg_gray
@@ -301,7 +317,7 @@
     int have_bg = FALSE;
 #ifdef FEATURE_LOOP
     int loop = FALSE;
-    long loop_interval = -1;		/* seconds (100,000 max) */
+    long loop_interval = -1;            /* seconds (100,000 max) */
 #endif
     double LUT_exponent;                /* just the lookup table */
     double CRT_exponent = 2.2;          /* just the monitor */
@@ -435,18 +451,6 @@
                     loop_interval = 100000;
             }
 #endif
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-        } else if (!strncmp(*argv, "-nommxfilters", 7)) {
-            rpng2_info.nommxfilters = TRUE;
-        } else if (!strncmp(*argv, "-nommxcombine", 7)) {
-            rpng2_info.nommxcombine = TRUE;
-        } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
-            rpng2_info.nommxinterlace = TRUE;
-        } else if (!strcmp(*argv, "-nommx")) {
-            rpng2_info.nommxfilters = TRUE;
-            rpng2_info.nommxcombine = TRUE;
-            rpng2_info.nommxinterlace = TRUE;
-#endif
         } else {
             if (**argv != '-') {
                 filename = *argv;
@@ -467,50 +471,47 @@
         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
         readpng2_version_info();
         fprintf(stderr, "\n"
-          "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-          "        %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
-#endif
+          "Usage:   ");
+        fprintf(stderr,
+          "%s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
+          "        %*s [-usleep dur | -timing] [-pause]\n",
+          PROGNAME, (int)strlen(PROGNAME), " ");
+        fprintf(stderr,
 #ifdef FEATURE_LOOP
-          "        %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
-#else
-          "        %*s [-usleep dur | -timing] [-pause] file.png\n\n"
+          "        [-loop [sec]]"
 #endif
+          " file.png\n\n");
+        fprintf(stderr,
           "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
           "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
-          "\t\t  to the product of the lookup-table exponent (varies)\n"
+          "\t\t  to the product of the lookup-table exponent (varies)\n",
+          default_display_exponent);
+        fprintf(stderr,
           "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
           "    bg  \tdesired background color in 7-character hex RGB format\n"
           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
           "\t\t  used with transparent images; overrides -bgpat\n"
           "    pat \tdesired background pattern number (0-%d); used with\n"
-          "\t\t  transparent images; overrides -bgcolor\n"
+          "\t\t  transparent images; overrides -bgcolor\n",
+          num_bgpat-1);
 #ifdef FEATURE_LOOP
+        fprintf(stderr,
           "    -loop\tloops through background images after initial display\n"
           "\t\t  is complete (depends on -bgpat)\n"
-          "    sec \tseconds to display each background image (default = 2)\n"
+          "    sec \tseconds to display each background image (default = 2)\n");
 #endif
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-          "    -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
-          "\t\t  combining rows, and expanding interlacing, respectively\n"
-#endif
+        fprintf(stderr,
           "    dur \tduration in microseconds to wait after displaying each\n"
           "\t\t  row (for demo purposes)\n"
           "    -timing\tenables delay for every block read, to simulate modem\n"
           "\t\t  download of image (~36 Kbps)\n"
           "    -pause\tpauses after displaying each pass until mouse clicked\n"
           "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
-          "is displayed) to quit.\n"
-          "\n", PROGNAME,
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
-          (int)strlen(PROGNAME), " ",
-#endif
-          (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
+          "is displayed) to quit.\n");
         exit(1);
     }
 
-
     if (!(infile = fopen(filename, "rb"))) {
         fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
         ++error;
@@ -612,7 +613,7 @@
                   "(unexpectedly) while reading PNG image file\n");
                 exit(3);
             } else /* if (error) */ {
-                // will print error message below
+                /* will print error message below */
             }
             break;
         }
@@ -756,6 +757,8 @@
     Trace((stderr, "about to call rpng2_x_cleanup()\n"))
     rpng2_x_cleanup();
 
+    (void)argc; /* Unused */
+
     return 0;
 }
 
@@ -802,8 +805,9 @@
     if (rpng2_x_create_window()) {
 
         /* GRR TEMPORARY HACK:  this is fundamentally no different from cases
-         * above; libpng should longjmp() back to us when png_ptr goes away.
-         * If we/it segfault instead, seems like a libpng bug... */
+         * above; libpng should call our error handler to longjmp() back to us
+         * when png_ptr goes away.  If we/it segfault instead, seems like a
+         * libpng bug... */
 
         /* we're here via libpng callback, so if window fails, clean and bail */
         readpng2_cleanup(&rpng2_info);
@@ -1297,7 +1301,7 @@
         for (row = 0;  row < rpng2_info.height;  ++row) {
             src = bg_data + row*bg_rowbytes;
             dest = ximage->data + row*ximage_rowbytes;
-            if (bpp == 32) {	/* slightly optimized version */
+            if (bpp == 32) {    /* slightly optimized version */
                 for (i = rpng2_info.width;  i > 0;  --i) {
                     red   = *src++;
                     green = *src++;
@@ -1846,6 +1850,9 @@
         XFlush(display);
     }
 
+    (void)startcol;
+    (void)width;
+
 } /* end function rpng2_x_redisplay_image() */
 
 
diff --git a/contrib/gregbook/writepng.c b/contrib/gregbook/writepng.c
index e6d81ea..c5c9534 100644
--- a/contrib/gregbook/writepng.c
+++ b/contrib/gregbook/writepng.c
@@ -55,8 +55,9 @@
 
 
 #include <stdlib.h>     /* for exit() prototype */
+#include <zlib.h>
 
-#include "png.h"        /* libpng header; includes zlib.h and setjmp.h */
+#include "png.h"        /* libpng header, includes setjmp.h */
 #include "writepng.h"   /* typedefs, common macros, public prototypes */
 
 
@@ -89,7 +90,7 @@
 
     /* could also replace libpng warning-handler (final NULL), but no need: */
 
-    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
+    png_ptr = png_create_write_struct(png_get_libpng_ver(NULL), mainprog_ptr,
       writepng_error_handler, NULL);
     if (!png_ptr)
         return 4;   /* out of memory */
@@ -104,7 +105,8 @@
     /* setjmp() must be called in every function that calls a PNG-writing
      * libpng function, unless an alternate error handler was installed--
      * but compatible error handlers must either use longjmp() themselves
-     * (as in this program) or exit immediately, so here we go: */
+     * (as in this program) or some other method to return control to
+     * application code, so here we go: */
 
     if (setjmp(mainprog_ptr->jmpbuf)) {
         png_destroy_write_struct(&png_ptr, &info_ptr);
@@ -388,5 +390,12 @@
         exit(99);
     }
 
+    /* Now we have our data structure we can use the information in it
+     * to return control to our own higher level code (all the points
+     * where 'setjmp' is called in this file.)  This will work with other
+     * error handling mechanisms as well - libpng always calls png_error
+     * when it can proceed no further, thus, so long as the error handler
+     * is intercepted, application code can do its own error recovery.
+     */
     longjmp(mainprog_ptr->jmpbuf, 1);
 }
diff --git a/contrib/libtests/fakepng.c b/contrib/libtests/fakepng.c
new file mode 100644
index 0000000..6512c14
--- /dev/null
+++ b/contrib/libtests/fakepng.c
@@ -0,0 +1,65 @@
+/* Fake a PNG - just write it out directly.
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2014.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ */
+
+#include <stdio.h>
+#include <zlib.h> /* for crc32 */
+
+void
+put_uLong(uLong val)
+{
+   putchar(val >> 24);
+   putchar(val >> 16);
+   putchar(val >>  8);
+   putchar(val >>  0);
+}
+
+void
+put_chunk(const unsigned char *chunk, uInt length)
+{
+   uLong crc;
+
+   put_uLong(length-4); /* Exclude the tag */
+
+   fwrite(chunk, length, 1, stdout);
+
+   crc = crc32(0, Z_NULL, 0);
+   put_uLong(crc32(crc, chunk, length));
+}
+
+const unsigned char signature[] =
+{
+   137, 80, 78, 71, 13, 10, 26, 10
+};
+
+const unsigned char IHDR[] =
+{
+   73, 72, 68, 82, /* IHDR */
+   0, 0, 0, 1, /* width */
+   0, 0, 0, 1, /* height */
+   1, /* bit depth */
+   0, /* color type: greyscale */
+   0, /* compression method */
+   0, /* filter method */
+   0  /* interlace method: none */
+};
+
+const unsigned char unknown[] =
+{
+   'u', 'n', 'K', 'n' /* "unKn" - private safe to copy */
+};
+
+int
+main(void)
+{
+   fwrite(signature, sizeof signature, 1, stdout);
+   put_chunk(IHDR, sizeof IHDR);
+
+   for (;;)
+      put_chunk(unknown, sizeof unknown);
+}
diff --git a/contrib/libtests/gentests.sh b/contrib/libtests/gentests.sh
new file mode 100755
index 0000000..f0f8d23
--- /dev/null
+++ b/contrib/libtests/gentests.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 John Cunningham Bowler
+#
+# Last changed in libpng 1.6.0 [February 14, 2013]
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+#
+# Generate a set of PNG test images.  The images are generated in a
+# sub-directory called 'tests' by default, however a command line argument will
+# change that name.  The generation requires a built version of makepng in the
+# current directory.
+#
+usage(){
+   exec >&2
+   echo "$0 [<directory>]"
+   echo '  Generate a set of PNG test files in "directory" ("tests" by default)'
+   exit 1
+}
+
+mp="$PWD/makepng"
+test -x "$mp" || {
+   exec >&2
+   echo "$0: the 'makepng' program must exist"
+   echo "  in the directory within which this program:"
+   echo "    $mp"
+   echo "  is executed"
+   usage
+}
+
+# Just one argument: the directory
+testdir="tests"
+test $# -gt 1 && {
+   testdir="$1"
+   shift
+}
+test $# -eq 0 || usage
+
+# Take care not to clobber something
+if test -e "$testdir"
+then
+   test -d "$testdir" || usage
+else
+   # mkdir -p isn't portable, so do the following
+   mkdir "$testdir" 2>/dev/null || mkdir -p "$testdir" || usage
+fi
+
+# This fails in a very satisfactory way if it's not accessible
+cd "$testdir"
+:>"test$$.png" || {
+   exec >&2
+   echo "$testdir: directory not writable"
+   usage
+}
+rm "test$$.png" || {
+   exec >&2
+   echo "$testdir: you have create but not write privileges here."
+   echo "  This is unexpected.  You have a spurion; "'"'"test$$.png"'"'"."
+   echo "  You need to remove this yourself.  Try a different directory."
+   exit 1
+}
+
+# Now call makepng ($mp) to create every file we can think of with a
+# reasonable name
+doit(){
+   for gamma in "" --sRGB --linear --1.8
+   do
+      case "$gamma" in
+         "")
+            gname=;;
+         --sRGB)
+            gname="-srgb";;
+         --linear)
+            gname="-lin";;
+         --1.8)
+            gname="-18";;
+         *)
+            gname="-$gamma";;
+      esac
+      "$mp" $gamma "$1" "$2" "test-$1-$2$gname.png"
+   done
+}
+#
+for ct in gray palette
+do
+   for bd in 1 2 4 8
+   do
+      doit "$ct" "$bd"
+   done
+done
+#
+doit "gray" "16"
+#
+for ct in gray-alpha rgb rgb-alpha
+do
+   for bd in 8 16
+   do
+      doit "$ct" "$bd"
+   done
+done
diff --git a/contrib/libtests/makepng.c b/contrib/libtests/makepng.c
new file mode 100644
index 0000000..60d96d1
--- /dev/null
+++ b/contrib/libtests/makepng.c
@@ -0,0 +1,1941 @@
+/* makepng.c */
+#define _ISOC99_SOURCE
+/* Copyright: */
+#define COPYRIGHT "\251 2013,2015 John Cunningham Bowler"
+/*
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Make a test PNG image.  The arguments are as follows:
+ *
+ *    makepng [--sRGB|--linear|--1.8] [--tRNS] [--nofilters] \
+ *       color-type bit-depth [file-name]
+ *
+ * The color-type may be numeric (and must match the numbers used by the PNG
+ * specification) or one of the format names listed below.  The bit-depth is the
+ * component bit depth, or the pixel bit-depth for a color-mapped image.
+ *
+ * Without any options no color-space information is written, with the options
+ * an sRGB or the appropriate gAMA chunk is written.  "1.8" refers to the
+ * display system used on older Apple computers to correct for high ambient
+ * light levels in the viewing environment; it applies a transform of
+ * approximately value^(1/1.45) to the color values and so a gAMA chunk of 65909
+ * is written (1.45/2.2).
+ *
+ * The image data is generated internally.  Unless --color is given the images
+ * used are as follows:
+ *
+ * 1 channel: a square image with a diamond, the least luminous colors are on
+ *    the edge of the image, the most luminous in the center.
+ *
+ * 2 channels: the color channel increases in luminosity from top to bottom, the
+ *    alpha channel increases in opacity from left to right.
+ *
+ * 3 channels: linear combinations of, from the top-left corner clockwise,
+ *    black, green, white, red.
+ *
+ * 4 channels: linear combinations of, from the top-left corner clockwise,
+ *    transparent, red, green, blue.
+ *
+ * For color-mapped images a four channel color-map is used and if --tRNS is
+ * given the PNG file has a tRNS chunk, as follows:
+ *
+ * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
+ * 2-bit: entry 0: transparent-green
+ *        entry 1: 40%-red
+ *        entry 2: 80%-blue
+ *        entry 3: opaque-white
+ * 4-bit: the 16 combinations of the 2-bit case
+ * 8-bit: the 256 combinations of the 4-bit case
+ *
+ * The palette always has 2^bit-depth entries and the tRNS chunk one fewer.  The
+ * image is the 1-channel diamond, but using palette index, not luminosity.
+ *
+ * For formats other than color-mapped ones if --tRNS is specified a tRNS chunk
+ * is generated with all channels equal to the low bits of 0x0101.
+ *
+ * Image size is determined by the final pixel depth in bits, i.e. channels x
+ * bit-depth, as follows:
+ *
+ * 8 bits or less:    64x64
+ * 16 bits:           256x256
+ * More than 16 bits: 1024x1024
+ *
+ * Row filtering is the libpng default but may be turned off (the 'none' filter
+ * is used on every row) with the --nofilters option.
+ *
+ * The images are not interlaced.
+ *
+ * If file-name is given then the PNG is written to that file, else it is
+ * written to stdout.  Notice that stdout is not supported on systems where, by
+ * default, it assumes text output; this program makes no attempt to change the
+ * text mode of stdout!
+ *
+ *    makepng --color=<color> ...
+ *
+ * If --color is given then the whole image has that color, color-mapped images
+ * will have exactly one palette entry and all image files with be 16x16 in
+ * size.  The color value is 1 to 4 decimal numbers as appropriate for the color
+ * type.
+ *
+ *    makepng --small ...
+ *
+ * If --small is given the images are no larger than required to include every
+ * possible pixel value for the format.
+ *
+ * For formats with pixels 8 bits or fewer in size the images consist of a
+ * single row with 2^pixel-depth pixels, one of every possible value.
+ *
+ * For formats with 16-bit pixels a 256x256 image is generated containing every
+ * possible pixel value.
+ *
+ * For larger pixel sizes a 256x256 image is generated where the first row
+ * consists of each pixel that has identical byte values throughout the pixel
+ * followed by rows where the byte values differ within the pixel.
+ *
+ * In all cases the pixel values are arranged in such a way that the SUB and UP
+ * filters give byte sequences for maximal zlib compression.  By default (if
+ * --nofilters is not given) the SUB filter is used on the first row and the UP
+ * filter on all following rows.
+ *
+ * The --small option is meant to provide good test-case coverage, however the
+ * images are not easy to examine visually.  Without the --small option the
+ * images contain identical color values; the pixel values are adjusted
+ * according to the gamma encoding with no gamma encoding being interpreted as
+ * sRGB.
+ *
+ * LICENSING
+ * =========
+ *
+ * This code is copyright of the authors, see the COPYRIGHT define above.  The
+ * code is licensed as above, using the libpng license.  The code generates
+ * images which are solely the product of the code; the options choose which of
+ * the many possibilities to generate.  The images that result (but not the code
+ * which generates them) are licensed as defined here:
+ *
+ * IMPORTANT: the COPYRIGHT #define must contain ISO-Latin-1 characters, the
+ * IMAGE_LICENSING #define must contain UTF-8 characters.  The 'copyright'
+ * symbol 0xA9U (\251) in ISO-Latin-1 encoding and 0xC20xA9 (\302\251) in UTF-8.
+ */
+#define IMAGE_LICENSING "Dedicated to the public domain per Creative Commons "\
+    "license \"CC0 1.0\"; https://creativecommons.org/publicdomain/zero/1.0/"
+
+#include <stddef.h> /* for offsetof */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdint.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+#include <zlib.h>
+
+/* Work round for GCC complaints about casting a (double) function result to
+ * an unsigned:
+ */
+static unsigned int
+flooru(double d)
+{
+   d = floor(d);
+   return (unsigned int)d;
+}
+
+static png_byte
+floorb(double d)
+{
+   d = floor(d);
+   return (png_byte)d;
+}
+
+/* This structure is used for inserting extra chunks (the --insert argument, not
+ * documented above.)
+ */
+typedef struct chunk_insert
+{
+   struct chunk_insert *next;
+   void               (*insert)(png_structp, png_infop, int, png_charpp);
+   int                  nparams;
+   png_charp            parameters[1];
+} chunk_insert;
+
+static unsigned int
+channels_of_type(int color_type)
+{
+   if (color_type & PNG_COLOR_MASK_PALETTE)
+      return 1;
+
+   else
+   {
+      int channels = 1;
+
+      if (color_type & PNG_COLOR_MASK_COLOR)
+         channels = 3;
+
+      if (color_type & PNG_COLOR_MASK_ALPHA)
+         return channels + 1;
+
+      else
+         return channels;
+   }
+}
+
+static unsigned int
+pixel_depth_of_type(int color_type, int bit_depth)
+{
+   return channels_of_type(color_type) * bit_depth;
+}
+
+static unsigned int
+image_size_of_type(int color_type, int bit_depth, unsigned int *colors,
+   int small)
+{
+   if (*colors)
+      return 16;
+
+   else
+   {
+      int pixel_depth = pixel_depth_of_type(color_type, bit_depth);
+
+      if (small)
+      {
+         if (pixel_depth <= 8) /* there will be one row */
+            return 1 << pixel_depth;
+
+         else
+            return 256;
+      }
+
+      else if (pixel_depth < 8)
+         return 64;
+
+      else if (pixel_depth > 16)
+         return 1024;
+
+      else
+         return 256;
+   }
+}
+
+static void
+set_color(png_colorp color, png_bytep trans, unsigned int red,
+   unsigned int green, unsigned int blue, unsigned int alpha,
+   png_const_bytep gamma_table)
+{
+   color->red = gamma_table[red];
+   color->green = gamma_table[green];
+   color->blue = gamma_table[blue];
+   *trans = (png_byte)alpha;
+}
+
+static int
+generate_palette(png_colorp palette, png_bytep trans, int bit_depth,
+   png_const_bytep gamma_table, unsigned int *colors)
+{
+   /*
+    * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
+    * 2-bit: entry 0: transparent-green
+    *        entry 1: 40%-red
+    *        entry 2: 80%-blue
+    *        entry 3: opaque-white
+    * 4-bit: the 16 combinations of the 2-bit case
+    * 8-bit: the 256 combinations of the 4-bit case
+    */
+   switch (colors[0])
+   {
+      default:
+         fprintf(stderr, "makepng: --colors=...: invalid count %u\n",
+            colors[0]);
+         exit(1);
+
+      case 1:
+         set_color(palette+0, trans+0, colors[1], colors[1], colors[1], 255,
+            gamma_table);
+         return 1;
+
+      case 2:
+         set_color(palette+0, trans+0, colors[1], colors[1], colors[1],
+            colors[2], gamma_table);
+         return 1;
+
+      case 3:
+         set_color(palette+0, trans+0, colors[1], colors[2], colors[3], 255,
+            gamma_table);
+         return 1;
+
+      case 4:
+         set_color(palette+0, trans+0, colors[1], colors[2], colors[3],
+            colors[4], gamma_table);
+         return 1;
+
+      case 0:
+         if (bit_depth == 1)
+         {
+            set_color(palette+0, trans+0, 255, 0, 0, 0, gamma_table);
+            set_color(palette+1, trans+1, 255, 255, 255, 255, gamma_table);
+            return 2;
+         }
+
+         else
+         {
+            unsigned int size = 1U << (bit_depth/2); /* 2, 4 or 16 */
+            unsigned int x, y;
+            volatile unsigned int ip = 0;
+
+            for (x=0; x<size; ++x) for (y=0; y<size; ++y)
+            {
+               ip = x + (size * y);
+
+               /* size is at most 16, so the scaled value below fits in 16 bits
+                */
+#              define interp(pos, c1, c2) ((pos * c1) + ((size-pos) * c2))
+#              define xyinterp(x, y, c1, c2, c3, c4) (((size * size / 2) +\
+                  (interp(x, c1, c2) * y + (size-y) * interp(x, c3, c4))) /\
+                  (size*size))
+
+               set_color(palette+ip, trans+ip,
+                  /* color:    green, red,blue,white */
+                  xyinterp(x, y,   0, 255,   0, 255),
+                  xyinterp(x, y, 255,   0,   0, 255),
+                  xyinterp(x, y,   0,   0, 255, 255),
+                  /* alpha:        0, 102, 204, 255) */
+                  xyinterp(x, y,   0, 102, 204, 255),
+                  gamma_table);
+            }
+
+            return ip+1;
+         }
+   }
+}
+
+static void
+set_value(png_bytep row, size_t rowbytes, png_uint_32 x, unsigned int bit_depth,
+   png_uint_32 value, png_const_bytep gamma_table, double conv)
+{
+   unsigned int mask = (1U << bit_depth)-1;
+
+   x *= bit_depth;  /* Maximum x is 4*1024, maximum bit_depth is 16 */
+
+   if (value <= mask)
+   {
+      png_uint_32 offset = x >> 3;
+
+      if (offset < rowbytes && (bit_depth < 16 || offset+1 < rowbytes))
+      {
+         row += offset;
+
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+               /* Don't gamma correct - values get smashed */
+               {
+                  unsigned int shift = (8 - bit_depth) - (x & 0x7U);
+
+                  mask <<= shift;
+                  value = (value << shift) & mask;
+                  *row = (png_byte)((*row & ~mask) | value);
+               }
+               return;
+
+            default:
+               fprintf(stderr, "makepng: bad bit depth (internal error)\n");
+               exit(1);
+
+            case 16:
+               value = flooru(65535*pow(value/65535.,conv)+.5);
+               *row++ = (png_byte)(value >> 8);
+               *row = (png_byte)value;
+               return;
+
+            case 8:
+               *row = gamma_table[value];
+               return;
+         }
+      }
+
+      else
+      {
+         fprintf(stderr, "makepng: row buffer overflow (internal error)\n");
+         exit(1);
+      }
+   }
+
+   else
+   {
+      fprintf(stderr, "makepng: component overflow (internal error)\n");
+      exit(1);
+   }
+}
+
+static int /* filter mask for row */
+generate_row(png_bytep row, size_t rowbytes, unsigned int y, int color_type,
+   int bit_depth, png_const_bytep gamma_table, double conv,
+   unsigned int *colors, int small)
+{
+   int filters = 0; /* file *MASK*, 0 means the default, not NONE */
+   png_uint_32 size_max =
+      image_size_of_type(color_type, bit_depth, colors, small)-1;
+   png_uint_32 depth_max = (1U << bit_depth)-1; /* up to 65536 */
+
+   if (colors[0] == 0) if (small)
+   {
+      unsigned int pixel_depth = pixel_depth_of_type(color_type, bit_depth);
+
+      /* For pixel depths less than 16 generate a single row containing all the
+       * possible pixel values.  For 16 generate all 65536 byte pair
+       * combinations in a 256x256 pixel array.
+       */
+      switch (pixel_depth)
+      {
+         case 1:
+            assert(y == 0 && rowbytes == 1 && size_max == 1);
+            row[0] = 0x6CU; /* binary: 01101100, only top 2 bits used */
+            filters = PNG_FILTER_NONE;
+            break;
+
+         case 2:
+            assert(y == 0 && rowbytes == 1 && size_max == 3);
+            row[0] = 0x1BU; /* binary 00011011, all bits used */
+            filters = PNG_FILTER_NONE;
+            break;
+
+         case 4:
+            assert(y == 0 && rowbytes == 8 && size_max == 15);
+            row[0] = 0x01U;
+            row[1] = 0x23U; /* SUB gives 0x22U for all following bytes */
+            row[2] = 0x45U;
+            row[3] = 0x67U;
+            row[4] = 0x89U;
+            row[5] = 0xABU;
+            row[6] = 0xCDU;
+            row[7] = 0xEFU;
+            filters = PNG_FILTER_SUB;
+            break;
+
+         case 8:
+            /* The row will have all the pixel values in order starting with
+             * '1', the SUB filter will change every byte into '1' (including
+             * the last, which generates pixel value '0').  Since the SUB filter
+             * has value 1 this should result in maximum compression.
+             */
+            assert(y == 0 && rowbytes == 256 && size_max == 255);
+            for (;;)
+            {
+               row[size_max] = 0xFFU & (size_max+1);
+               if (size_max == 0)
+                  break;
+               --size_max;
+            }
+            filters = PNG_FILTER_SUB;
+            break;
+
+         case 16:
+            /* Rows are generated such that each row has a constant difference
+             * between the first and second byte of each pixel and so that the
+             * difference increases by 1 at each row.  The rows start with the
+             * first byte value of 0 and the value increases to 255 across the
+             * row.
+             *
+             * The difference starts at 1, so the first row is:
+             *
+             *     0 1 1 2 2 3 3 4 ... 254 255 255 0
+             *
+             * This means that running the SUB filter on the first row produces:
+             *
+             *   [SUB==1] 0 1 0 1 0 1...
+             *
+             * Then the difference is 2 on the next row, giving:
+             *
+             *    0 2 1 3 2 4 3 5 ... 254 0 255 1
+             *
+             * When the UP filter is run on this libpng produces:
+             *
+             *   [UP ==2] 0 1 0 1 0 1...
+             *
+             * And so on for all the remain rows to the final two * rows:
+             *
+             *    row 254: 0 255 1 0 2 1 3 2 4 3 ... 254 253 255 254
+             *    row 255: 0   0 1 1 2 2 3 3 4 4 ... 254 254 255 255
+             */
+            assert(rowbytes == 512 && size_max == 255);
+            for (;;)
+            {
+               row[2*size_max  ] = 0xFFU & size_max;
+               row[2*size_max+1] = 0xFFU & (size_max+y+1);
+               if (size_max == 0)
+                  break;
+               --size_max;
+            }
+            /* The first row must include PNG_FILTER_UP so that libpng knows we
+             * need to keep it for the following row:
+             */
+            filters = (y == 0 ? PNG_FILTER_SUB+PNG_FILTER_UP : PNG_FILTER_UP);
+            break;
+
+         case 24:
+         case 32:
+         case 48:
+         case 64:
+            /* The rows are filled by an alogorithm similar to the above, in the
+             * first row pixel bytes are all equal, increasing from 0 by 1 for
+             * each pixel.  In the second row the bytes within a pixel are
+             * incremented 1,3,5,7,... from the previous row byte.  Using an odd
+             * number ensures all the possible byte values are used.
+             */
+            assert(size_max == 255 && rowbytes == 256*(pixel_depth>>3));
+            pixel_depth >>= 3; /* now in bytes */
+            while (rowbytes > 0)
+            {
+               const size_t pixel_index = --rowbytes/pixel_depth;
+
+               if (y == 0)
+                  row[rowbytes] = 0xFFU & pixel_index;
+
+               else
+               {
+                  const size_t byte_offset =
+                     rowbytes - pixel_index * pixel_depth;
+
+                  row[rowbytes] =
+                     0xFFU & (pixel_index + (byte_offset * 2*y) + 1);
+               }
+            }
+            filters = (y == 0 ? PNG_FILTER_SUB+PNG_FILTER_UP : PNG_FILTER_UP);
+            break;
+
+         default:
+            assert(0/*NOT REACHED*/);
+      }
+   }
+
+   else switch (channels_of_type(color_type))
+   {
+   /* 1 channel: a square image with a diamond, the least luminous colors are on
+    *    the edge of the image, the most luminous in the center.
+    */
+      case 1:
+         {
+            png_uint_32 x;
+            png_uint_32 base = 2*size_max - abs(2*y-size_max);
+
+            for (x=0; x<=size_max; ++x)
+            {
+               png_uint_32 luma = base - abs(2*x-size_max);
+
+               /* 'luma' is now in the range 0..2*size_max, we need
+                * 0..depth_max
+                */
+               luma = (luma*depth_max + size_max) / (2*size_max);
+               set_value(row, rowbytes, x, bit_depth, luma, gamma_table, conv);
+            }
+         }
+         break;
+
+   /* 2 channels: the color channel increases in luminosity from top to bottom,
+    *    the alpha channel increases in opacity from left to right.
+    */
+      case 2:
+         {
+            png_uint_32 alpha = (depth_max * y * 2 + size_max) / (2 * size_max);
+            png_uint_32 x;
+
+            for (x=0; x<=size_max; ++x)
+            {
+               set_value(row, rowbytes, 2*x, bit_depth,
+                  (depth_max * x * 2 + size_max) / (2 * size_max), gamma_table,
+                  conv);
+               set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
+                  conv);
+            }
+         }
+         break;
+
+   /* 3 channels: linear combinations of, from the top-left corner clockwise,
+    *    black, green, white, red.
+    */
+      case 3:
+         {
+            /* x0: the black->red scale (the value of the red component) at the
+             *     start of the row (blue and green are 0).
+             * x1: the green->white scale (the value of the red and blue
+             *     components at the end of the row; green is depth_max).
+             */
+            png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
+            png_uint_32 x;
+
+            /* Interpolate x/depth_max from start to end:
+             *
+             *        start end         difference
+             * red:     Y    Y            0
+             * green:   0   depth_max   depth_max
+             * blue:    0    Y            Y
+             */
+            for (x=0; x<=size_max; ++x)
+            {
+               set_value(row, rowbytes, 3*x+0, bit_depth, /* red */ Y,
+                     gamma_table, conv);
+               set_value(row, rowbytes, 3*x+1, bit_depth, /* green */
+                  (depth_max * x * 2 + size_max) / (2 * size_max),
+                  gamma_table, conv);
+               set_value(row, rowbytes, 3*x+2, bit_depth, /* blue */
+                  (Y * x * 2 + size_max) / (2 * size_max),
+                  gamma_table, conv);
+            }
+         }
+         break;
+
+   /* 4 channels: linear combinations of, from the top-left corner clockwise,
+    *    transparent, red, green, blue.
+    */
+      case 4:
+         {
+            /* x0: the transparent->blue scale (the value of the blue and alpha
+             *     components) at the start of the row (red and green are 0).
+             * x1: the red->green scale (the value of the red and green
+             *     components at the end of the row; blue is 0 and alpha is
+             *     depth_max).
+             */
+            png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
+            png_uint_32 x;
+
+            /* Interpolate x/depth_max from start to end:
+             *
+             *        start    end       difference
+             * red:     0   depth_max-Y depth_max-Y
+             * green:   0       Y             Y
+             * blue:    Y       0            -Y
+             * alpha:   Y    depth_max  depth_max-Y
+             */
+            for (x=0; x<=size_max; ++x)
+            {
+               set_value(row, rowbytes, 4*x+0, bit_depth, /* red */
+                  ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
+                  gamma_table, conv);
+               set_value(row, rowbytes, 4*x+1, bit_depth, /* green */
+                  (Y * x * 2 + size_max) / (2 * size_max),
+                  gamma_table, conv);
+               set_value(row, rowbytes, 4*x+2, bit_depth, /* blue */
+                  Y - (Y * x * 2 + size_max) / (2 * size_max),
+                  gamma_table, conv);
+               set_value(row, rowbytes, 4*x+3, bit_depth, /* alpha */
+                  Y + ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
+                  gamma_table, conv);
+            }
+         }
+         break;
+
+      default:
+         fprintf(stderr, "makepng: internal bad channel count\n");
+         exit(2);
+   }
+
+   else if (color_type & PNG_COLOR_MASK_PALETTE)
+   {
+      /* Palette with fixed color: the image rows are all 0 and the image width
+       * is 16.
+       */
+      memset(row, 0, rowbytes);
+   }
+
+   else if (colors[0] == channels_of_type(color_type))
+      switch (channels_of_type(color_type))
+      {
+         case 1:
+            {
+               const png_uint_32 luma = colors[1];
+               png_uint_32 x;
+
+               for (x=0; x<=size_max; ++x)
+                  set_value(row, rowbytes, x, bit_depth, luma, gamma_table,
+                     conv);
+            }
+            break;
+
+         case 2:
+            {
+               const png_uint_32 luma = colors[1];
+               const png_uint_32 alpha = colors[2];
+               png_uint_32 x;
+
+               for (x=0; x<size_max; ++x)
+               {
+                  set_value(row, rowbytes, 2*x, bit_depth, luma, gamma_table,
+                     conv);
+                  set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
+                     conv);
+               }
+            }
+            break;
+
+         case 3:
+            {
+               const png_uint_32 red = colors[1];
+               const png_uint_32 green = colors[2];
+               const png_uint_32 blue = colors[3];
+               png_uint_32 x;
+
+               for (x=0; x<=size_max; ++x)
+               {
+                  set_value(row, rowbytes, 3*x+0, bit_depth, red, gamma_table,
+                     conv);
+                  set_value(row, rowbytes, 3*x+1, bit_depth, green, gamma_table,
+                     conv);
+                  set_value(row, rowbytes, 3*x+2, bit_depth, blue, gamma_table,
+                     conv);
+               }
+            }
+            break;
+
+         case 4:
+            {
+               const png_uint_32 red = colors[1];
+               const png_uint_32 green = colors[2];
+               const png_uint_32 blue = colors[3];
+               const png_uint_32 alpha = colors[4];
+               png_uint_32 x;
+
+               for (x=0; x<=size_max; ++x)
+               {
+                  set_value(row, rowbytes, 4*x+0, bit_depth, red, gamma_table,
+                     conv);
+                  set_value(row, rowbytes, 4*x+1, bit_depth, green, gamma_table,
+                     conv);
+                  set_value(row, rowbytes, 4*x+2, bit_depth, blue, gamma_table,
+                     conv);
+                  set_value(row, rowbytes, 4*x+3, bit_depth, alpha, gamma_table,
+                     conv);
+               }
+            }
+         break;
+
+         default:
+            fprintf(stderr, "makepng: internal bad channel count\n");
+            exit(2);
+      }
+
+   else
+   {
+      fprintf(stderr,
+         "makepng: --color: count(%u) does not match channels(%u)\n",
+         colors[0], channels_of_type(color_type));
+      exit(1);
+   }
+
+   return filters;
+}
+
+
+static void PNGCBAPI
+makepng_warning(png_structp png_ptr, png_const_charp message)
+{
+   const char **ep = png_get_error_ptr(png_ptr);
+   const char *name;
+
+   if (ep != NULL && *ep != NULL)
+      name = *ep;
+
+   else
+      name = "makepng";
+
+  fprintf(stderr, "%s: warning: %s\n", name, message);
+}
+
+static void PNGCBAPI
+makepng_error(png_structp png_ptr, png_const_charp message)
+{
+   makepng_warning(png_ptr, message);
+   png_longjmp(png_ptr, 1);
+}
+
+static int /* 0 on success, else an error code */
+write_png(const char **name, FILE *fp, int color_type, int bit_depth,
+   volatile png_fixed_point gamma, chunk_insert * volatile insert,
+   unsigned int filters, unsigned int *colors, int small, int tRNS)
+{
+   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+      name, makepng_error, makepng_warning);
+   volatile png_infop info_ptr = NULL;
+   volatile png_bytep row = NULL;
+
+   if (png_ptr == NULL)
+   {
+      fprintf(stderr, "makepng: OOM allocating write structure\n");
+      return 1;
+   }
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      png_structp nv_ptr = png_ptr;
+      png_infop nv_info = info_ptr;
+
+      png_ptr = NULL;
+      info_ptr = NULL;
+      png_destroy_write_struct(&nv_ptr, &nv_info);
+      if (row != NULL) free(row);
+      return 1;
+   }
+
+   /* Allow benign errors so that we can write PNGs with errors */
+   png_set_benign_errors(png_ptr, 1/*allowed*/);
+
+   /* Max out the text compression level in an attempt to make the license
+    * small.   If --small then do the same for the IDAT.
+    */
+   if (small)
+      png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
+
+   png_set_text_compression_level(png_ptr, Z_BEST_COMPRESSION);
+
+   png_init_io(png_ptr, fp);
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+      png_error(png_ptr, "OOM allocating info structure");
+
+   {
+      const unsigned int size =
+         image_size_of_type(color_type, bit_depth, colors, small);
+      unsigned int ysize;
+      png_fixed_point real_gamma = 45455; /* For sRGB */
+      png_byte gamma_table[256];
+      double conv;
+
+      /* Normally images are square, but with 'small' we want to simply generate
+       * all the pixel values, or all that we reasonably can:
+       */
+      if (small)
+      {
+         const unsigned int pixel_depth =
+            pixel_depth_of_type(color_type, bit_depth);
+
+         if (pixel_depth <= 8U)
+         {
+            assert(size == (1U<<pixel_depth));
+            ysize = 1U;
+         }
+
+         else
+         {
+            assert(size == 256U);
+            ysize = 256U;
+         }
+      }
+
+      else
+         ysize = size;
+
+      /* This function uses the libpng values used on read to carry extra
+       * information about the gamma:
+       */
+      if (gamma == PNG_GAMMA_MAC_18)
+         gamma = 65909;
+
+      else if (gamma > 0 && gamma < 1000)
+         gamma = PNG_FP_1;
+
+      if (gamma > 0)
+         real_gamma = gamma;
+
+      {
+         unsigned int i;
+
+         if (real_gamma == 45455) for (i=0; i<256; ++i)
+         {
+            gamma_table[i] = (png_byte)i;
+            conv = 1.;
+         }
+
+         else
+         {
+            /* Convert 'i' from sRGB (45455) to real_gamma, this makes
+             * the images look the same regardless of the gAMA chunk.
+             */
+            conv = real_gamma;
+            conv /= 45455;
+
+            gamma_table[0] = 0;
+
+            for (i=1; i<255; ++i)
+               gamma_table[i] = floorb(pow(i/255.,conv) * 255 + .5);
+
+            gamma_table[255] = 255;
+         }
+      }
+
+      png_set_IHDR(png_ptr, info_ptr, size, ysize, bit_depth, color_type,
+         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+      if (color_type & PNG_COLOR_MASK_PALETTE)
+      {
+         int npalette;
+         png_color palette[256];
+         png_byte trans[256];
+
+         npalette = generate_palette(palette, trans, bit_depth, gamma_table,
+            colors);
+         png_set_PLTE(png_ptr, info_ptr, palette, npalette);
+
+         if (tRNS)
+            png_set_tRNS(png_ptr, info_ptr, trans, npalette-1,
+               NULL/*transparent color*/);
+
+         /* Reset gamma_table to prevent the image rows being changed */
+         for (npalette=0; npalette<256; ++npalette)
+            gamma_table[npalette] = (png_byte)npalette;
+      }
+
+      else if (tRNS)
+      {
+         png_color_16 col;
+
+         col.red = col.green = col.blue = col.gray =
+            0x0101U & ((1U<<bit_depth)-1U);
+         col.index = 0U;
+         png_set_tRNS(png_ptr, info_ptr, NULL/*trans*/, 1U, &col);
+      }
+
+      if (gamma == PNG_DEFAULT_sRGB)
+         png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
+
+      else if (gamma > 0) /* Else don't set color space information */
+      {
+         png_set_gAMA_fixed(png_ptr, info_ptr, real_gamma);
+
+         /* Just use the sRGB values here. */
+         png_set_cHRM_fixed(png_ptr, info_ptr,
+            /* color      x       y */
+            /* white */ 31270, 32900,
+            /* red   */ 64000, 33000,
+            /* green */ 30000, 60000,
+            /* blue  */ 15000,  6000
+         );
+      }
+
+      /* Insert extra information. */
+      while (insert != NULL)
+      {
+         insert->insert(png_ptr, info_ptr, insert->nparams, insert->parameters);
+         insert = insert->next;
+      }
+
+      /* Write the file header. */
+      png_write_info(png_ptr, info_ptr);
+
+      /* Restrict the filters */
+      png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters);
+
+      {
+#        ifdef PNG_WRITE_INTERLACING_SUPPORTED
+            int passes = png_set_interlace_handling(png_ptr);
+#        else /* !WRITE_INTERLACING */
+            int passes = 1;
+#        endif /* !WRITE_INTERLACING */
+         int pass;
+         png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+         row = malloc(rowbytes);
+
+         if (row == NULL)
+            png_error(png_ptr, "OOM allocating row buffer");
+
+         for (pass = 0; pass < passes; ++pass)
+         {
+            unsigned int y;
+
+            for (y=0; y<ysize; ++y)
+            {
+               unsigned int row_filters =
+                  generate_row(row, rowbytes, y, color_type, bit_depth,
+                        gamma_table, conv, colors, small);
+
+               if (row_filters != 0 && filters == PNG_ALL_FILTERS)
+                  png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, row_filters);
+
+               png_write_row(png_ptr, row);
+            }
+         }
+      }
+   }
+
+   /* Finish writing the file. */
+   png_write_end(png_ptr, info_ptr);
+
+   {
+      png_structp nv_ptr = png_ptr;
+      png_infop nv_info = info_ptr;
+
+      png_ptr = NULL;
+      info_ptr = NULL;
+      png_destroy_write_struct(&nv_ptr, &nv_info);
+   }
+   free(row);
+   return 0;
+}
+
+
+static size_t
+load_file(png_const_charp name, png_bytepp result)
+{
+   FILE *fp = tmpfile();
+
+   if (fp != NULL)
+   {
+      FILE *ip = fopen(name, "rb");
+
+      if (ip != NULL)
+      {
+         size_t total = 0;
+         int ch;
+
+         for (;;)
+         {
+            ch = getc(ip);
+            if (ch == EOF) break;
+            putc(ch, fp);
+            ++total;
+         }
+
+         if (ferror(ip))
+         {
+            perror(name);
+            fprintf(stderr, "%s: read error\n", name);
+            (void)fclose(ip);
+         }
+
+         else
+         {
+            (void)fclose(ip);
+
+            if (ferror(fp))
+            {
+               perror("temporary file");
+               fprintf(stderr, "temporary file write error\n");
+            }
+
+            else
+            {
+               rewind(fp);
+
+               if (total > 0)
+               {
+                  /* Round up to a multiple of 4 here to allow an iCCP profile
+                   * to be padded to a 4x boundary.
+                   */
+                  png_bytep data = malloc((total+3)&~3);
+
+                  if (data != NULL)
+                  {
+                     size_t new_size = 0;
+
+                     for (;;)
+                     {
+                        ch = getc(fp);
+                        if (ch == EOF) break;
+                        data[new_size++] = (png_byte)ch;
+                     }
+
+                     if (ferror(fp) || new_size != total)
+                     {
+                        perror("temporary file");
+                        fprintf(stderr, "temporary file read error\n");
+                        free(data);
+                     }
+
+                     else
+                     {
+                        (void)fclose(fp);
+                        *result = data;
+                        return total;
+                     }
+                  }
+
+                  else
+                     fprintf(stderr, "%s: out of memory loading file\n", name);
+               }
+
+               else
+                  fprintf(stderr, "%s: empty file\n", name);
+            }
+         }
+      }
+
+      else
+      {
+         perror(name);
+         fprintf(stderr, "%s: open failed\n", name);
+      }
+
+      fclose(fp);
+   }
+
+   else
+      fprintf(stderr, "makepng: %s: could not open temporary file\n", name);
+
+   exit(1);
+   return 0;
+}
+
+static png_size_t
+load_fake(png_charp param, png_bytepp profile)
+{
+   char *endptr = NULL;
+   uint64_t size = strtoull(param, &endptr, 0/*base*/);
+
+   /* The 'fake' format is <number>*[string] */
+   if (endptr != NULL && *endptr == '*')
+   {
+      size_t len = strlen(++endptr);
+      size_t result = (size_t)size;
+
+      if (len == 0) len = 1; /* capture the terminating '\0' */
+
+      /* Now repeat that string to fill 'size' bytes. */
+      if (result == size && (*profile = malloc(result)) != NULL)
+      {
+         png_bytep out = *profile;
+
+         if (len == 1)
+            memset(out, *endptr, result);
+
+         else
+         {
+            while (size >= len)
+            {
+               memcpy(out, endptr, len);
+               out += len;
+               size -= len;
+            }
+            memcpy(out, endptr, size);
+         }
+
+         return result;
+      }
+
+      else
+      {
+         fprintf(stderr, "%s: size exceeds system limits\n", param);
+         exit(1);
+      }
+   }
+
+   return 0;
+}
+
+static void
+check_param_count(int nparams, int expect)
+{
+   if (nparams != expect)
+   {
+      fprintf(stderr, "bad parameter count (internal error)\n");
+      exit(1);
+   }
+}
+
+static void
+insert_iCCP(png_structp png_ptr, png_infop info_ptr, int nparams,
+   png_charpp params)
+{
+   png_bytep profile = NULL;
+   png_uint_32 proflen = 0;
+   int result;
+
+   check_param_count(nparams, 2);
+
+   switch (params[1][0])
+   {
+      case '<':
+         {
+            png_size_t filelen = load_file(params[1]+1, &profile);
+            if (filelen > 0xfffffffc) /* Maximum profile length */
+            {
+               fprintf(stderr, "%s: file too long (%lu) for an ICC profile\n",
+                  params[1]+1, (unsigned long)filelen);
+               exit(1);
+            }
+
+            proflen = (png_uint_32)filelen;
+         }
+         break;
+
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+         {
+            png_size_t fake_len = load_fake(params[1], &profile);
+
+            if (fake_len > 0) /* else a simple parameter */
+            {
+               if (fake_len > 0xffffffff) /* Maximum profile length */
+               {
+                  fprintf(stderr,
+                     "%s: fake data too long (%lu) for an ICC profile\n",
+                     params[1], (unsigned long)fake_len);
+                  exit(1);
+               }
+               proflen = (png_uint_32)(fake_len & ~3U);
+               /* Always fix up the profile length. */
+               png_save_uint_32(profile, proflen);
+               break;
+            }
+         }
+
+      default:
+         fprintf(stderr, "--insert iCCP \"%s\": unrecognized\n", params[1]);
+         fprintf(stderr, "  use '<' to read a file: \"<filename\"\n");
+         exit(1);
+   }
+
+   result = 1;
+
+   if (proflen & 3)
+   {
+      fprintf(stderr,
+         "makepng: --insert iCCP %s: profile length made a multiple of 4\n",
+         params[1]);
+
+      /* load_file allocates extra space for this padding, the ICC spec requires
+       * padding with zero bytes.
+       */
+      while (proflen & 3)
+         profile[proflen++] = 0;
+   }
+
+   if (profile != NULL && proflen > 3)
+   {
+      png_uint_32 prof_header = png_get_uint_32(profile);
+
+      if (prof_header != proflen)
+      {
+         fprintf(stderr, "--insert iCCP %s: profile length field wrong:\n",
+            params[1]);
+         fprintf(stderr, "  actual %lu, recorded value %lu (corrected)\n",
+            (unsigned long)proflen, (unsigned long)prof_header);
+         png_save_uint_32(profile, proflen);
+      }
+   }
+
+   if (result && profile != NULL && proflen >=4)
+      png_set_iCCP(png_ptr, info_ptr, params[0], PNG_COMPRESSION_TYPE_BASE,
+         profile, proflen);
+
+   if (profile)
+      free(profile);
+
+   if (!result)
+      exit(1);
+}
+
+static void
+clear_text(png_text *text, png_charp keyword)
+{
+   text->compression = -1; /* none */
+   text->key = keyword;
+   text->text = NULL;
+   text->text_length = 0; /* libpng calculates this */
+   text->itxt_length = 0; /* libpng calculates this */
+   text->lang = NULL;
+   text->lang_key = NULL;
+}
+
+static void
+set_text(png_structp png_ptr, png_infop info_ptr, png_textp text,
+   png_charp param)
+{
+   switch (param[0])
+   {
+      case '<':
+         {
+            png_bytep file = NULL;
+
+            text->text_length = load_file(param+1, &file);
+            text->text = (png_charp)file;
+         }
+         break;
+
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+         {
+            png_bytep data = NULL;
+            png_size_t fake_len = load_fake(param, &data);
+
+            if (fake_len > 0) /* else a simple parameter */
+            {
+               text->text_length = fake_len;
+               text->text = (png_charp)data;
+               break;
+            }
+         }
+
+      default:
+         text->text = param;
+         break;
+   }
+
+   png_set_text(png_ptr, info_ptr, text, 1);
+
+   if (text->text != param)
+      free(text->text);
+}
+
+static void
+insert_tEXt(png_structp png_ptr, png_infop info_ptr, int nparams,
+   png_charpp params)
+{
+   png_text text;
+
+   check_param_count(nparams, 2);
+   clear_text(&text, params[0]);
+   set_text(png_ptr, info_ptr, &text, params[1]);
+}
+
+static void
+insert_zTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
+   png_charpp params)
+{
+   png_text text;
+
+   check_param_count(nparams, 2);
+   clear_text(&text, params[0]);
+   text.compression = 0; /* deflate */
+   set_text(png_ptr, info_ptr, &text, params[1]);
+}
+
+static void
+insert_iTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
+   png_charpp params)
+{
+   png_text text;
+
+   check_param_count(nparams, 4);
+   clear_text(&text, params[0]);
+   text.compression = 2; /* iTXt + deflate */
+   text.lang = params[1];/* language tag */
+   text.lang_key = params[2]; /* translated keyword */
+   set_text(png_ptr, info_ptr, &text, params[3]);
+}
+
+static void
+insert_hIST(png_structp png_ptr, png_infop info_ptr, int nparams,
+      png_charpp params)
+{
+   int i;
+   png_uint_16 freq[256];
+
+   /* libpng takes the count from the PLTE count; we don't check it here but we
+    * do set the array to 0 for unspecified entries.
+    */
+   memset(freq, 0, sizeof freq);
+   for (i=0; i<nparams; ++i)
+   {
+      char *endptr = NULL;
+      unsigned long int l = strtoul(params[i], &endptr, 0/*base*/);
+
+      if (params[i][0] && *endptr == 0 && l <= 65535)
+         freq[i] = (png_uint_16)l;
+
+      else
+      {
+         fprintf(stderr, "hIST[%d]: %s: invalid frequency\n", i, params[i]);
+         exit(1);
+      }
+   }
+
+   png_set_hIST(png_ptr, info_ptr, freq);
+}
+
+static png_byte
+bval(png_const_structrp png_ptr, png_charp param, unsigned int maxval)
+{
+   char *endptr = NULL;
+   unsigned long int l = strtoul(param, &endptr, 0/*base*/);
+
+   if (param[0] && *endptr == 0 && l <= maxval)
+      return (png_byte)l;
+
+   else
+      png_error(png_ptr, "sBIT: invalid sBIT value");
+}
+
+static void
+insert_sBIT(png_structp png_ptr, png_infop info_ptr, int nparams,
+      png_charpp params)
+{
+   const int ct = png_get_color_type(png_ptr, info_ptr);
+   const int c = (ct & PNG_COLOR_MASK_COLOR ? 3 : 1) +
+      (ct & PNG_COLOR_MASK_ALPHA ? 1 : 0);
+   const unsigned int maxval =
+      ct & PNG_COLOR_MASK_PALETTE ? 8U : png_get_bit_depth(png_ptr, info_ptr);
+   png_color_8 sBIT;
+
+   if (nparams != c)
+      png_error(png_ptr, "sBIT: incorrect parameter count");
+
+   if (ct & PNG_COLOR_MASK_COLOR)
+   {
+      sBIT.red = bval(png_ptr, params[0], maxval);
+      sBIT.green = bval(png_ptr, params[1], maxval);
+      sBIT.blue = bval(png_ptr, params[2], maxval);
+      sBIT.gray = 42;
+   }
+
+   else
+   {
+      sBIT.red = sBIT.green = sBIT.blue = 42;
+      sBIT.gray = bval(png_ptr, params[0], maxval);
+   }
+
+   if (ct & PNG_COLOR_MASK_ALPHA)
+      sBIT.alpha = bval(png_ptr, params[nparams-1], maxval);
+
+   else
+      sBIT.alpha = 42;
+
+   png_set_sBIT(png_ptr, info_ptr, &sBIT);
+}
+
+#if 0
+static void
+insert_sPLT(png_structp png_ptr, png_infop info_ptr, int nparams, png_charpp params)
+{
+   fprintf(stderr, "insert sPLT: NYI\n");
+}
+#endif
+
+static int
+find_parameters(png_const_charp what, png_charp param, png_charp *list,
+   int nparams)
+{
+   /* Parameters are separated by '\n' or ':' characters, up to nparams are
+    * accepted (more is an error) and the number found is returned.
+    */
+   int i;
+   for (i=0; *param && i<nparams; ++i)
+   {
+      list[i] = param;
+      while (*++param) if (*param == '\n' || *param == ':')
+      {
+         *param++ = 0; /* Terminate last parameter */
+         break;        /* And start a new one. */
+      }
+   }
+
+   if (*param)
+   {
+      fprintf(stderr, "--insert %s: too many parameters (%s)\n", what, param);
+      exit(1);
+   }
+
+   list[i] = NULL; /* terminates list */
+   return i; /* number of parameters filled in */
+}
+
+static void
+bad_parameter_count(png_const_charp what, int nparams)
+{
+   fprintf(stderr, "--insert %s: bad parameter count %d\n", what, nparams);
+   exit(1);
+}
+
+static chunk_insert *
+make_insert(png_const_charp what,
+   void (*insert)(png_structp, png_infop, int, png_charpp),
+   int nparams, png_charpp list)
+{
+   int i;
+   chunk_insert *cip;
+
+   cip = malloc(offsetof(chunk_insert,parameters) +
+      nparams * sizeof (png_charp));
+
+   if (cip == NULL)
+   {
+      fprintf(stderr, "--insert %s: out of memory allocating %d parameters\n",
+         what, nparams);
+      exit(1);
+   }
+
+   cip->next = NULL;
+   cip->insert = insert;
+   cip->nparams = nparams;
+   for (i=0; i<nparams; ++i)
+      cip->parameters[i] = list[i];
+
+   return cip;
+}
+
+static chunk_insert *
+find_insert(png_const_charp what, png_charp param)
+{
+   png_uint_32 chunk = 0;
+   png_charp parameter_list[1024];
+   int i, nparams;
+
+   /* Assemble the chunk name */
+   for (i=0; i<4; ++i)
+   {
+      char ch = what[i];
+
+      if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))
+         chunk = (chunk << 8) + what[i];
+
+      else
+         break;
+   }
+
+   if (i < 4 || what[4] != 0)
+   {
+      fprintf(stderr, "makepng --insert \"%s\": invalid chunk name\n", what);
+      exit(1);
+   }
+
+   /* Assemble the parameter list. */
+   nparams = find_parameters(what, param, parameter_list, 1024);
+
+#  define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+
+   switch (chunk)
+   {
+      case CHUNK(105,67,67,80):  /* iCCP */
+         if (nparams == 2)
+            return make_insert(what, insert_iCCP, nparams, parameter_list);
+         break;
+
+      case CHUNK(116,69,88,116): /* tEXt */
+         if (nparams == 2)
+            return make_insert(what, insert_tEXt, nparams, parameter_list);
+         break;
+
+      case CHUNK(122,84,88,116): /* zTXt */
+         if (nparams == 2)
+            return make_insert(what, insert_zTXt, nparams, parameter_list);
+         break;
+
+      case CHUNK(105,84,88,116): /* iTXt */
+         if (nparams == 4)
+            return make_insert(what, insert_iTXt, nparams, parameter_list);
+         break;
+
+      case CHUNK(104,73,83,84):  /* hIST */
+         if (nparams <= 256)
+            return make_insert(what, insert_hIST, nparams, parameter_list);
+         break;
+
+      case CHUNK(115,66,73,84): /* sBIT */
+         if (nparams <= 4)
+            return make_insert(what, insert_sBIT, nparams, parameter_list);
+         break;
+
+#if 0
+      case CHUNK(115,80,76,84):  /* sPLT */
+         return make_insert(what, insert_sPLT, nparams, parameter_list);
+#endif
+
+      default:
+         fprintf(stderr, "makepng --insert \"%s\": unrecognized chunk name\n",
+            what);
+         exit(1);
+   }
+
+   bad_parameter_count(what, nparams);
+   return NULL;
+}
+
+/* This is necessary because libpng expects writeable strings for things like
+ * text chunks (maybe this should be fixed...)
+ */
+static png_charp
+strstash(png_const_charp foo)
+{
+   /* The program indicates a memory allocation error by crashing, this is by
+    * design.
+    */
+   if (foo != NULL)
+   {
+      png_charp bar = malloc(strlen(foo)+1);
+      return strcpy(bar, foo);
+   }
+
+   return NULL;
+}
+
+static png_charp
+strstash_list(const png_const_charp *text)
+{
+   size_t foo = 0;
+   png_charp result, bar;
+   const png_const_charp *line = text;
+
+   while (*line != NULL)
+      foo += strlen(*line++);
+
+   result = bar = malloc(foo+1);
+
+   line = text;
+   while (*line != NULL)
+   {
+      foo = strlen(*line);
+      memcpy(bar, *line++, foo);
+      bar += foo;
+   }
+
+   *bar = 0;
+   return result;
+}
+
+/* These are used to insert Copyright and Licence fields, they allow the text to
+ * have \n unlike the --insert option.
+ */
+static chunk_insert *
+add_tEXt(const char *key, const png_const_charp *text)
+{
+   static char what[5] = { 116, 69, 88, 116, 0 };
+   png_charp parameter_list[3];
+
+   parameter_list[0] = strstash(key);
+   parameter_list[1] = strstash_list(text);
+   parameter_list[2] = NULL;
+
+   return make_insert(what, insert_tEXt, 2, parameter_list);
+}
+
+static chunk_insert *
+add_iTXt(const char *key, const char *language, const char *language_key,
+      const png_const_charp *text)
+{
+   static char what[5] = { 105, 84, 88, 116, 0 };
+   png_charp parameter_list[5];
+
+   parameter_list[0] = strstash(key);
+   parameter_list[1] = strstash(language);
+   parameter_list[2] = strstash(language_key);
+   parameter_list[3] = strstash_list(text);
+   parameter_list[4] = NULL;
+
+   return make_insert(what, insert_iTXt, 4, parameter_list);
+}
+
+/* This is a not-very-good parser for a sequence of numbers (including 0).  It
+ * doesn't accept some apparently valid things, but it accepts all the sensible
+ * combinations.
+ */
+static void
+parse_color(char *arg, unsigned int *colors)
+{
+   unsigned int ncolors = 0;
+
+   while (*arg && ncolors < 4)
+   {
+      char *ep = arg;
+
+      unsigned long ul = strtoul(arg, &ep, 0);
+
+      if (ul > 65535)
+      {
+         fprintf(stderr, "makepng --color=...'%s': too big\n", arg);
+         exit(1);
+      }
+
+      if (ep == arg)
+      {
+         fprintf(stderr, "makepng --color=...'%s': not a valid color\n", arg);
+         exit(1);
+      }
+
+      if (*ep) ++ep; /* skip a separator */
+      arg = ep;
+
+      colors[++ncolors] = (unsigned int)ul; /* checked above */
+   }
+
+   if (*arg)
+   {
+      fprintf(stderr, "makepng --color=...'%s': too many values\n", arg);
+      exit(1);
+   }
+
+   *colors = ncolors;
+}
+
+int
+main(int argc, char **argv)
+{
+   FILE *fp = stdout;
+   const char *file_name = NULL;
+   int color_type = 8; /* invalid */
+   int bit_depth = 32; /* invalid */
+   int small = 0; /* make full size images */
+   int tRNS = 0; /* don't output a tRNS chunk */
+   unsigned int colors[5];
+   unsigned int filters = PNG_ALL_FILTERS;
+   png_fixed_point gamma = 0; /* not set */
+   chunk_insert *head_insert = NULL;
+   chunk_insert **insert_ptr = &head_insert;
+
+   memset(colors, 0, sizeof colors);
+
+   while (--argc > 0)
+   {
+      char *arg = *++argv;
+
+      if (strcmp(arg, "--small") == 0)
+      {
+         small = 1;
+         continue;
+      }
+
+      if (strcmp(arg, "--tRNS") == 0)
+      {
+         tRNS = 1;
+         continue;
+      }
+
+      if (strcmp(arg, "--sRGB") == 0)
+      {
+         gamma = PNG_DEFAULT_sRGB;
+         continue;
+      }
+
+      if (strcmp(arg, "--linear") == 0)
+      {
+         gamma = PNG_FP_1;
+         continue;
+      }
+
+      if (strcmp(arg, "--1.8") == 0)
+      {
+         gamma = PNG_GAMMA_MAC_18;
+         continue;
+      }
+
+      if (strcmp(arg, "--nofilters") == 0)
+      {
+         filters = PNG_FILTER_NONE;
+         continue;
+      }
+
+      if (strncmp(arg, "--color=", 8) == 0)
+      {
+          parse_color(arg+8, colors);
+          continue;
+      }
+
+      if (argc >= 3 && strcmp(arg, "--insert") == 0)
+      {
+         png_const_charp what = *++argv;
+         png_charp param = *++argv;
+         chunk_insert *new_insert;
+
+         argc -= 2;
+
+         new_insert = find_insert(what, param);
+
+         if (new_insert != NULL)
+         {
+            *insert_ptr = new_insert;
+            insert_ptr = &new_insert->next;
+         }
+
+         continue;
+      }
+
+      if (arg[0] == '-')
+      {
+         fprintf(stderr, "makepng: %s: invalid option\n", arg);
+         exit(1);
+      }
+
+      if (strcmp(arg, "palette") == 0)
+      {
+         color_type = PNG_COLOR_TYPE_PALETTE;
+         continue;
+      }
+
+      if (strncmp(arg, "gray", 4) == 0)
+      {
+         if (arg[4] == 0)
+         {
+            color_type = PNG_COLOR_TYPE_GRAY;
+            continue;
+         }
+
+         else if (strcmp(arg+4, "a") == 0 ||
+            strcmp(arg+4, "alpha") == 0 ||
+            strcmp(arg+4, "-alpha") == 0)
+         {
+            color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+            continue;
+         }
+      }
+
+      if (strncmp(arg, "rgb", 3) == 0)
+      {
+         if (arg[3] == 0)
+         {
+            color_type = PNG_COLOR_TYPE_RGB;
+            continue;
+         }
+
+         else if (strcmp(arg+3, "a") == 0 ||
+            strcmp(arg+3, "alpha") == 0 ||
+            strcmp(arg+3, "-alpha") == 0)
+         {
+            color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+            continue;
+         }
+      }
+
+      if (color_type == 8 && isdigit(arg[0]))
+      {
+         color_type = atoi(arg);
+         if (color_type < 0 || color_type > 6 || color_type == 1 ||
+            color_type == 5)
+         {
+            fprintf(stderr, "makepng: %s: not a valid color type\n", arg);
+            exit(1);
+         }
+
+         continue;
+      }
+
+      if (bit_depth == 32 && isdigit(arg[0]))
+      {
+         bit_depth = atoi(arg);
+         if (bit_depth <= 0 || bit_depth > 16 ||
+            (bit_depth & -bit_depth) != bit_depth)
+         {
+            fprintf(stderr, "makepng: %s: not a valid bit depth\n", arg);
+            exit(1);
+         }
+
+         continue;
+      }
+
+      if (argc == 1) /* It's the file name */
+      {
+         fp = fopen(arg, "wb");
+         if (fp == NULL)
+         {
+            fprintf(stderr, "%s: %s: could not open\n", arg, strerror(errno));
+            exit(1);
+         }
+
+         file_name = arg;
+         continue;
+      }
+
+      fprintf(stderr, "makepng: %s: unknown argument\n", arg);
+      exit(1);
+   } /* argument while loop */
+
+   if (color_type == 8 || bit_depth == 32)
+   {
+      fprintf(stderr, "usage: makepng [--small] [--sRGB|--linear|--1.8] "
+         "[--color=...] color-type bit-depth [file-name]\n"
+         "  Make a test PNG file, by default writes to stdout.\n"
+         "  Other options are available, UTSL.\n");
+      exit(1);
+   }
+
+   /* Check the colors */
+   {
+      const unsigned int lim = (color_type == PNG_COLOR_TYPE_PALETTE ? 255U :
+         (1U<<bit_depth)-1);
+      unsigned int i;
+
+      for (i=1; i<=colors[0]; ++i)
+         if (colors[i] > lim)
+         {
+            fprintf(stderr, "makepng: --color=...: %u out of range [0..%u]\n",
+               colors[i], lim);
+            exit(1);
+         }
+   }
+
+   /* small and colors are incomparible (will probably crash if both are used at
+    * the same time!)
+    */
+   if (small && colors[0] != 0)
+   {
+      fprintf(stderr, "makepng: --color --small: only one at a time!\n");
+      exit(1);
+   }
+
+   /* Restrict the filters for more speed to those we know are used for the
+    * generated images.
+    */
+   if (filters == PNG_ALL_FILTERS && !small/*small provides defaults*/)
+   {
+      if ((color_type & PNG_COLOR_MASK_PALETTE) != 0 || bit_depth < 8)
+         filters = PNG_FILTER_NONE;
+
+      else if (color_type & PNG_COLOR_MASK_COLOR) /* rgb */
+      {
+         if (bit_depth == 8)
+            filters &= ~(PNG_FILTER_NONE | PNG_FILTER_AVG);
+
+         else
+            filters = PNG_FILTER_SUB | PNG_FILTER_PAETH;
+      }
+
+      else /* gray 8 or 16-bit */
+         filters &= ~PNG_FILTER_NONE;
+   }
+
+   /* Insert standard copyright and licence text. */
+   {
+      static png_const_charp copyright[] =
+      {
+         COPYRIGHT, /* ISO-Latin-1 */
+         NULL
+      };
+      static png_const_charp licensing[] =
+      {
+         IMAGE_LICENSING, /* UTF-8 */
+         NULL
+      };
+
+      chunk_insert *new_insert;
+
+      new_insert = add_tEXt("Copyright", copyright);
+      if (new_insert != NULL)
+      {
+         *insert_ptr = new_insert;
+         insert_ptr = &new_insert->next;
+      }
+
+      new_insert = add_iTXt("Licensing", "en", NULL, licensing);
+      if (new_insert != NULL)
+      {
+         *insert_ptr = new_insert;
+         insert_ptr = &new_insert->next;
+      }
+   }
+
+   {
+      int ret = write_png(&file_name, fp, color_type, bit_depth, gamma,
+         head_insert, filters, colors, small, tRNS);
+
+      if (ret != 0 && file_name != NULL)
+         remove(file_name);
+
+      return ret;
+   }
+}
diff --git a/contrib/libtests/pngimage.c b/contrib/libtests/pngimage.c
new file mode 100644
index 0000000..442b2f4
--- /dev/null
+++ b/contrib/libtests/pngimage.c
@@ -0,0 +1,1687 @@
+/* pngimage.c
+ *
+ * Copyright (c) 2015 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.20 [December 3, 2015]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Test the png_read_png and png_write_png interfaces.  Given a PNG file load it
+ * using png_read_png and then write with png_write_png.  Test all possible
+ * transforms.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+#ifndef PNG_SETJMP_SUPPORTED
+#  include <setjmp.h> /* because png.h did *not* include this */
+#endif
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)\
+    && (defined(PNG_READ_PNG_SUPPORTED) || PNG_LIBPNG_VER < 10700)
+/* If a transform is valid on both read and write this implies that if the
+ * transform is applied to read it must also be applied on write to produce
+ * meaningful data.  This is because these transforms when performed on read
+ * produce data with a memory format that does not correspond to a PNG format.
+ *
+ * Most of these transforms are invertible; after applying the transform on
+ * write the result is the original PNG data that would have would have been
+ * read if no transform were applied.
+ *
+ * The exception is _SHIFT, which destroys the low order bits marked as not
+ * significant in a PNG with the sBIT chunk.
+ *
+ * The following table lists, for each transform, the conditions under which it
+ * is expected to do anything.  Conditions are defined as follows:
+ *
+ * 1) Color mask bits required - simply a mask to AND with color_type; one of
+ *    these must be present for the transform to fire, except that 0 means
+ *    'always'.
+ * 2) Color mask bits which must be absent - another mask - none of these must
+ *    be present.
+ * 3) Bit depths - a mask of component bit depths for the transform to fire.
+ * 4) 'read' - the transform works in png_read_png.
+ * 5) 'write' - the transform works in png_write_png.
+ * 6) PNG_INFO_chunk; a mask of the chunks that must be present for the
+ *    transform to fire.  All must be present - the requirement is that
+ *    png_get_valid() & mask == mask, so if mask is 0 there is no requirement.
+ *
+ * The condition refers to the original image state - if multiple transforms are
+ * used together it is possible to cause a transform that wouldn't fire on the
+ * original image to fire.
+ */
+static struct transform_info
+{
+   const char *name;
+   int         transform;
+   png_uint_32 valid_chunks;
+#     define CHUNK_NONE 0
+#     define CHUNK_sBIT PNG_INFO_sBIT
+#     define CHUNK_tRNS PNG_INFO_tRNS
+   png_byte    color_mask_required;
+   png_byte    color_mask_absent;
+#     define COLOR_MASK_X   0
+#     define COLOR_MASK_P   PNG_COLOR_MASK_PALETTE
+#     define COLOR_MASK_C   PNG_COLOR_MASK_COLOR
+#     define COLOR_MASK_A   PNG_COLOR_MASK_ALPHA
+#     define COLOR_MASK_ALL (PALETTE+COLOR+ALPHA)  /* absent = gray, no alpha */
+   png_byte    bit_depths;
+#     define BD_ALL  (1 + 2 + 4 + 8 + 16)
+#     define BD_PAL  (1 + 2 + 4 + 8)
+#     define BD_LOW  (1 + 2 + 4)
+#     define BD_16   16
+#     define BD_TRUE (8+16) /* i.e. true-color depths */
+   png_byte    when;
+#     define TRANSFORM_R  1
+#     define TRANSFORM_W  2
+#     define TRANSFORM_RW 3
+   png_byte    tested; /* the transform was tested somewhere */
+} transform_info[] =
+{
+   /* List ALL the PNG_TRANSFORM_ macros here.  Check for support using the READ
+    * macros; even if the transform is supported on write it cannot be tested
+    * without the read support.
+    */
+#  define T(name,chunk,cm_required,cm_absent,bd,when)\
+   {  #name, PNG_TRANSFORM_ ## name, CHUNK_ ## chunk,\
+      COLOR_MASK_ ## cm_required, COLOR_MASK_ ## cm_absent, BD_ ## bd,\
+      TRANSFORM_ ## when, 0/*!tested*/ }
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+   T(STRIP_16,            NONE, X,   X,   16,  R),
+      /* drops the bottom 8 bits when bit depth is 16 */
+#endif
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+   T(STRIP_ALPHA,         NONE, A,   X,  ALL,  R),
+      /* removes the alpha channel if present */
+#endif
+#ifdef PNG_WRITE_PACK_SUPPORTED
+#  define TRANSFORM_RW_PACK TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_PACK TRANSFORM_R
+#endif
+#ifdef PNG_READ_PACK_SUPPORTED
+   T(PACKING,             NONE, X,   X,  LOW, RW_PACK),
+      /* unpacks low-bit-depth components into 1 byte per component on read,
+       * reverses this on write.
+       */
+#endif
+#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
+#  define TRANSFORM_RW_PACKSWAP TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_PACKSWAP TRANSFORM_R
+#endif
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+   T(PACKSWAP,            NONE, X,   X,  LOW, RW_PACKSWAP),
+      /* reverses the order of low-bit-depth components packed into a byte */
+#endif
+#ifdef PNG_READ_EXPAND_SUPPORTED
+   T(EXPAND,              NONE, P,   X,  ALL,  R),
+      /* expands PLTE PNG files to RGB (no tRNS) or RGBA (tRNS) *
+       * Note that the 'EXPAND' transform does lots of different things: */
+   T(EXPAND,              NONE, X,   C,  ALL,  R),
+      /* expands grayscale PNG files to RGB, or RGBA */
+   T(EXPAND,              tRNS, X,   A,  ALL,  R),
+      /* expands the tRNS chunk in files without alpha */
+#endif
+#ifdef PNG_WRITE_INVERT_SUPPORTED
+#  define TRANSFORM_RW_INVERT TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_INVERT TRANSFORM_R
+#endif
+#ifdef PNG_READ_INVERT_SUPPORTED
+   T(INVERT_MONO,         NONE, X,   C,  ALL, RW_INVERT),
+      /* converts gray-scale components to 1..0 from 0..1 */
+#endif
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+#  define TRANSFORM_RW_SHIFT TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_SHIFT TRANSFORM_R
+#endif
+#ifdef PNG_READ_SHIFT_SUPPORTED
+   T(SHIFT,               sBIT, X,   X,  ALL, RW_SHIFT),
+      /* reduces component values to the original range based on the sBIT chunk,
+       * this is only partially reversible - the low bits are lost and cannot be
+       * recovered on write.  In fact write code replicates the bits to generate
+       * new low-order bits.
+       */
+#endif
+#ifdef PNG_WRITE_BGR_SUPPORTED
+#  define TRANSFORM_RW_BGR TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_BGR TRANSFORM_R
+#endif
+#ifdef PNG_READ_BGR_SUPPORTED
+   T(BGR,                 NONE, C,   P, TRUE, RW_BGR),
+      /* reverses the rgb component values of true-color pixels */
+#endif
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#  define TRANSFORM_RW_SWAP_ALPHA TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_SWAP_ALPHA TRANSFORM_R
+#endif
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+   T(SWAP_ALPHA,          NONE, A,   X, TRUE, RW_SWAP_ALPHA),
+      /* swaps the alpha channel of RGBA or GA pixels to the front - ARGB or
+       * AG, on write reverses the process.
+       */
+#endif
+#ifdef PNG_WRITE_SWAP_SUPPORTED
+#  define TRANSFORM_RW_SWAP TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_SWAP TRANSFORM_R
+#endif
+#ifdef PNG_READ_SWAP_SUPPORTED
+   T(SWAP_ENDIAN,         NONE, X,   P,   16, RW_SWAP),
+      /* byte-swaps 16-bit component values */
+#endif
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#  define TRANSFORM_RW_INVERT_ALPHA TRANSFORM_RW
+#else
+#  define TRANSFORM_RW_INVERT_ALPHA TRANSFORM_R
+#endif
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+   T(INVERT_ALPHA,        NONE, A,   X, TRUE, RW_INVERT_ALPHA),
+      /* converts an alpha channel from 0..1 to 1..0 */
+#endif
+#ifdef PNG_WRITE_FILLER_SUPPORTED
+   T(STRIP_FILLER_BEFORE, NONE, A,   P, TRUE,  W), /* 'A' for a filler! */
+      /* on write skips a leading filler channel; testing requires data with a
+       * filler channel so this is produced from RGBA or GA images by removing
+       * the 'alpha' flag from the color type in place.
+       */
+   T(STRIP_FILLER_AFTER,  NONE, A,   P, TRUE,  W),
+      /* on write strips a trailing filler channel */
+#endif
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+   T(GRAY_TO_RGB,         NONE, X,   C,  ALL,  R),
+      /* expands grayscale images to RGB, also causes the palette part of
+       * 'EXPAND' to happen.  Low bit depth grayscale images are expanded to
+       * 8-bits per component and no attempt is made to convert the image to a
+       * palette image.  While this transform is partially reversible
+       * png_write_png does not currently support this.
+       */
+   T(GRAY_TO_RGB,         NONE, P,   X,  ALL,  R),
+      /* The 'palette' side effect mentioned above; a bit bogus but this is the
+       * way the libpng code works.
+       */
+#endif
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   T(EXPAND_16,           NONE, X,   X,  PAL,  R),
+      /* expands images to 16-bits per component, as a side effect expands
+       * palette images to RGB and expands the tRNS chunk if present, so it can
+       * modify 16-bit per component images as well:
+       */
+   T(EXPAND_16,           tRNS, X,   A,   16,  R),
+      /* side effect of EXPAND_16 - expands the tRNS chunk in an RGB or G 16-bit
+       * image.
+       */
+#endif
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+   T(SCALE_16,            NONE, X,   X,   16,  R),
+      /* scales 16-bit components to 8-bits. */
+#endif
+
+   { NULL /*name*/, 0, 0, 0, 0, 0, 0, 0/*!tested*/ }
+
+#undef T
+};
+
+#define ARRAY_SIZE(a) ((sizeof a)/(sizeof a[0]))
+#define TTABLE_SIZE ARRAY_SIZE(transform_info)
+
+/* Some combinations of options that should be reversible are not; these cases
+ * are bugs.
+ */
+static int known_bad_combos[][2] =
+{
+   /* problem, antidote */
+   { PNG_TRANSFORM_SHIFT | PNG_TRANSFORM_INVERT_ALPHA, 0/*antidote*/ }
+};
+
+static int
+is_combo(int transforms)
+{
+   return transforms & (transforms-1); /* non-zero if more than one set bit */
+}
+
+static int
+first_transform(int transforms)
+{
+   return transforms & -transforms; /* lowest set bit */
+}
+
+static int
+is_bad_combo(int transforms)
+{
+   unsigned int i;
+
+   for (i=0; i<ARRAY_SIZE(known_bad_combos); ++i)
+   {
+      int combo = known_bad_combos[i][0];
+
+      if ((combo & transforms) == combo &&
+         (transforms & known_bad_combos[i][1]) == 0)
+         return 1;
+   }
+
+   return 0; /* combo is ok */
+}
+
+static const char *
+transform_name(int t)
+   /* The name, if 't' has multiple bits set the name of the lowest set bit is
+    * returned.
+    */
+{
+   unsigned int i;
+
+   t &= -t; /* first set bit */
+
+   for (i=0; i<TTABLE_SIZE; ++i) if (transform_info[i].name != NULL)
+   {
+      if ((transform_info[i].transform & t) != 0)
+         return transform_info[i].name;
+   }
+
+   return "invalid transform";
+}
+
+/* Variables calculated by validate_T below and used to record all the supported
+ * transforms.  Need (unsigned int) here because of the places where these
+ * values are used (unsigned compares in the 'exhaustive' iterator.)
+ */
+static unsigned int read_transforms, write_transforms, rw_transforms;
+
+static void
+validate_T(void)
+   /* Validate the above table - this just builds the above values */
+{
+   unsigned int i;
+
+   for (i=0; i<TTABLE_SIZE; ++i) if (transform_info[i].name != NULL)
+   {
+      if (transform_info[i].when & TRANSFORM_R)
+         read_transforms |= transform_info[i].transform;
+
+      if (transform_info[i].when & TRANSFORM_W)
+         write_transforms |= transform_info[i].transform;
+   }
+
+   /* Reversible transforms are those which are supported on both read and
+    * write.
+    */
+   rw_transforms = read_transforms & write_transforms;
+}
+
+/* FILE DATA HANDLING
+ *    The original file is cached in memory.  During write the output file is
+ *    written to memory.
+ *
+ *    In both cases the file data is held in a linked list of buffers - not all
+ *    of these are in use at any time.
+ */
+#define NEW(type) ((type *)malloc(sizeof (type)))
+#define DELETE(ptr) (free(ptr))
+
+struct buffer_list
+{
+   struct buffer_list *next;         /* next buffer in list */
+   png_byte            buffer[1024]; /* the actual buffer */
+};
+
+struct buffer
+{
+   struct buffer_list  *last;       /* last buffer in use */
+   size_t               end_count;  /* bytes in the last buffer */
+   struct buffer_list  *current;    /* current buffer being read */
+   size_t               read_count; /* count of bytes read from current */
+   struct buffer_list   first;      /* the very first buffer */
+};
+
+static void
+buffer_init(struct buffer *buffer)
+   /* Call this only once for a given buffer */
+{
+   buffer->first.next = NULL;
+   buffer->last = NULL;
+   buffer->current = NULL;
+}
+
+static void
+buffer_destroy_list(struct buffer_list *list)
+{
+   if (list != NULL)
+   {
+      struct buffer_list *next = list->next;
+      DELETE(list);
+      buffer_destroy_list(next);
+   }
+}
+
+static void
+buffer_destroy(struct buffer *buffer)
+{
+   struct buffer_list *list = buffer->first.next;
+   buffer_init(buffer);
+   buffer_destroy_list(list);
+}
+
+#ifdef PNG_WRITE_SUPPORTED
+static void
+buffer_start_write(struct buffer *buffer)
+{
+   buffer->last = &buffer->first;
+   buffer->end_count = 0;
+   buffer->current = NULL;
+}
+#endif
+
+static void
+buffer_start_read(struct buffer *buffer)
+{
+   buffer->current = &buffer->first;
+   buffer->read_count = 0;
+}
+
+#ifdef ENOMEM /* required by POSIX 1003.1 */
+#  define MEMORY ENOMEM
+#else
+#  define MEMORY ERANGE /* required by ANSI-C */
+#endif
+static struct buffer *
+get_buffer(png_structp pp)
+   /* Used from libpng callbacks to get the current buffer */
+{
+   return (struct buffer*)png_get_io_ptr(pp);
+}
+
+static struct buffer_list *
+buffer_extend(struct buffer_list *current)
+{
+   struct buffer_list *add;
+
+   assert(current->next == NULL);
+
+   add = NEW(struct buffer_list);
+   if (add == NULL)
+      return NULL;
+
+   add->next = NULL;
+   current->next = add;
+
+   return add;
+}
+
+/* Load a buffer from a file; does the equivalent of buffer_start_write.  On a
+ * read error returns an errno value, else returns 0.
+ */
+static int
+buffer_from_file(struct buffer *buffer, FILE *fp)
+{
+   struct buffer_list *last = &buffer->first;
+   size_t count = 0;
+
+   for (;;)
+   {
+      size_t r = fread(last->buffer+count, 1/*size*/,
+         (sizeof last->buffer)-count, fp);
+
+      if (r > 0)
+      {
+         count += r;
+
+         if (count >= sizeof last->buffer)
+         {
+            assert(count == sizeof last->buffer);
+            count = 0;
+
+            if (last->next == NULL)
+            {
+               last = buffer_extend(last);
+               if (last == NULL)
+                  return MEMORY;
+            }
+
+            else
+               last = last->next;
+         }
+      }
+
+      else /* fread failed - probably end of file */
+      {
+         if (feof(fp))
+         {
+            buffer->last = last;
+            buffer->end_count = count;
+            return 0; /* no error */
+         }
+
+         /* Some kind of funky error; errno should be non-zero */
+         return errno == 0 ? ERANGE : errno;
+      }
+   }
+}
+
+/* This structure is used to control the test of a single file. */
+typedef enum
+{
+   VERBOSE,        /* switches on all messages */
+   INFORMATION,
+   WARNINGS,       /* switches on warnings */
+   LIBPNG_WARNING,
+   APP_WARNING,
+   ERRORS,         /* just errors */
+   APP_FAIL,       /* continuable error - no need to longjmp */
+   LIBPNG_ERROR,   /* this and higher cause a longjmp */
+   LIBPNG_BUG,     /* erroneous behavior in libpng */
+   APP_ERROR,      /* such as out-of-memory in a callback */
+   QUIET,          /* no normal messages */
+   USER_ERROR,     /* such as file-not-found */
+   INTERNAL_ERROR
+} error_level;
+#define LEVEL_MASK      0xf   /* where the level is in 'options' */
+
+#define EXHAUSTIVE      0x010 /* Test all combinations of active options */
+#define STRICT          0x020 /* Fail on warnings as well as errors */
+#define LOG             0x040 /* Log pass/fail to stdout */
+#define CONTINUE        0x080 /* Continue on APP_FAIL errors */
+#define SKIP_BUGS       0x100 /* Skip over known bugs */
+#define LOG_SKIPPED     0x200 /* Log skipped bugs */
+#define FIND_BAD_COMBOS 0x400 /* Attempt to deduce bad combos */
+#define LIST_COMBOS     0x800 /* List combos by name */
+
+/* Result masks apply to the result bits in the 'results' field below; these
+ * bits are simple 1U<<error_level.  A pass requires either nothing worse than
+ * warnings (--relaxes) or nothing worse than information (--strict)
+ */
+#define RESULT_STRICT(r)   (((r) & ~((1U<<WARNINGS)-1)) == 0)
+#define RESULT_RELAXED(r)  (((r) & ~((1U<<ERRORS)-1)) == 0)
+
+struct display
+{
+   jmp_buf        error_return;      /* Where to go to on error */
+
+   const char    *filename;          /* The name of the original file */
+   const char    *operation;         /* Operation being performed */
+   int            transforms;        /* Transform used in operation */
+   png_uint_32    options;           /* See display_log below */
+   png_uint_32    results;           /* A mask of errors seen */
+
+
+   png_structp    original_pp;       /* used on the original read */
+   png_infop      original_ip;       /* set by the original read */
+
+   png_size_t     original_rowbytes; /* of the original rows: */
+   png_bytepp     original_rows;     /* from the original read */
+
+   /* Original chunks valid */
+   png_uint_32    chunks;
+
+   /* Original IHDR information */
+   png_uint_32    width;
+   png_uint_32    height;
+   int            bit_depth;
+   int            color_type;
+   int            interlace_method;
+   int            compression_method;
+   int            filter_method;
+
+   /* Derived information for the original image. */
+   int            active_transforms;  /* transforms that do something on read */
+   int            ignored_transforms; /* transforms that should do nothing */
+
+   /* Used on a read, both the original read and when validating a written
+    * image.
+    */
+   png_structp    read_pp;
+   png_infop      read_ip;
+
+#  ifdef PNG_WRITE_SUPPORTED
+      /* Used to write a new image (the original info_ptr is used) */
+      png_structp   write_pp;
+      struct buffer written_file;   /* where the file gets written */
+#  endif
+
+   struct buffer  original_file;     /* Data read from the original file */
+};
+
+static void
+display_init(struct display *dp)
+   /* Call this only once right at the start to initialize the control
+    * structure, the (struct buffer) lists are maintained across calls - the
+    * memory is not freed.
+    */
+{
+   memset(dp, 0, sizeof *dp);
+   dp->options = WARNINGS; /* default to !verbose, !quiet */
+   dp->filename = NULL;
+   dp->operation = NULL;
+   dp->original_pp = NULL;
+   dp->original_ip = NULL;
+   dp->original_rows = NULL;
+   dp->read_pp = NULL;
+   dp->read_ip = NULL;
+   buffer_init(&dp->original_file);
+
+#  ifdef PNG_WRITE_SUPPORTED
+      dp->write_pp = NULL;
+      buffer_init(&dp->written_file);
+#  endif
+}
+
+static void
+display_clean_read(struct display *dp)
+{
+   if (dp->read_pp != NULL)
+      png_destroy_read_struct(&dp->read_pp, &dp->read_ip, NULL);
+}
+
+#ifdef PNG_WRITE_SUPPORTED
+static void
+display_clean_write(struct display *dp)
+{
+      if (dp->write_pp != NULL)
+         png_destroy_write_struct(&dp->write_pp, NULL);
+}
+#endif
+
+static void
+display_clean(struct display *dp)
+{
+#  ifdef PNG_WRITE_SUPPORTED
+      display_clean_write(dp);
+#  endif
+   display_clean_read(dp);
+
+   dp->original_rowbytes = 0;
+   dp->original_rows = NULL;
+   dp->chunks = 0;
+
+   png_destroy_read_struct(&dp->original_pp, &dp->original_ip, NULL);
+   /* leave the filename for error detection */
+   dp->results = 0; /* reset for next time */
+}
+
+static void
+display_destroy(struct display *dp)
+{
+    /* Release any memory held in the display. */
+#  ifdef PNG_WRITE_SUPPORTED
+      buffer_destroy(&dp->written_file);
+#  endif
+
+   buffer_destroy(&dp->original_file);
+}
+
+static struct display *
+get_dp(png_structp pp)
+   /* The display pointer is always stored in the png_struct error pointer */
+{
+   struct display *dp = (struct display*)png_get_error_ptr(pp);
+
+   if (dp == NULL)
+   {
+      fprintf(stderr, "pngimage: internal error (no display)\n");
+      exit(99); /* prevents a crash */
+   }
+
+   return dp;
+}
+
+/* error handling */
+#ifdef __GNUC__
+#  define VGATTR __attribute__((__format__ (__printf__,3,4)))
+   /* Required to quiet GNUC warnings when the compiler sees a stdarg function
+    * that calls one of the stdio v APIs.
+    */
+#else
+#  define VGATTR
+#endif
+static void VGATTR
+display_log(struct display *dp, error_level level, const char *fmt, ...)
+   /* 'level' is as above, fmt is a stdio style format string.  This routine
+    * does not return if level is above LIBPNG_WARNING
+    */
+{
+   dp->results |= 1U << level;
+
+   if (level > (error_level)(dp->options & LEVEL_MASK))
+   {
+      const char *lp;
+      va_list ap;
+
+      switch (level)
+      {
+         case INFORMATION:    lp = "information"; break;
+         case LIBPNG_WARNING: lp = "warning(libpng)"; break;
+         case APP_WARNING:    lp = "warning(pngimage)"; break;
+         case APP_FAIL:       lp = "error(continuable)"; break;
+         case LIBPNG_ERROR:   lp = "error(libpng)"; break;
+         case LIBPNG_BUG:     lp = "bug(libpng)"; break;
+         case APP_ERROR:      lp = "error(pngimage)"; break;
+         case USER_ERROR:     lp = "error(user)"; break;
+
+         case INTERNAL_ERROR: /* anything unexpected is an internal error: */
+         case VERBOSE: case WARNINGS: case ERRORS: case QUIET:
+         default:             lp = "bug(pngimage)"; break;
+      }
+
+      fprintf(stderr, "%s: %s: %s",
+         dp->filename != NULL ? dp->filename : "<stdin>", lp, dp->operation);
+
+      if (dp->transforms != 0)
+      {
+         int tr = dp->transforms;
+
+         if (is_combo(tr))
+         {
+            if (dp->options & LIST_COMBOS)
+            {
+               int trx = tr;
+
+               fprintf(stderr, "(");
+               if (trx)
+               {
+                  int start = 0;
+
+                  while (trx)
+                  {
+                     int trz = trx & -trx;
+
+                     if (start) fprintf(stderr, "+");
+                     fprintf(stderr, "%s", transform_name(trz));
+                     start = 1;
+                     trx &= ~trz;
+                  }
+               }
+
+               else
+                  fprintf(stderr, "-");
+               fprintf(stderr, ")");
+            }
+
+            else
+               fprintf(stderr, "(0x%x)", tr);
+         }
+
+         else
+            fprintf(stderr, "(%s)", transform_name(tr));
+      }
+
+      fprintf(stderr, ": ");
+
+      va_start(ap, fmt);
+      vfprintf(stderr, fmt, ap);
+      va_end(ap);
+
+      fputc('\n', stderr);
+   }
+   /* else do not output any message */
+
+   /* Errors cause this routine to exit to the fail code */
+   if (level > APP_FAIL || (level > ERRORS && !(dp->options & CONTINUE)))
+      longjmp(dp->error_return, level);
+}
+
+/* error handler callbacks for libpng */
+static void PNGCBAPI
+display_warning(png_structp pp, png_const_charp warning)
+{
+   display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning);
+}
+
+static void PNGCBAPI
+display_error(png_structp pp, png_const_charp error)
+{
+   struct display *dp = get_dp(pp);
+
+   display_log(dp, LIBPNG_ERROR, "%s", error);
+}
+
+static void
+display_cache_file(struct display *dp, const char *filename)
+   /* Does the initial cache of the file. */
+{
+   FILE *fp;
+   int ret;
+
+   dp->filename = filename;
+
+   if (filename != NULL)
+   {
+      fp = fopen(filename, "rb");
+      if (fp == NULL)
+         display_log(dp, USER_ERROR, "open failed: %s", strerror(errno));
+   }
+
+   else
+      fp = stdin;
+
+   ret = buffer_from_file(&dp->original_file, fp);
+
+   fclose(fp);
+
+   if (ret != 0)
+      display_log(dp, APP_ERROR, "read failed: %s", strerror(ret));
+}
+
+static void
+buffer_read(struct display *dp, struct buffer *bp, png_bytep data,
+   png_size_t size)
+{
+   struct buffer_list *last = bp->current;
+   size_t read_count = bp->read_count;
+
+   while (size > 0)
+   {
+      size_t avail;
+
+      if (last == NULL ||
+         (last == bp->last && read_count >= bp->end_count))
+      {
+         display_log(dp, USER_ERROR, "file truncated (%lu bytes)",
+            (unsigned long)size);
+         /*NOTREACHED*/
+         break;
+      }
+
+      else if (read_count >= sizeof last->buffer)
+      {
+         /* Move to the next buffer: */
+         last = last->next;
+         read_count = 0;
+         bp->current = last; /* Avoid update outside the loop */
+
+         /* And do a sanity check (the EOF case is caught above) */
+         if (last == NULL)
+         {
+            display_log(dp, INTERNAL_ERROR, "damaged buffer list");
+            /*NOTREACHED*/
+            break;
+         }
+      }
+
+      avail = (sizeof last->buffer) - read_count;
+      if (avail > size)
+         avail = size;
+
+      memcpy(data, last->buffer + read_count, avail);
+      read_count += avail;
+      size -= avail;
+      data += avail;
+   }
+
+   bp->read_count = read_count;
+}
+
+static void PNGCBAPI
+read_function(png_structp pp, png_bytep data, png_size_t size)
+{
+   buffer_read(get_dp(pp), get_buffer(pp), data, size);
+}
+
+static void
+read_png(struct display *dp, struct buffer *bp, const char *operation,
+   int transforms)
+{
+   png_structp pp;
+   png_infop   ip;
+
+   /* This cleans out any previous read and sets operation and transforms to
+    * empty.
+    */
+   display_clean_read(dp);
+
+   if (operation != NULL) /* else this is a verify and do not overwrite info */
+   {
+      dp->operation = operation;
+      dp->transforms = transforms;
+   }
+
+   dp->read_pp = pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, dp,
+      display_error, display_warning);
+   if (pp == NULL)
+      display_log(dp, LIBPNG_ERROR, "failed to create read struct");
+
+   /* The png_read_png API requires us to make the info struct, but it does the
+    * call to png_read_info.
+    */
+   dp->read_ip = ip = png_create_info_struct(pp);
+   if (ip == NULL)
+      display_log(dp, LIBPNG_ERROR, "failed to create info struct");
+
+#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
+      /* Remove the user limits, if any */
+      png_set_user_limits(pp, 0x7fffffff, 0x7fffffff);
+#  endif
+
+   /* Set the IO handling */
+   buffer_start_read(bp);
+   png_set_read_fn(pp, bp, read_function);
+
+   png_read_png(pp, ip, transforms, NULL/*params*/);
+
+#if 0 /* crazy debugging */
+   {
+      png_bytep pr = png_get_rows(pp, ip)[0];
+      size_t rb = png_get_rowbytes(pp, ip);
+      size_t cb;
+      char c = ' ';
+
+      fprintf(stderr, "%.4x %2d (%3lu bytes):", transforms, png_get_bit_depth(pp,ip), (unsigned long)rb);
+
+      for (cb=0; cb<rb; ++cb)
+         fputc(c, stderr), fprintf(stderr, "%.2x", pr[cb]), c='.';
+
+      fputc('\n', stderr);
+   }
+#endif
+}
+
+static void
+update_display(struct display *dp)
+   /* called once after the first read to update all the info, original_pp and
+    * original_ip must have been filled in.
+    */
+{
+   png_structp pp;
+   png_infop   ip;
+
+   /* Now perform the initial read with a 0 tranform. */
+   read_png(dp, &dp->original_file, "original read", 0/*no transform*/);
+
+   /* Move the result to the 'original' fields */
+   dp->original_pp = pp = dp->read_pp, dp->read_pp = NULL;
+   dp->original_ip = ip = dp->read_ip, dp->read_ip = NULL;
+
+   dp->original_rowbytes = png_get_rowbytes(pp, ip);
+   if (dp->original_rowbytes == 0)
+      display_log(dp, LIBPNG_BUG, "png_get_rowbytes returned 0");
+
+   dp->chunks = png_get_valid(pp, ip, 0xffffffff);
+   if ((dp->chunks & PNG_INFO_IDAT) == 0) /* set by png_read_png */
+      display_log(dp, LIBPNG_BUG, "png_read_png did not set IDAT flag");
+
+   dp->original_rows = png_get_rows(pp, ip);
+   if (dp->original_rows == NULL)
+      display_log(dp, LIBPNG_BUG, "png_read_png did not create row buffers");
+
+   if (!png_get_IHDR(pp, ip,
+      &dp->width, &dp->height, &dp->bit_depth, &dp->color_type,
+      &dp->interlace_method, &dp->compression_method, &dp->filter_method))
+      display_log(dp, LIBPNG_BUG, "png_get_IHDR failed");
+
+   /* 'active' transforms are discovered based on the original image format;
+    * running one active transform can activate others.  At present the code
+    * does not attempt to determine the closure.
+    */
+   {
+      png_uint_32 chunks = dp->chunks;
+      int active = 0, inactive = 0;
+      int ct = dp->color_type;
+      int bd = dp->bit_depth;
+      unsigned int i;
+
+      for (i=0; i<TTABLE_SIZE; ++i) if (transform_info[i].name != NULL)
+      {
+         int transform = transform_info[i].transform;
+
+         if ((transform_info[i].valid_chunks == 0 ||
+               (transform_info[i].valid_chunks & chunks) != 0) &&
+            (transform_info[i].color_mask_required & ct) ==
+               transform_info[i].color_mask_required &&
+            (transform_info[i].color_mask_absent & ct) == 0 &&
+            (transform_info[i].bit_depths & bd) != 0 &&
+            (transform_info[i].when & TRANSFORM_R) != 0)
+            active |= transform;
+
+         else if ((transform_info[i].when & TRANSFORM_R) != 0)
+            inactive |= transform;
+      }
+
+      /* Some transforms appear multiple times in the table; the 'active' status
+       * is the logical OR of these and the inactive status must be adjusted to
+       * take this into account.
+       */
+      inactive &= ~active;
+
+      dp->active_transforms = active;
+      dp->ignored_transforms = inactive; /* excluding write-only transforms */
+   }
+}
+
+static int
+compare_read(struct display *dp, int applied_transforms)
+{
+   /* Compare the png_info from read_ip with original_info */
+   size_t rowbytes;
+   png_uint_32 width, height;
+   int bit_depth, color_type;
+   int interlace_method, compression_method, filter_method;
+   const char *e = NULL;
+
+   png_get_IHDR(dp->read_pp, dp->read_ip, &width, &height, &bit_depth,
+      &color_type, &interlace_method, &compression_method, &filter_method);
+
+#  define C(item) if (item != dp->item) \
+      display_log(dp, APP_WARNING, "IHDR " #item "(%lu) changed to %lu",\
+         (unsigned long)dp->item, (unsigned long)item), e = #item
+
+   /* The IHDR should be identical: */
+   C(width);
+   C(height);
+   C(bit_depth);
+   C(color_type);
+   C(interlace_method);
+   C(compression_method);
+   C(filter_method);
+
+   /* 'e' remains set to the name of the last thing changed: */
+   if (e)
+      display_log(dp, APP_ERROR, "IHDR changed (%s)", e);
+
+   /* All the chunks from the original PNG should be preserved in the output PNG
+    * because the PNG format has not been changed.
+    */
+   {
+      unsigned long chunks =
+         png_get_valid(dp->read_pp, dp->read_ip, 0xffffffff);
+
+      if (chunks != dp->chunks)
+         display_log(dp, APP_FAIL, "PNG chunks changed from 0x%lx to 0x%lx",
+            (unsigned long)dp->chunks, chunks);
+   }
+
+   /* rowbytes should be the same */
+   rowbytes = png_get_rowbytes(dp->read_pp, dp->read_ip);
+
+   /* NOTE: on 64-bit systems this may trash the top bits of rowbytes,
+    * which could lead to weird error messages.
+    */
+   if (rowbytes != dp->original_rowbytes)
+      display_log(dp, APP_ERROR, "PNG rowbytes changed from %lu to %lu",
+         (unsigned long)dp->original_rowbytes, (unsigned long)rowbytes);
+
+   /* The rows should be the same too, unless the applied transforms includes
+    * the shift transform, in which case low bits may have been lost.
+    */
+   {
+      png_bytepp rows = png_get_rows(dp->read_pp, dp->read_ip);
+      unsigned int mask;  /* mask (if not zero) for the final byte */
+
+      if (bit_depth < 8)
+      {
+         /* Need the stray bits at the end, this depends only on the low bits
+          * of the image width; overflow does not matter.  If the width is an
+          * exact multiple of 8 bits this gives a mask of 0, not 0xff.
+          */
+         mask = 0xff & (0xff00 >> ((bit_depth * width) & 7));
+      }
+
+      else
+         mask = 0;
+
+      if (rows == NULL)
+         display_log(dp, LIBPNG_BUG, "png_get_rows returned NULL");
+
+      if ((applied_transforms & PNG_TRANSFORM_SHIFT) == 0 ||
+         (dp->active_transforms & PNG_TRANSFORM_SHIFT) == 0 ||
+         color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         unsigned long y;
+
+         for (y=0; y<height; ++y)
+         {
+            png_bytep row = rows[y];
+            png_bytep orig = dp->original_rows[y];
+
+            if (memcmp(row, orig, rowbytes-(mask != 0)) != 0 || (mask != 0 &&
+               ((row[rowbytes-1] & mask) != (orig[rowbytes-1] & mask))))
+            {
+               size_t x;
+
+               /* Find the first error */
+               for (x=0; x<rowbytes-1; ++x) if (row[x] != orig[x])
+                  break;
+
+               display_log(dp, APP_FAIL,
+                  "byte(%lu,%lu) changed 0x%.2x -> 0x%.2x",
+                  (unsigned long)x, (unsigned long)y, orig[x], row[x]);
+               return 0; /* don't keep reporting failed rows on 'continue' */
+            }
+         }
+      }
+
+      else
+      {
+         unsigned long y;
+         int bpp;   /* bits-per-pixel then bytes-per-pixel */
+         /* components are up to 8 bytes in size */
+         png_byte sig_bits[8];
+         png_color_8p sBIT;
+
+         if (png_get_sBIT(dp->read_pp, dp->read_ip, &sBIT) != PNG_INFO_sBIT)
+            display_log(dp, INTERNAL_ERROR,
+               "active shift transform but no sBIT in file");
+
+         switch (color_type)
+         {
+            case PNG_COLOR_TYPE_GRAY:
+               sig_bits[0] = sBIT->gray;
+               bpp = bit_depth;
+               break;
+
+            case PNG_COLOR_TYPE_GA:
+               sig_bits[0] = sBIT->gray;
+               sig_bits[1] = sBIT->alpha;
+               bpp = 2 * bit_depth;
+               break;
+
+            case PNG_COLOR_TYPE_RGB:
+               sig_bits[0] = sBIT->red;
+               sig_bits[1] = sBIT->green;
+               sig_bits[2] = sBIT->blue;
+               bpp = 3 * bit_depth;
+               break;
+
+            case PNG_COLOR_TYPE_RGBA:
+               sig_bits[0] = sBIT->red;
+               sig_bits[1] = sBIT->green;
+               sig_bits[2] = sBIT->blue;
+               sig_bits[3] = sBIT->alpha;
+               bpp = 4 * bit_depth;
+               break;
+
+            default:
+               display_log(dp, LIBPNG_ERROR, "invalid colour type %d",
+                  color_type);
+               /*NOTREACHED*/
+               bpp = 0;
+               break;
+         }
+
+         {
+            int b;
+
+            for (b=0; 8*b<bpp; ++b)
+            {
+               /* libpng should catch this; if not there is a security issue
+                * because an app (like this one) may overflow an array. In fact
+                * libpng doesn't catch this at present.
+                */
+               if (sig_bits[b] == 0 || sig_bits[b] > bit_depth/*!palette*/)
+                  display_log(dp, LIBPNG_BUG,
+                     "invalid sBIT[%u]  value %d returned for PNG bit depth %d",
+                     b, sig_bits[b], bit_depth);
+            }
+         }
+
+         if (bpp < 8 && bpp != bit_depth)
+         {
+            /* sanity check; this is a grayscale PNG; something is wrong in the
+             * code above.
+             */
+            display_log(dp, INTERNAL_ERROR, "invalid bpp %u for bit_depth %u",
+               bpp, bit_depth);
+         }
+
+         switch (bit_depth)
+         {
+            int b;
+
+            case 16: /* Two bytes per component, big-endian */
+               for (b = (bpp >> 4); b > 0; --b)
+               {
+                  unsigned int sig = (unsigned int)(0xffff0000 >> sig_bits[b]);
+
+                  sig_bits[2*b+1] = (png_byte)sig;
+                  sig_bits[2*b+0] = (png_byte)(sig >> 8); /* big-endian */
+               }
+               break;
+
+            case 8: /* One byte per component */
+               for (b=0; b*8 < bpp; ++b)
+                  sig_bits[b] = (png_byte)(0xff00 >> sig_bits[b]);
+               break;
+
+            case 1: /* allowed, but dumb */
+               /* Value is 1 */
+               sig_bits[0] = 0xff;
+               break;
+
+            case 2: /* Replicate 4 times */
+               /* Value is 1 or 2 */
+               b = 0x3 & ((0x3<<2) >> sig_bits[0]);
+               b |= b << 2;
+               b |= b << 4;
+               sig_bits[0] = (png_byte)b;
+               break;
+
+            case 4: /* Relicate twice */
+               /* Value is 1, 2, 3 or 4 */
+               b = 0xf & ((0xf << 4) >> sig_bits[0]);
+               b |= b << 4;
+               sig_bits[0] = (png_byte)b;
+               break;
+
+            default:
+               display_log(dp, LIBPNG_BUG, "invalid bit depth %d", bit_depth);
+               break;
+         }
+
+         /* Convert bpp to bytes; this gives '1' for low-bit depth grayscale,
+          * where there are multiple pixels per byte.
+          */
+         bpp = (bpp+7) >> 3;
+
+         /* The mask can be combined with sig_bits[0] */
+         if (mask != 0)
+         {
+            mask &= sig_bits[0];
+
+            if (bpp != 1 || mask == 0)
+               display_log(dp, INTERNAL_ERROR, "mask calculation error %u, %u",
+                  bpp, mask);
+         }
+
+         for (y=0; y<height; ++y)
+         {
+            png_bytep row = rows[y];
+            png_bytep orig = dp->original_rows[y];
+            unsigned long x;
+
+            for (x=0; x<(width-(mask!=0)); ++x)
+            {
+               int b;
+
+               for (b=0; b<bpp; ++b)
+               {
+                  if ((*row++ & sig_bits[b]) != (*orig++ & sig_bits[b]))
+                  {
+                     display_log(dp, APP_FAIL,
+                        "significant bits at (%lu[%u],%lu) changed %.2x->%.2x",
+                        x, b, y, orig[-1], row[-1]);
+                     return 0;
+                  }
+               }
+            }
+
+            if (mask != 0 && (*row & mask) != (*orig & mask))
+            {
+               display_log(dp, APP_FAIL,
+                  "significant bits at (%lu[end],%lu) changed", x, y);
+               return 0;
+            }
+         } /* for y */
+      }
+   }
+
+   return 1; /* compare succeeded */
+}
+
+#ifdef PNG_WRITE_SUPPORTED
+static void
+buffer_write(struct display *dp, struct buffer *buffer, png_bytep data,
+   png_size_t size)
+   /* Generic write function used both from the write callback provided to
+    * libpng and from the generic read code.
+    */
+{
+   /* Write the data into the buffer, adding buffers as required */
+   struct buffer_list *last = buffer->last;
+   size_t end_count = buffer->end_count;
+
+   while (size > 0)
+   {
+      size_t avail;
+
+      if (end_count >= sizeof last->buffer)
+      {
+         if (last->next == NULL)
+         {
+            last = buffer_extend(last);
+
+            if (last == NULL)
+               display_log(dp, APP_ERROR, "out of memory saving file");
+         }
+
+         else
+            last = last->next;
+
+         buffer->last = last; /* avoid the need to rewrite every time */
+         end_count = 0;
+      }
+
+      avail = (sizeof last->buffer) - end_count;
+      if (avail > size)
+         avail = size;
+
+      memcpy(last->buffer + end_count, data, avail);
+      end_count += avail;
+      size -= avail;
+      data += avail;
+   }
+
+   buffer->end_count = end_count;
+}
+
+static void PNGCBAPI
+write_function(png_structp pp, png_bytep data, png_size_t size)
+{
+   buffer_write(get_dp(pp), get_buffer(pp), data, size);
+}
+
+static void
+write_png(struct display *dp, png_infop ip, int transforms)
+{
+   display_clean_write(dp); /* safety */
+
+   buffer_start_write(&dp->written_file);
+   dp->operation = "write";
+   dp->transforms = transforms;
+
+   dp->write_pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, dp,
+      display_error, display_warning);
+
+   if (dp->write_pp == NULL)
+      display_log(dp, APP_ERROR, "failed to create write png_struct");
+
+   png_set_write_fn(dp->write_pp, &dp->written_file, write_function,
+      NULL/*flush*/);
+
+#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
+      /* Remove the user limits, if any */
+      png_set_user_limits(dp->write_pp, 0x7fffffff, 0x7fffffff);
+#  endif
+
+   /* Certain transforms require the png_info to be zapped to allow the
+    * transform to work correctly.
+    */
+   if (transforms & (PNG_TRANSFORM_PACKING|
+                     PNG_TRANSFORM_STRIP_FILLER|
+                     PNG_TRANSFORM_STRIP_FILLER_BEFORE))
+   {
+      int ct = dp->color_type;
+
+      if (transforms & (PNG_TRANSFORM_STRIP_FILLER|
+                        PNG_TRANSFORM_STRIP_FILLER_BEFORE))
+         ct &= ~PNG_COLOR_MASK_ALPHA;
+
+      png_set_IHDR(dp->write_pp, ip, dp->width, dp->height, dp->bit_depth, ct,
+         dp->interlace_method, dp->compression_method, dp->filter_method);
+   }
+
+   png_write_png(dp->write_pp, ip, transforms, NULL/*params*/);
+
+   /* Clean it on the way out - if control returns to the caller then the
+    * written_file contains the required data.
+    */
+   display_clean_write(dp);
+}
+#endif /* WRITE_SUPPORTED */
+
+static int
+skip_transform(struct display *dp, int tr)
+   /* Helper to test for a bad combo and log it if it is skipped */
+{
+   if ((dp->options & SKIP_BUGS) != 0 && is_bad_combo(tr))
+   {
+      /* Log this to stdout if logging is on, otherwise just do an information
+       * display_log.
+       */
+      if ((dp->options & LOG_SKIPPED) != 0)
+      {
+         printf("SKIP: %s transforms ", dp->filename);
+
+         while (tr != 0)
+         {
+            int next = first_transform(tr);
+            tr &= ~next;
+
+            printf("%s", transform_name(next));
+            if (tr != 0)
+               putchar('+');
+         }
+
+         putchar('\n');
+      }
+
+      else
+         display_log(dp, INFORMATION, "%s: skipped known bad combo 0x%x",
+            dp->filename, tr);
+
+      return 1; /* skip */
+   }
+
+   return 0; /* don't skip */
+}
+
+static void
+test_one_file(struct display *dp, const char *filename)
+{
+   /* First cache the file and update the display original file
+    * information for the new file.
+    */
+   dp->operation = "cache file";
+   dp->transforms = 0;
+   display_cache_file(dp, filename);
+   update_display(dp);
+
+   /* First test: if there are options that should be ignored for this file
+    * verify that they really are ignored.
+    */
+   if (dp->ignored_transforms != 0)
+   {
+      read_png(dp, &dp->original_file, "ignored transforms",
+         dp->ignored_transforms);
+
+      /* The result should be identical to the original_rows */
+      if (!compare_read(dp, 0/*transforms applied*/))
+         return; /* no point testing more */
+   }
+
+#ifdef PNG_WRITE_SUPPORTED
+   /* Second test: write the original PNG data out to a new file (to test the
+    * write side) then read the result back in and make sure that it hasn't
+    * changed.
+    */
+   dp->operation = "write";
+   write_png(dp, dp->original_ip, 0/*transforms*/);
+   read_png(dp, &dp->written_file, NULL, 0/*transforms*/);
+   if (!compare_read(dp, 0/*transforms applied*/))
+      return;
+#endif
+
+   /* Third test: the active options.  Test each in turn, or, with the
+    * EXHAUSTIVE option, test all possible combinations.
+    */
+   {
+      /* Use unsigned int here because the code below to increment through all
+       * the possibilities exhaustively has to use a compare and that must be
+       * unsigned, because some transforms are negative on a 16-bit system.
+       */
+      unsigned int active = dp->active_transforms;
+      const int exhaustive = (dp->options & EXHAUSTIVE) != 0;
+      unsigned int current = first_transform(active);
+      unsigned int bad_transforms = 0;
+      unsigned int bad_combo = ~0U;    /* bitwise AND of failing transforms */
+      unsigned int bad_combo_list = 0; /* bitwise OR of failures */
+
+      for (;;)
+      {
+         read_png(dp, &dp->original_file, "active transforms", current);
+
+         /* If this involved any irreversible transformations then if we write
+          * it out with just the reversible transformations and read it in again
+          * with the same transforms we should get the same thing.  At present
+          * this isn't done - it just seems like a waste of time and it would
+          * require two sets of read png_struct/png_info.
+          *
+          * If there were no irreversible transformations then if we write it
+          * out and read it back in again (without the reversible transforms)
+          * we should get back to the place where we started.
+          */
+#ifdef PNG_WRITE_SUPPORTED
+         if ((current & write_transforms) == current)
+         {
+            /* All transforms reversible: write the PNG with the transformations
+             * reversed, then read it back in with no transformations.  The
+             * result should be the same as the original apart from the loss of
+             * low order bits because of the SHIFT/sBIT transform.
+             */
+            dp->operation = "reversible transforms";
+            write_png(dp, dp->read_ip, current);
+
+            /* And if this is read back in, because all the transformations were
+             * reversible, the result should be the same.
+             */
+            read_png(dp, &dp->written_file, NULL, 0);
+            if (!compare_read(dp, current/*for the SHIFT/sBIT transform*/))
+            {
+               /* This set of transforms failed.  If a single bit is set - if
+                * there is just one transform - don't include this in further
+                * 'exhaustive' tests.  Notice that each transform is tested on
+                * its own before testing combos in the exhaustive case.
+                */
+               if (is_combo(current))
+               {
+                  bad_combo &= current;
+                  bad_combo_list |= current;
+               }
+
+               else
+                  bad_transforms |= current;
+            }
+         }
+#endif
+
+         /* Now move to the next transform */
+         if (exhaustive) /* all combinations */
+         {
+            unsigned int next = current;
+
+            do
+            {
+               if (next == read_transforms) /* Everything tested */
+                  goto combo;
+
+               ++next;
+            }  /* skip known bad combos if the relevant option is set; skip
+                * combos involving known bad single transforms in all cases.
+                */
+            while (  (next & read_transforms) <= current
+                  || (next & active) == 0 /* skip cases that do nothing */
+                  || (next & bad_transforms) != 0
+                  || skip_transform(dp, next));
+
+            assert((next & read_transforms) == next);
+            current = next;
+         }
+
+         else /* one at a time */
+         {
+            active &= ~current;
+
+            if (active == 0)
+               goto combo;
+
+            current = first_transform(active);
+         }
+      }
+
+combo:
+      if (dp->options & FIND_BAD_COMBOS)
+      {
+         /* bad_combos identifies the combos that occur in all failing cases;
+          * bad_combo_list identifies transforms that do not prevent the
+          * failure.
+          */
+         if (bad_combo != ~0U)
+            printf("%s[0x%x]: PROBLEM: 0x%x[0x%x] ANTIDOTE: 0x%x\n",
+               dp->filename, active, bad_combo, bad_combo_list,
+               rw_transforms & ~bad_combo_list);
+
+         else
+            printf("%s: no %sbad combos found\n", dp->filename,
+               (dp->options & SKIP_BUGS) ? "additional " : "");
+      }
+   }
+}
+
+static int
+do_test(struct display *dp, const char *file)
+   /* Exists solely to isolate the setjmp clobbers */
+{
+   int ret = setjmp(dp->error_return);
+
+   if (ret == 0)
+   {
+      test_one_file(dp, file);
+      return 0;
+   }
+
+   else if (ret < ERRORS) /* shouldn't longjmp on warnings */
+      display_log(dp, INTERNAL_ERROR, "unexpected return code %d", ret);
+
+   return ret;
+}
+
+int
+main(const int argc, const char * const * const argv)
+{
+   /* For each file on the command line test it with a range of transforms */
+   int option_end, ilog = 0;
+   struct display d;
+
+   validate_T();
+   display_init(&d);
+
+   for (option_end=1; option_end<argc; ++option_end)
+   {
+      const char *name = argv[option_end];
+
+      if (strcmp(name, "--verbose") == 0)
+         d.options = (d.options & ~LEVEL_MASK) | VERBOSE;
+
+      else if (strcmp(name, "--warnings") == 0)
+         d.options = (d.options & ~LEVEL_MASK) | WARNINGS;
+
+      else if (strcmp(name, "--errors") == 0)
+         d.options = (d.options & ~LEVEL_MASK) | ERRORS;
+
+      else if (strcmp(name, "--quiet") == 0)
+         d.options = (d.options & ~LEVEL_MASK) | QUIET;
+
+      else if (strcmp(name, "--exhaustive") == 0)
+         d.options |= EXHAUSTIVE;
+
+      else if (strcmp(name, "--fast") == 0)
+         d.options &= ~EXHAUSTIVE;
+
+      else if (strcmp(name, "--strict") == 0)
+         d.options |= STRICT;
+
+      else if (strcmp(name, "--relaxed") == 0)
+         d.options &= ~STRICT;
+
+      else if (strcmp(name, "--log") == 0)
+      {
+         ilog = option_end; /* prevent display */
+         d.options |= LOG;
+      }
+
+      else if (strcmp(name, "--nolog") == 0)
+         d.options &= ~LOG;
+
+      else if (strcmp(name, "--continue") == 0)
+         d.options |= CONTINUE;
+
+      else if (strcmp(name, "--stop") == 0)
+         d.options &= ~CONTINUE;
+
+      else if (strcmp(name, "--skip-bugs") == 0)
+         d.options |= SKIP_BUGS;
+
+      else if (strcmp(name, "--test-all") == 0)
+         d.options &= ~SKIP_BUGS;
+
+      else if (strcmp(name, "--log-skipped") == 0)
+         d.options |= LOG_SKIPPED;
+
+      else if (strcmp(name, "--nolog-skipped") == 0)
+         d.options &= ~LOG_SKIPPED;
+
+      else if (strcmp(name, "--find-bad-combos") == 0)
+         d.options |= FIND_BAD_COMBOS;
+
+      else if (strcmp(name, "--nofind-bad-combos") == 0)
+         d.options &= ~FIND_BAD_COMBOS;
+
+      else if (strcmp(name, "--list-combos") == 0)
+         d.options |= LIST_COMBOS;
+
+      else if (strcmp(name, "--nolist-combos") == 0)
+         d.options &= ~LIST_COMBOS;
+
+      else if (name[0] == '-' && name[1] == '-')
+      {
+         fprintf(stderr, "pngimage: %s: unknown option\n", name);
+         return 99;
+      }
+
+      else
+         break; /* Not an option */
+   }
+
+   {
+      int i;
+      int errors = 0;
+
+      for (i=option_end; i<argc; ++i)
+      {
+         {
+            int ret = do_test(&d, argv[i]);
+
+            if (ret > QUIET) /* abort on user or internal error */
+               return 99;
+         }
+
+         /* Here on any return, including failures, except user/internal issues
+          */
+         {
+            const int pass = (d.options & STRICT) ?
+               RESULT_STRICT(d.results) : RESULT_RELAXED(d.results);
+
+            if (!pass)
+               ++errors;
+
+            if (d.options & LOG)
+            {
+               int j;
+
+               printf("%s: pngimage ", pass ? "PASS" : "FAIL");
+
+               for (j=1; j<option_end; ++j) if (j != ilog)
+                  printf("%s ", argv[j]);
+
+               printf("%s\n", d.filename);
+            }
+         }
+
+         display_clean(&d);
+      }
+
+      /* Release allocated memory */
+      display_destroy(&d);
+
+      return errors != 0;
+   }
+}
+#else /* !INFO_IMAGE || !SEQUENTIAL_READ || !READ_PNG*/
+int
+main(void)
+{
+   fprintf(stderr, "pngimage: no support for png_read/write_image\n");
+   return 77;
+}
+#endif
diff --git a/contrib/libtests/pngstest-errors.h b/contrib/libtests/pngstest-errors.h
new file mode 100644
index 0000000..3356f1c
--- /dev/null
+++ b/contrib/libtests/pngstest-errors.h
@@ -0,0 +1,165 @@
+/* contrib/libtests/pngstest-errors.h
+ *
+ * BUILT USING: libpng version 1.6.19beta03 - September 25, 2015
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * THIS IS A MACHINE GENERATED FILE: do not edit it directly!
+ * Instead run:
+ *
+ *    pngstest --accumulate
+ *
+ * on as many PNG files as possible; at least PNGSuite and
+ * contrib/libtests/testpngs.
+ */
+static png_uint_16 gpc_error[16/*in*/][16/*out*/][4/*a*/] =
+{
+ { /* input: sRGB-gray */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 372, 0 }, { 0, 0, 372, 0 }, { 0, 0, 372, 0 }, { 0, 0, 372, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: sRGB-gray+alpha */
+  { 0, 19, 0, 0 }, { 0, 0, 0, 0 }, { 0, 20, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 897, 788, 0 }, { 0, 897, 788, 0 }, { 0, 897, 788, 0 }, { 0, 897, 788, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: sRGB-rgb */
+  { 0, 0, 19, 0 }, { 0, 0, 19, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 893, 0 }, { 0, 0, 893, 0 }, { 0, 0, 811, 0 }, { 0, 0, 811, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: sRGB-rgb+alpha */
+  { 0, 16, 17, 0 }, { 0, 17, 17, 0 }, { 0, 19, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 850, 875, 0 }, { 0, 850, 875, 0 }, { 0, 897, 788, 0 }, { 0, 897, 788, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-gray */
+  { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-gray+alpha */
+  { 0, 74, 9, 0 }, { 0, 20, 9, 0 }, { 0, 74, 9, 0 }, { 0, 20, 9, 0 },
+  { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 1, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-rgb */
+  { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+  { 0, 0, 4, 0 }, { 0, 0, 4, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-rgb+alpha */
+  { 0, 126, 143, 0 }, { 0, 11, 7, 0 }, { 0, 74, 9, 0 }, { 0, 17, 9, 0 },
+  { 0, 4, 4, 0 }, { 0, 5, 4, 0 }, { 0, 0, 0, 0 }, { 0, 1, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-gray */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-gray+alpha */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-rgb */
+  { 0, 0, 13, 0 }, { 0, 0, 13, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+  { 0, 0, 673, 0 }, { 0, 0, 673, 0 }, { 0, 0, 674, 0 }, { 0, 0, 674, 0 },
+  { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 460, 0 }, { 0, 0, 460, 0 }, { 0, 0, 263, 0 }, { 0, 0, 263, 0 }
+ }, { /* input: color-mapped-sRGB-rgb+alpha */
+  { 0, 6, 8, 0 }, { 0, 7, 8, 0 }, { 0, 75, 9, 0 }, { 0, 9, 9, 0 },
+  { 0, 585, 427, 0 }, { 0, 585, 427, 0 }, { 0, 717, 514, 0 }, { 0, 717, 514, 0 },
+  { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 13323, 460, 0 }, { 0, 427, 460, 0 }, { 0, 16480, 263, 0 }, { 0, 243, 263, 0 }
+ }, { /* input: color-mapped-linear-gray */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 282, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-gray+alpha */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 253, 282, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 265, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb+alpha */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 243, 265, 0 }
+ }
+};
+static png_uint_16 gpc_error_via_linear[16][4/*out*/][4] =
+{
+ { /* input: sRGB-gray */
+  { 0, 0, 7, 0 }, { 0, 0, 7, 0 }, { 0, 0, 7, 0 }, { 0, 0, 7, 0 }
+ }, { /* input: sRGB-gray+alpha */
+  { 0, 15, 15, 0 }, { 0, 186, 15, 0 }, { 0, 15, 15, 0 }, { 0, 186, 15, 0 }
+ }, { /* input: sRGB-rgb */
+  { 0, 0, 20, 0 }, { 0, 0, 20, 0 }, { 0, 0, 15, 0 }, { 0, 0, 15, 0 }
+ }, { /* input: sRGB-rgb+alpha */
+  { 0, 16, 17, 0 }, { 0, 187, 17, 0 }, { 0, 15, 15, 0 }, { 0, 186, 15, 0 }
+ }, { /* input: linear-gray */
+  { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }
+ }, { /* input: linear-gray+alpha */
+  { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }
+ }, { /* input: linear-rgb */
+  { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }
+ }, { /* input: linear-rgb+alpha */
+  { 0, 1, 1, 0 }, { 0, 9, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }
+ }, { /* input: color-mapped-sRGB-gray */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-gray+alpha */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-rgb */
+  { 0, 0, 13, 0 }, { 0, 0, 13, 0 }, { 0, 0, 14, 0 }, { 0, 0, 14, 0 }
+ }, { /* input: color-mapped-sRGB-rgb+alpha */
+  { 0, 4, 8, 0 }, { 0, 9, 8, 0 }, { 0, 9, 5, 0 }, { 0, 32, 5, 0 }
+ }, { /* input: color-mapped-linear-gray */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-gray+alpha */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb+alpha */
+  { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }
+};
+static png_uint_16 gpc_error_to_colormap[8/*i*/][8/*o*/][4] =
+{
+ { /* input: sRGB-gray */
+  { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+  { 0, 0, 560, 0 }, { 0, 0, 560, 0 }, { 0, 0, 560, 0 }, { 0, 0, 560, 0 }
+ }, { /* input: sRGB-gray+alpha */
+  { 0, 19, 9, 0 }, { 0, 255, 9, 25 }, { 0, 88, 9, 0 }, { 0, 255, 9, 25 },
+  { 0, 1012, 928, 0 }, { 0, 16026, 928, 6425 }, { 0, 1012, 928, 0 }, { 0, 16026, 928, 6425 }
+ }, { /* input: sRGB-rgb */
+  { 0, 0, 19, 0 }, { 0, 0, 19, 0 }, { 0, 0, 25, 0 }, { 0, 0, 25, 0 },
+  { 0, 0, 962, 0 }, { 0, 0, 962, 0 }, { 0, 0, 13677, 0 }, { 0, 0, 13677, 0 }
+ }, { /* input: sRGB-rgb+alpha */
+  { 0, 63, 77, 0 }, { 0, 255, 19, 25 }, { 0, 225, 25, 0 }, { 0, 255, 25, 67 },
+  { 0, 17534, 18491, 0 }, { 0, 15736, 2824, 6425 }, { 0, 14019, 13677, 0 }, { 0, 50115, 13677, 17219 }
+ }, { /* input: linear-gray */
+  { 0, 0, 73, 0 }, { 0, 0, 73, 0 }, { 0, 0, 73, 0 }, { 0, 0, 73, 0 },
+  { 0, 0, 18817, 0 }, { 0, 0, 18817, 0 }, { 0, 0, 18817, 0 }, { 0, 0, 18817, 0 }
+ }, { /* input: linear-gray+alpha */
+  { 0, 74, 74, 0 }, { 0, 255, 74, 25 }, { 0, 99, 74, 0 }, { 0, 255, 74, 25 },
+  { 0, 18919, 18907, 0 }, { 0, 24549, 18907, 6553 }, { 0, 18919, 18907, 0 }, { 0, 24549, 18907, 6553 }
+ }, { /* input: linear-rgb */
+  { 0, 0, 73, 0 }, { 0, 0, 73, 0 }, { 0, 0, 98, 0 }, { 0, 0, 98, 0 },
+  { 0, 0, 18664, 0 }, { 0, 0, 18664, 0 }, { 0, 0, 24998, 0 }, { 0, 0, 24998, 0 }
+ }, { /* input: linear-rgb+alpha */
+  { 0, 181, 196, 0 }, { 0, 255, 61, 25 }, { 206, 187, 98, 0 }, { 0, 255, 98, 67 },
+  { 0, 18141, 18137, 0 }, { 0, 17494, 17504, 6553 }, { 0, 24979, 24992, 0 }, { 0, 49172, 24992, 17347 }
+ }
+};
+/* END MACHINE GENERATED */
diff --git a/contrib/libtests/pngstest.c b/contrib/libtests/pngstest.c
new file mode 100644
index 0000000..3374c72
--- /dev/null
+++ b/contrib/libtests/pngstest.c
@@ -0,0 +1,3722 @@
+/*-
+ * pngstest.c
+ *
+ * Copyright (c) 2013-2015 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.19 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Test for the PNG 'simplified' APIs.
+ */
+#define _ISOC90_SOURCE 1
+#define MALLOC_CHECK_ 2/*glibc facility: turn on debugging*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* Else nothing can be done */
+#include "../tools/sRGB.h"
+
+/* KNOWN ISSUES
+ *
+ * These defines switch on alternate algorithms for format conversions to match
+ * the current libpng implementation; they are set to allow pngstest to pass
+ * even though libpng is producing answers that are not as correct as they
+ * should be.
+ */
+#define ALLOW_UNUSED_GPC 0
+   /* If true include unused static GPC functions and declare an external array
+    * of them to hide the fact that they are unused.  This is for development
+    * use while testing the correct function to use to take into account libpng
+    * misbehavior, such as using a simple power law to correct sRGB to linear.
+    */
+
+/* The following is to support direct compilation of this file as C++ */
+#ifdef __cplusplus
+#  define voidcast(type, value) static_cast<type>(value)
+#  define aligncastconst(type, value) \
+      static_cast<type>(static_cast<const void*>(value))
+#else
+#  define voidcast(type, value) (value)
+#  define aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+/* During parallel runs of pngstest each temporary file needs a unique name,
+ * this is used to permit uniqueness using a command line argument which can be
+ * up to 22 characters long.
+ */
+static char tmpf[23] = "TMP";
+
+/* Generate random bytes.  This uses a boring repeatable algorithm and it
+ * is implemented here so that it gives the same set of numbers on every
+ * architecture.  It's a linear congruential generator (Knuth or Sedgewick
+ * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
+ * Hill, "The Art of Electronics".
+ */
+static void
+make_random_bytes(png_uint_32* seed, void* pv, size_t size)
+{
+   png_uint_32 u0 = seed[0], u1 = seed[1];
+   png_bytep bytes = voidcast(png_bytep, pv);
+
+   /* There are thirty three bits, the next bit in the sequence is bit-33 XOR
+    * bit-20.  The top 1 bit is in u1, the bottom 32 are in u0.
+    */
+   size_t i;
+   for (i=0; i<size; ++i)
+   {
+      /* First generate 8 new bits then shift them in at the end. */
+      png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
+      u1 <<= 8;
+      u1 |= u0 >> 24;
+      u0 <<= 8;
+      u0 |= u;
+      *bytes++ = (png_byte)u;
+   }
+
+   seed[0] = u0;
+   seed[1] = u1;
+}
+
+static void
+random_color(png_colorp color)
+{
+   static png_uint_32 color_seed[2] = { 0x12345678, 0x9abcdef };
+   make_random_bytes(color_seed, color, sizeof *color);
+}
+
+/* Math support - neither Cygwin nor Visual Studio have C99 support and we need
+ * a predictable rounding function, so make one here:
+ */
+static double
+closestinteger(double x)
+{
+   return floor(x + .5);
+}
+
+/* Cast support: remove GCC whines. */
+static png_byte
+u8d(double d)
+{
+   d = closestinteger(d);
+   return (png_byte)d;
+}
+
+static png_uint_16
+u16d(double d)
+{
+   d = closestinteger(d);
+   return (png_uint_16)d;
+}
+
+/* sRGB support: use exact calculations rounded to the nearest int, see the
+ * fesetround() call in main().  sRGB_to_d optimizes the 8 to 16-bit conversion.
+ */
+static double sRGB_to_d[256];
+static double g22_to_d[256];
+
+static void
+init_sRGB_to_d(void)
+{
+   int i;
+
+   sRGB_to_d[0] = 0;
+   for (i=1; i<255; ++i)
+      sRGB_to_d[i] = linear_from_sRGB(i/255.);
+   sRGB_to_d[255] = 1;
+
+   g22_to_d[0] = 0;
+   for (i=1; i<255; ++i)
+      g22_to_d[i] = pow(i/255., 1/.45455);
+   g22_to_d[255] = 1;
+}
+
+static png_byte
+sRGB(double linear /*range 0.0 .. 1.0*/)
+{
+   return u8d(255 * sRGB_from_linear(linear));
+}
+
+static png_byte
+isRGB(int fixed_linear)
+{
+   return sRGB(fixed_linear / 65535.);
+}
+
+#if 0 /* not used */
+static png_byte
+unpremultiply(int component, int alpha)
+{
+   if (alpha <= component)
+      return 255; /* Arbitrary, but consistent with the libpng code */
+
+   else if (alpha >= 65535)
+      return isRGB(component);
+
+   else
+      return sRGB((double)component / alpha);
+}
+#endif
+
+static png_uint_16
+ilinear(int fixed_srgb)
+{
+   return u16d(65535 * sRGB_to_d[fixed_srgb]);
+}
+
+static png_uint_16
+ilineara(int fixed_srgb, int alpha)
+{
+   return u16d((257 * alpha) * sRGB_to_d[fixed_srgb]);
+}
+
+static png_uint_16
+ilinear_g22(int fixed_srgb)
+{
+   return u16d(65535 * g22_to_d[fixed_srgb]);
+}
+
+#if ALLOW_UNUSED_GPC
+static png_uint_16
+ilineara_g22(int fixed_srgb, int alpha)
+{
+   return u16d((257 * alpha) * g22_to_d[fixed_srgb]);
+}
+#endif
+
+static double
+YfromRGBint(int ir, int ig, int ib)
+{
+   double r = ir;
+   double g = ig;
+   double b = ib;
+   return YfromRGB(r, g, b);
+}
+
+#if 0 /* unused */
+/* The error that results from using a 2.2 power law in place of the correct
+ * sRGB transform, given an 8-bit value which might be either sRGB or power-law.
+ */
+static int
+power_law_error8(int value)
+{
+   if (value > 0 && value < 255)
+   {
+      double vd = value / 255.;
+      double e = fabs(
+         pow(sRGB_to_d[value], 1/2.2) - sRGB_from_linear(pow(vd, 2.2)));
+
+      /* Always allow an extra 1 here for rounding errors */
+      e = 1+floor(255 * e);
+      return (int)e;
+   }
+
+   return 0;
+}
+
+static int error_in_sRGB_roundtrip = 56; /* by experiment */
+static int
+power_law_error16(int value)
+{
+   if (value > 0 && value < 65535)
+   {
+      /* Round trip the value through an 8-bit representation but using
+       * non-matching to/from conversions.
+       */
+      double vd = value / 65535.;
+      double e = fabs(
+         pow(sRGB_from_linear(vd), 2.2) - linear_from_sRGB(pow(vd, 1/2.2)));
+
+      /* Always allow an extra 1 here for rounding errors */
+      e = error_in_sRGB_roundtrip+floor(65535 * e);
+      return (int)e;
+   }
+
+   return 0;
+}
+
+static int
+compare_8bit(int v1, int v2, int error_limit, int multiple_algorithms)
+{
+   int e = abs(v1-v2);
+   int ev1, ev2;
+
+   if (e <= error_limit)
+      return 1;
+
+   if (!multiple_algorithms)
+      return 0;
+
+   ev1 = power_law_error8(v1);
+   if (e <= ev1)
+      return 1;
+
+   ev2 = power_law_error8(v2);
+   if (e <= ev2)
+      return 1;
+
+   return 0;
+}
+
+static int
+compare_16bit(int v1, int v2, int error_limit, int multiple_algorithms)
+{
+   int e = abs(v1-v2);
+   int ev1, ev2;
+
+   if (e <= error_limit)
+      return 1;
+
+   /* "multiple_algorithms" in this case means that a color-map has been
+    * involved somewhere, so we can deduce that the values were forced to 8-bit
+    * (like the via_linear case for 8-bit.)
+    */
+   if (!multiple_algorithms)
+      return 0;
+
+   ev1 = power_law_error16(v1);
+   if (e <= ev1)
+      return 1;
+
+   ev2 = power_law_error16(v2);
+   if (e <= ev2)
+      return 1;
+
+   return 0;
+}
+#endif /* unused */
+
+#define READ_FILE 1      /* else memory */
+#define USE_STDIO 2      /* else use file name */
+#define STRICT 4         /* fail on warnings too */
+#define VERBOSE 8
+#define KEEP_TMPFILES 16 /* else delete temporary files */
+#define KEEP_GOING 32
+#define ACCUMULATE 64
+#define FAST_WRITE 128
+#define sRGB_16BIT 256
+
+static void
+print_opts(png_uint_32 opts)
+{
+   if (opts & READ_FILE)
+      printf(" --file");
+   if (opts & USE_STDIO)
+      printf(" --stdio");
+   if (opts & STRICT)
+      printf(" --strict");
+   if (opts & VERBOSE)
+      printf(" --verbose");
+   if (opts & KEEP_TMPFILES)
+      printf(" --preserve");
+   if (opts & KEEP_GOING)
+      printf(" --keep-going");
+   if (opts & ACCUMULATE)
+      printf(" --accumulate");
+   if (!(opts & FAST_WRITE)) /* --fast is currently the default */
+      printf(" --slow");
+   if (opts & sRGB_16BIT)
+      printf(" --sRGB-16bit");
+}
+
+#define FORMAT_NO_CHANGE 0x80000000 /* additional flag */
+
+/* A name table for all the formats - defines the format of the '+' arguments to
+ * pngstest.
+ */
+#define FORMAT_COUNT 64
+#define FORMAT_MASK 0x3f
+static PNG_CONST char * PNG_CONST format_names[FORMAT_COUNT] =
+{
+   "sRGB-gray",
+   "sRGB-gray+alpha",
+   "sRGB-rgb",
+   "sRGB-rgb+alpha",
+   "linear-gray",
+   "linear-gray+alpha",
+   "linear-rgb",
+   "linear-rgb+alpha",
+
+   "color-mapped-sRGB-gray",
+   "color-mapped-sRGB-gray+alpha",
+   "color-mapped-sRGB-rgb",
+   "color-mapped-sRGB-rgb+alpha",
+   "color-mapped-linear-gray",
+   "color-mapped-linear-gray+alpha",
+   "color-mapped-linear-rgb",
+   "color-mapped-linear-rgb+alpha",
+
+   "sRGB-gray",
+   "sRGB-gray+alpha",
+   "sRGB-bgr",
+   "sRGB-bgr+alpha",
+   "linear-gray",
+   "linear-gray+alpha",
+   "linear-bgr",
+   "linear-bgr+alpha",
+
+   "color-mapped-sRGB-gray",
+   "color-mapped-sRGB-gray+alpha",
+   "color-mapped-sRGB-bgr",
+   "color-mapped-sRGB-bgr+alpha",
+   "color-mapped-linear-gray",
+   "color-mapped-linear-gray+alpha",
+   "color-mapped-linear-bgr",
+   "color-mapped-linear-bgr+alpha",
+
+   "sRGB-gray",
+   "alpha+sRGB-gray",
+   "sRGB-rgb",
+   "alpha+sRGB-rgb",
+   "linear-gray",
+   "alpha+linear-gray",
+   "linear-rgb",
+   "alpha+linear-rgb",
+
+   "color-mapped-sRGB-gray",
+   "color-mapped-alpha+sRGB-gray",
+   "color-mapped-sRGB-rgb",
+   "color-mapped-alpha+sRGB-rgb",
+   "color-mapped-linear-gray",
+   "color-mapped-alpha+linear-gray",
+   "color-mapped-linear-rgb",
+   "color-mapped-alpha+linear-rgb",
+
+   "sRGB-gray",
+   "alpha+sRGB-gray",
+   "sRGB-bgr",
+   "alpha+sRGB-bgr",
+   "linear-gray",
+   "alpha+linear-gray",
+   "linear-bgr",
+   "alpha+linear-bgr",
+
+   "color-mapped-sRGB-gray",
+   "color-mapped-alpha+sRGB-gray",
+   "color-mapped-sRGB-bgr",
+   "color-mapped-alpha+sRGB-bgr",
+   "color-mapped-linear-gray",
+   "color-mapped-alpha+linear-gray",
+   "color-mapped-linear-bgr",
+   "color-mapped-alpha+linear-bgr",
+};
+
+/* Decode an argument to a format number. */
+static png_uint_32
+formatof(const char *arg)
+{
+   char *ep;
+   unsigned long format = strtoul(arg, &ep, 0);
+
+   if (ep > arg && *ep == 0 && format < FORMAT_COUNT)
+      return (png_uint_32)format;
+
+   else for (format=0; format < FORMAT_COUNT; ++format)
+   {
+      if (strcmp(format_names[format], arg) == 0)
+         return (png_uint_32)format;
+   }
+
+   fprintf(stderr, "pngstest: format name '%s' invalid\n", arg);
+   return FORMAT_COUNT;
+}
+
+/* Bitset/test functions for formats */
+#define FORMAT_SET_COUNT (FORMAT_COUNT / 32)
+typedef struct
+{
+   png_uint_32 bits[FORMAT_SET_COUNT];
+}
+format_list;
+
+static void format_init(format_list *pf)
+{
+   int i;
+   for (i=0; i<FORMAT_SET_COUNT; ++i)
+      pf->bits[i] = 0; /* All off */
+}
+
+#if 0 /* currently unused */
+static void format_clear(format_list *pf)
+{
+   int i;
+   for (i=0; i<FORMAT_SET_COUNT; ++i)
+      pf->bits[i] = 0;
+}
+#endif
+
+static int format_is_initial(format_list *pf)
+{
+   int i;
+   for (i=0; i<FORMAT_SET_COUNT; ++i)
+      if (pf->bits[i] != 0)
+         return 0;
+
+   return 1;
+}
+
+static int format_set(format_list *pf, png_uint_32 format)
+{
+   if (format < FORMAT_COUNT)
+      return pf->bits[format >> 5] |= ((png_uint_32)1) << (format & 31);
+
+   return 0;
+}
+
+#if 0 /* currently unused */
+static int format_unset(format_list *pf, png_uint_32 format)
+{
+   if (format < FORMAT_COUNT)
+      return pf->bits[format >> 5] &= ~((png_uint_32)1) << (format & 31);
+
+   return 0;
+}
+#endif
+
+static int format_isset(format_list *pf, png_uint_32 format)
+{
+   return format < FORMAT_COUNT &&
+      (pf->bits[format >> 5] & (((png_uint_32)1) << (format & 31))) != 0;
+}
+
+static void format_default(format_list *pf, int redundant)
+{
+   if (redundant)
+   {
+      int i;
+
+      /* set everything, including flags that are pointless */
+      for (i=0; i<FORMAT_SET_COUNT; ++i)
+         pf->bits[i] = ~(png_uint_32)0;
+   }
+
+   else
+   {
+      png_uint_32 f;
+
+      for (f=0; f<FORMAT_COUNT; ++f)
+      {
+         /* Eliminate redundant and unsupported settings. */
+#        ifdef PNG_FORMAT_BGR_SUPPORTED
+            /* BGR is meaningless if no color: */
+            if ((f & PNG_FORMAT_FLAG_COLOR) == 0 &&
+               (f & PNG_FORMAT_FLAG_BGR) != 0)
+#        else
+            if ((f & 0x10U/*HACK: fixed value*/) != 0)
+#        endif
+            continue;
+
+         /* AFIRST is meaningless if no alpha: */
+#        ifdef PNG_FORMAT_AFIRST_SUPPORTED
+            if ((f & PNG_FORMAT_FLAG_ALPHA) == 0 &&
+               (f & PNG_FORMAT_FLAG_AFIRST) != 0)
+#        else
+            if ((f & 0x20U/*HACK: fixed value*/) != 0)
+#        endif
+            continue;
+
+         format_set(pf, f);
+      }
+   }
+}
+
+/* THE Image STRUCTURE */
+/* The super-class of a png_image, contains the decoded image plus the input
+ * data necessary to re-read the file with a different format.
+ */
+typedef struct
+{
+   png_image   image;
+   png_uint_32 opts;
+   const char *file_name;
+   int         stride_extra;
+   FILE       *input_file;
+   png_voidp   input_memory;
+   png_size_t  input_memory_size;
+   png_bytep   buffer;
+   ptrdiff_t   stride;
+   png_size_t  bufsize;
+   png_size_t  allocsize;
+   char        tmpfile_name[32];
+   png_uint_16 colormap[256*4];
+}
+Image;
+
+/* Initializer: also sets the permitted error limit for 16-bit operations. */
+static void
+newimage(Image *image)
+{
+   memset(image, 0, sizeof *image);
+}
+
+/* Reset the image to be read again - only needs to rewind the FILE* at present.
+ */
+static void
+resetimage(Image *image)
+{
+   if (image->input_file != NULL)
+      rewind(image->input_file);
+}
+
+/* Free the image buffer; the buffer is re-used on a re-read, this is just for
+ * cleanup.
+ */
+static void
+freebuffer(Image *image)
+{
+   if (image->buffer) free(image->buffer);
+   image->buffer = NULL;
+   image->bufsize = 0;
+   image->allocsize = 0;
+}
+
+/* Delete function; cleans out all the allocated data and the temporary file in
+ * the image.
+ */
+static void
+freeimage(Image *image)
+{
+   freebuffer(image);
+   png_image_free(&image->image);
+
+   if (image->input_file != NULL)
+   {
+      fclose(image->input_file);
+      image->input_file = NULL;
+   }
+
+   if (image->input_memory != NULL)
+   {
+      free(image->input_memory);
+      image->input_memory = NULL;
+      image->input_memory_size = 0;
+   }
+
+   if (image->tmpfile_name[0] != 0 && (image->opts & KEEP_TMPFILES) == 0)
+   {
+      (void)remove(image->tmpfile_name);
+      image->tmpfile_name[0] = 0;
+   }
+}
+
+/* This is actually a re-initializer; allows an image structure to be re-used by
+ * freeing everything that relates to an old image.
+ */
+static void initimage(Image *image, png_uint_32 opts, const char *file_name,
+   int stride_extra)
+{
+   freeimage(image);
+   memset(&image->image, 0, sizeof image->image);
+   image->opts = opts;
+   image->file_name = file_name;
+   image->stride_extra = stride_extra;
+}
+
+/* Make sure the image buffer is big enough; allows re-use of the buffer if the
+ * image is re-read.
+ */
+#define BUFFER_INIT8 73
+static void
+allocbuffer(Image *image)
+{
+   png_size_t size = PNG_IMAGE_BUFFER_SIZE(image->image, image->stride);
+
+   if (size+32 > image->bufsize)
+   {
+      freebuffer(image);
+      image->buffer = voidcast(png_bytep, malloc(size+32));
+      if (image->buffer == NULL)
+      {
+         fflush(stdout);
+         fprintf(stderr,
+            "simpletest: out of memory allocating %lu(+32) byte buffer\n",
+            (unsigned long)size);
+         exit(1);
+      }
+      image->bufsize = size+32;
+   }
+
+   memset(image->buffer, 95, image->bufsize);
+   memset(image->buffer+16, BUFFER_INIT8, size);
+   image->allocsize = size;
+}
+
+/* Make sure 16 bytes match the given byte. */
+static int
+check16(png_const_bytep bp, int b)
+{
+   int i = 16;
+
+   do
+      if (*bp != b) return 1;
+   while (--i);
+
+   return 0;
+}
+
+/* Check for overwrite in the image buffer. */
+static void
+checkbuffer(Image *image, const char *arg)
+{
+   if (check16(image->buffer, 95))
+   {
+      fflush(stdout);
+      fprintf(stderr, "%s: overwrite at start of image buffer\n", arg);
+      exit(1);
+   }
+
+   if (check16(image->buffer+16+image->allocsize, 95))
+   {
+      fflush(stdout);
+      fprintf(stderr, "%s: overwrite at end of image buffer\n", arg);
+      exit(1);
+   }
+}
+
+/* ERROR HANDLING */
+/* Log a terminal error, also frees the libpng part of the image if necessary.
+ */
+static int
+logerror(Image *image, const char *a1, const char *a2, const char *a3)
+{
+   fflush(stdout);
+   if (image->image.warning_or_error)
+      fprintf(stderr, "%s%s%s: %s\n", a1, a2, a3, image->image.message);
+
+   else
+      fprintf(stderr, "%s%s%s\n", a1, a2, a3);
+
+   if (image->image.opaque != NULL)
+   {
+      fprintf(stderr, "%s: image opaque pointer non-NULL on error\n",
+         image->file_name);
+      png_image_free(&image->image);
+   }
+
+   return 0;
+}
+
+/* Log an error and close a file (just a utility to do both things in one
+ * function call.)
+ */
+static int
+logclose(Image *image, FILE *f, const char *name, const char *operation)
+{
+   int e = errno;
+
+   fclose(f);
+   return logerror(image, name, operation, strerror(e));
+}
+
+/* Make sure the png_image has been freed - validates that libpng is doing what
+ * the spec says and freeing the image.
+ */
+static int
+checkopaque(Image *image)
+{
+   if (image->image.opaque != NULL)
+   {
+      png_image_free(&image->image);
+      return logerror(image, image->file_name, ": opaque not NULL", "");
+   }
+
+   else if (image->image.warning_or_error != 0 && (image->opts & STRICT) != 0)
+      return logerror(image, image->file_name, " --strict", "");
+
+   else
+      return 1;
+}
+
+/* IMAGE COMPARISON/CHECKING */
+/* Compare the pixels of two images, which should be the same but aren't.  The
+ * images must have been checked for a size match.
+ */
+typedef struct
+{
+   /* The components, for grayscale images the gray value is in 'g' and if alpha
+    * is not present 'a' is set to 255 or 65535 according to format.
+    */
+   int         r, g, b, a;
+} Pixel;
+
+typedef struct
+{
+   /* The background as the original sRGB 8-bit value converted to the final
+    * integer format and as a double precision linear value in the range 0..1
+    * for with partially transparent pixels.
+    */
+   int ir, ig, ib;
+   double dr, dg, db; /* linear r,g,b scaled to 0..1 */
+} Background;
+
+/* Basic image formats; control the data but not the layout thereof. */
+#define BASE_FORMATS\
+   (PNG_FORMAT_FLAG_ALPHA|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_LINEAR)
+
+/* Read a Pixel from a buffer.  The code below stores the correct routine for
+ * the format in a function pointer, these are the routines:
+ */
+static void
+gp_g8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = p->g = p->b = pp[0];
+   p->a = 255;
+}
+
+static void
+gp_ga8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = p->g = p->b = pp[0];
+   p->a = pp[1];
+}
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_ag8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = p->g = p->b = pp[1];
+   p->a = pp[0];
+}
+#endif
+
+static void
+gp_rgb8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = pp[0];
+   p->g = pp[1];
+   p->b = pp[2];
+   p->a = 255;
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgr8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = pp[2];
+   p->g = pp[1];
+   p->b = pp[0];
+   p->a = 255;
+}
+#endif
+
+static void
+gp_rgba8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = pp[0];
+   p->g = pp[1];
+   p->b = pp[2];
+   p->a = pp[3];
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgra8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = pp[2];
+   p->g = pp[1];
+   p->b = pp[0];
+   p->a = pp[3];
+}
+#endif
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_argb8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = pp[1];
+   p->g = pp[2];
+   p->b = pp[3];
+   p->a = pp[0];
+}
+#endif
+
+#if defined(PNG_FORMAT_AFIRST_SUPPORTED) && defined(PNG_FORMAT_BGR_SUPPORTED)
+static void
+gp_abgr8(Pixel *p, png_const_voidp pb)
+{
+   png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+   p->r = pp[3];
+   p->g = pp[2];
+   p->b = pp[1];
+   p->a = pp[0];
+}
+#endif
+
+static void
+gp_g16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = p->g = p->b = pp[0];
+   p->a = 65535;
+}
+
+static void
+gp_ga16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = p->g = p->b = pp[0];
+   p->a = pp[1];
+}
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_ag16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = p->g = p->b = pp[1];
+   p->a = pp[0];
+}
+#endif
+
+static void
+gp_rgb16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = pp[0];
+   p->g = pp[1];
+   p->b = pp[2];
+   p->a = 65535;
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgr16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = pp[2];
+   p->g = pp[1];
+   p->b = pp[0];
+   p->a = 65535;
+}
+#endif
+
+static void
+gp_rgba16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = pp[0];
+   p->g = pp[1];
+   p->b = pp[2];
+   p->a = pp[3];
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgra16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = pp[2];
+   p->g = pp[1];
+   p->b = pp[0];
+   p->a = pp[3];
+}
+#endif
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_argb16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = pp[1];
+   p->g = pp[2];
+   p->b = pp[3];
+   p->a = pp[0];
+}
+#endif
+
+#if defined(PNG_FORMAT_AFIRST_SUPPORTED) && defined(PNG_FORMAT_BGR_SUPPORTED)
+static void
+gp_abgr16(Pixel *p, png_const_voidp pb)
+{
+   png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+   p->r = pp[3];
+   p->g = pp[2];
+   p->b = pp[1];
+   p->a = pp[0];
+}
+#endif
+
+/* Given a format, return the correct one of the above functions. */
+static void (*
+get_pixel(png_uint_32 format))(Pixel *p, png_const_voidp pb)
+{
+   /* The color-map flag is irrelevant here - the caller of the function
+    * returned must either pass the buffer or, for a color-mapped image, the
+    * correct entry in the color-map.
+    */
+   if (format & PNG_FORMAT_FLAG_LINEAR)
+   {
+      if (format & PNG_FORMAT_FLAG_COLOR)
+      {
+#        ifdef PNG_FORMAT_BGR_SUPPORTED
+            if (format & PNG_FORMAT_FLAG_BGR)
+            {
+               if (format & PNG_FORMAT_FLAG_ALPHA)
+               {
+#                 ifdef PNG_FORMAT_AFIRST_SUPPORTED
+                     if (format & PNG_FORMAT_FLAG_AFIRST)
+                        return gp_abgr16;
+
+                     else
+#                 endif
+                     return gp_bgra16;
+               }
+
+               else
+                  return gp_bgr16;
+            }
+
+            else
+#        endif
+         {
+            if (format & PNG_FORMAT_FLAG_ALPHA)
+            {
+#              ifdef PNG_FORMAT_AFIRST_SUPPORTED
+                  if (format & PNG_FORMAT_FLAG_AFIRST)
+                     return gp_argb16;
+
+                  else
+#              endif
+                  return gp_rgba16;
+            }
+
+            else
+               return gp_rgb16;
+         }
+      }
+
+      else
+      {
+         if (format & PNG_FORMAT_FLAG_ALPHA)
+         {
+#           ifdef PNG_FORMAT_AFIRST_SUPPORTED
+               if (format & PNG_FORMAT_FLAG_AFIRST)
+                  return gp_ag16;
+
+               else
+#           endif
+               return gp_ga16;
+         }
+
+         else
+            return gp_g16;
+      }
+   }
+
+   else
+   {
+      if (format & PNG_FORMAT_FLAG_COLOR)
+      {
+#        ifdef PNG_FORMAT_BGR_SUPPORTED
+            if (format & PNG_FORMAT_FLAG_BGR)
+            {
+               if (format & PNG_FORMAT_FLAG_ALPHA)
+               {
+#                 ifdef PNG_FORMAT_AFIRST_SUPPORTED
+                     if (format & PNG_FORMAT_FLAG_AFIRST)
+                        return gp_abgr8;
+
+                     else
+#                 endif
+                     return gp_bgra8;
+               }
+
+               else
+                  return gp_bgr8;
+            }
+
+            else
+#        endif
+         {
+            if (format & PNG_FORMAT_FLAG_ALPHA)
+            {
+#              ifdef PNG_FORMAT_AFIRST_SUPPORTED
+                  if (format & PNG_FORMAT_FLAG_AFIRST)
+                     return gp_argb8;
+
+                  else
+#              endif
+                  return gp_rgba8;
+            }
+
+            else
+               return gp_rgb8;
+         }
+      }
+
+      else
+      {
+         if (format & PNG_FORMAT_FLAG_ALPHA)
+         {
+#           ifdef PNG_FORMAT_AFIRST_SUPPORTED
+               if (format & PNG_FORMAT_FLAG_AFIRST)
+                  return gp_ag8;
+
+               else
+#           endif
+               return gp_ga8;
+         }
+
+         else
+            return gp_g8;
+      }
+   }
+}
+
+/* Convertion between pixel formats.  The code above effectively eliminates the
+ * component ordering changes leaving three basic changes:
+ *
+ * 1) Remove an alpha channel by pre-multiplication or compositing on a
+ *    background color.  (Adding an alpha channel is a no-op.)
+ *
+ * 2) Remove color by mapping to grayscale.  (Grayscale to color is a no-op.)
+ *
+ * 3) Convert between 8-bit and 16-bit components.  (Both directtions are
+ *    relevant.)
+ *
+ * This gives the following base format conversion matrix:
+ *
+ *   OUT:    ----- 8-bit -----    ----- 16-bit -----
+ *   IN     G    GA   RGB  RGBA  G    GA   RGB  RGBA
+ *  8 G     .    .    .    .     lin  lin  lin  lin
+ *  8 GA    bckg .    bckc .     pre' pre  pre' pre
+ *  8 RGB   g8   g8   .    .     glin glin lin  lin
+ *  8 RGBA  g8b  g8   bckc .     gpr' gpre pre' pre
+ * 16 G     sRGB sRGB sRGB sRGB  .    .    .    .
+ * 16 GA    b16g unpg b16c unpc  A    .    A    .
+ * 16 RGB   sG   sG   sRGB sRGB  g16  g16  .    .
+ * 16 RGBA  gb16 sGp  cb16 sCp   g16  g16' A    .
+ *
+ *  8-bit to 8-bit:
+ * bckg: composite on gray background
+ * bckc: composite on color background
+ * g8:   convert sRGB components to sRGB grayscale
+ * g8b:  convert sRGB components to grayscale and composite on gray background
+ *
+ *  8-bit to 16-bit:
+ * lin:  make sRGB components linear, alpha := 65535
+ * pre:  make sRGB components linear and premultiply by alpha  (scale alpha)
+ * pre': as 'pre' but alpha := 65535
+ * glin: make sRGB components linear, convert to grayscale, alpha := 65535
+ * gpre: make sRGB components grayscale and linear and premultiply by alpha
+ * gpr': as 'gpre' but alpha := 65535
+ *
+ *  16-bit to 8-bit:
+ * sRGB: convert linear components to sRGB, alpha := 255
+ * unpg: unpremultiply gray component and convert to sRGB (scale alpha)
+ * unpc: unpremultiply color components and convert to sRGB (scale alpha)
+ * b16g: composite linear onto gray background and convert the result to sRGB
+ * b16c: composite linear onto color background and convert the result to sRGB
+ * sG:   convert linear RGB to sRGB grayscale
+ * sGp:  unpremultiply RGB then convert to sRGB grayscale
+ * sCp:  unpremultiply RGB then convert to sRGB
+ * gb16: composite linear onto background and convert to sRGB grayscale
+ *       (order doesn't matter, the composite and grayscale operations permute)
+ * cb16: composite linear onto background and convert to sRGB
+ *
+ *  16-bit to 16-bit:
+ * A:    set alpha to 65535
+ * g16:  convert linear RGB to linear grayscale (alpha := 65535)
+ * g16': as 'g16' but alpha is unchanged
+ */
+/* Simple copy: */
+static void
+gpc_noop(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+   out->r = in->r;
+   out->g = in->g;
+   out->b = in->b;
+   out->a = in->a;
+}
+
+#if ALLOW_UNUSED_GPC
+static void
+gpc_nop8(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+   if (in->a == 0)
+      out->r = out->g = out->b = 255;
+
+   else
+   {
+      out->r = in->r;
+      out->g = in->g;
+      out->b = in->b;
+   }
+
+   out->a = in->a;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+static void
+gpc_nop6(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+   if (in->a == 0)
+      out->r = out->g = out->b = 65535;
+
+   else
+   {
+      out->r = in->r;
+      out->g = in->g;
+      out->b = in->b;
+   }
+
+   out->a = in->a;
+}
+#endif
+
+/* 8-bit to 8-bit conversions */
+/* bckg: composite on gray background */
+static void
+gpc_bckg(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+      out->r = out->g = out->b = back->ig;
+
+   else if (in->a >= 255)
+      out->r = out->g = out->b = in->g;
+
+   else
+   {
+      double a = in->a / 255.;
+
+      out->r = out->g = out->b = sRGB(sRGB_to_d[in->g] * a + back->dg * (1-a));
+   }
+
+   out->a = 255;
+}
+
+/* bckc: composite on color background */
+static void
+gpc_bckc(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+   {
+      out->r = back->ir;
+      out->g = back->ig;
+      out->b = back->ib;
+   }
+
+   else if (in->a >= 255)
+   {
+      out->r = in->r;
+      out->g = in->g;
+      out->b = in->b;
+   }
+
+   else
+   {
+      double a = in->a / 255.;
+
+      out->r = sRGB(sRGB_to_d[in->r] * a + back->dr * (1-a));
+      out->g = sRGB(sRGB_to_d[in->g] * a + back->dg * (1-a));
+      out->b = sRGB(sRGB_to_d[in->b] * a + back->db * (1-a));
+   }
+
+   out->a = 255;
+}
+
+/* g8: convert sRGB components to sRGB grayscale */
+static void
+gpc_g8(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = in->g;
+
+   else
+      out->r = out->g = out->b =
+         sRGB(YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+   out->a = in->a;
+}
+
+/* g8b: convert sRGB components to grayscale and composite on gray background */
+static void
+gpc_g8b(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+      out->r = out->g = out->b = back->ig;
+
+   else if (in->a >= 255)
+   {
+      if (in->r == in->g && in->g == in->b)
+         out->r = out->g = out->b = in->g;
+
+      else
+         out->r = out->g = out->b = sRGB(YfromRGB(
+            sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+   }
+
+   else
+   {
+      double a = in->a/255.;
+
+      out->r = out->g = out->b = sRGB(a * YfromRGB(sRGB_to_d[in->r],
+         sRGB_to_d[in->g], sRGB_to_d[in->b]) + back->dg * (1-a));
+   }
+
+   out->a = 255;
+}
+
+/* 8-bit to 16-bit conversions */
+/* lin: make sRGB components linear, alpha := 65535 */
+static void
+gpc_lin(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = ilinear(in->r);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = ilinear(in->b);
+   }
+
+   else
+   {
+      out->g = ilinear(in->g);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = ilinear(in->b);
+   }
+
+   out->a = 65535;
+}
+
+/* pre: make sRGB components linear and premultiply by alpha (scale alpha) */
+static void
+gpc_pre(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = ilineara(in->r, in->a);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = ilineara(in->b, in->a);
+   }
+
+   else
+   {
+      out->g = ilineara(in->g, in->a);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = ilineara(in->b, in->a);
+   }
+
+   out->a = in->a * 257;
+}
+
+/* pre': as 'pre' but alpha := 65535 */
+static void
+gpc_preq(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = ilineara(in->r, in->a);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = ilineara(in->b, in->a);
+   }
+
+   else
+   {
+      out->g = ilineara(in->g, in->a);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = ilineara(in->b, in->a);
+   }
+
+   out->a = 65535;
+}
+
+/* glin: make sRGB components linear, convert to grayscale, alpha := 65535 */
+static void
+gpc_glin(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = ilinear(in->g);
+
+   else
+      out->r = out->g = out->b = u16d(65535 *
+         YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+   out->a = 65535;
+}
+
+/* gpre: make sRGB components grayscale and linear and premultiply by alpha */
+static void
+gpc_gpre(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = ilineara(in->g, in->a);
+
+   else
+      out->r = out->g = out->b = u16d(in->a * 257 *
+         YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+   out->a = 257 * in->a;
+}
+
+/* gpr': as 'gpre' but alpha := 65535 */
+static void
+gpc_gprq(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = ilineara(in->g, in->a);
+
+   else
+      out->r = out->g = out->b = u16d(in->a * 257 *
+         YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+   out->a = 65535;
+}
+
+/* 8-bit to 16-bit conversions for gAMA 45455 encoded values */
+/* Lin: make gAMA 45455 components linear, alpha := 65535 */
+static void
+gpc_Lin(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = ilinear_g22(in->r);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = ilinear_g22(in->b);
+   }
+
+   else
+   {
+      out->g = ilinear_g22(in->g);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = ilinear_g22(in->b);
+   }
+
+   out->a = 65535;
+}
+
+#if ALLOW_UNUSED_GPC
+/* Pre: make gAMA 45455 components linear and premultiply by alpha (scale alpha)
+ */
+static void
+gpc_Pre(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = ilineara_g22(in->r, in->a);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = ilineara_g22(in->b, in->a);
+   }
+
+   else
+   {
+      out->g = ilineara_g22(in->g, in->a);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = ilineara_g22(in->b, in->a);
+   }
+
+   out->a = in->a * 257;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Pre': as 'Pre' but alpha := 65535 */
+static void
+gpc_Preq(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = ilineara_g22(in->r, in->a);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = ilineara_g22(in->b, in->a);
+   }
+
+   else
+   {
+      out->g = ilineara_g22(in->g, in->a);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = ilineara_g22(in->b, in->a);
+   }
+
+   out->a = 65535;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Glin: make gAMA 45455 components linear, convert to grayscale, alpha := 65535
+ */
+static void
+gpc_Glin(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = ilinear_g22(in->g);
+
+   else
+      out->r = out->g = out->b = u16d(65535 *
+         YfromRGB(g22_to_d[in->r], g22_to_d[in->g], g22_to_d[in->b]));
+
+   out->a = 65535;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Gpre: make gAMA 45455 components grayscale and linear and premultiply by
+ * alpha.
+ */
+static void
+gpc_Gpre(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = ilineara_g22(in->g, in->a);
+
+   else
+      out->r = out->g = out->b = u16d(in->a * 257 *
+         YfromRGB(g22_to_d[in->r], g22_to_d[in->g], g22_to_d[in->b]));
+
+   out->a = 257 * in->a;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Gpr': as 'Gpre' but alpha := 65535 */
+static void
+gpc_Gprq(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->r == in->g && in->g == in->b)
+      out->r = out->g = out->b = ilineara_g22(in->g, in->a);
+
+   else
+      out->r = out->g = out->b = u16d(in->a * 257 *
+         YfromRGB(g22_to_d[in->r], g22_to_d[in->g], g22_to_d[in->b]));
+
+   out->a = 65535;
+}
+#endif
+
+/* 16-bit to 8-bit conversions */
+/* sRGB: convert linear components to sRGB, alpha := 255 */
+static void
+gpc_sRGB(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = isRGB(in->r);
+
+   if (in->g == in->r)
+   {
+      out->g = out->r;
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else
+         out->b = isRGB(in->b);
+   }
+
+   else
+   {
+      out->g = isRGB(in->g);
+
+      if (in->b == in->r)
+         out->b = out->r;
+
+      else if (in->b == in->g)
+         out->b = out->g;
+
+      else
+         out->b = isRGB(in->b);
+   }
+
+   out->a = 255;
+}
+
+/* unpg: unpremultiply gray component and convert to sRGB (scale alpha) */
+static void
+gpc_unpg(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->a <= 128)
+   {
+      out->r = out->g = out->b = 255;
+      out->a = 0;
+   }
+
+   else
+   {
+      out->r = out->g = out->b = sRGB((double)in->g / in->a);
+      out->a = u8d(in->a / 257.);
+   }
+}
+
+/* unpc: unpremultiply color components and convert to sRGB (scale alpha) */
+static void
+gpc_unpc(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->a <= 128)
+   {
+      out->r = out->g = out->b = 255;
+      out->a = 0;
+   }
+
+   else
+   {
+      out->r = sRGB((double)in->r / in->a);
+      out->g = sRGB((double)in->g / in->a);
+      out->b = sRGB((double)in->b / in->a);
+      out->a = u8d(in->a / 257.);
+   }
+}
+
+/* b16g: composite linear onto gray background and convert the result to sRGB */
+static void
+gpc_b16g(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+      out->r = out->g = out->b = back->ig;
+
+   else
+   {
+      double a = in->a/65535.;
+      double a1 = 1-a;
+
+      a /= 65535;
+      out->r = out->g = out->b = sRGB(in->g * a + back->dg * a1);
+   }
+
+   out->a = 255;
+}
+
+/* b16c: composite linear onto color background and convert the result to sRGB*/
+static void
+gpc_b16c(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+   {
+      out->r = back->ir;
+      out->g = back->ig;
+      out->b = back->ib;
+   }
+
+   else
+   {
+      double a = in->a/65535.;
+      double a1 = 1-a;
+
+      a /= 65535;
+      out->r = sRGB(in->r * a + back->dr * a1);
+      out->g = sRGB(in->g * a + back->dg * a1);
+      out->b = sRGB(in->b * a + back->db * a1);
+   }
+
+   out->a = 255;
+}
+
+/* sG: convert linear RGB to sRGB grayscale */
+static void
+gpc_sG(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   out->r = out->g = out->b = sRGB(YfromRGBint(in->r, in->g, in->b)/65535);
+   out->a = 255;
+}
+
+/* sGp: unpremultiply RGB then convert to sRGB grayscale */
+static void
+gpc_sGp(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->a <= 128)
+   {
+      out->r = out->g = out->b = 255;
+      out->a = 0;
+   }
+
+   else
+   {
+      out->r = out->g = out->b = sRGB(YfromRGBint(in->r, in->g, in->b)/in->a);
+      out->a = u8d(in->a / 257.);
+   }
+}
+
+/* sCp: unpremultiply RGB then convert to sRGB */
+static void
+gpc_sCp(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+
+   if (in->a <= 128)
+   {
+      out->r = out->g = out->b = 255;
+      out->a = 0;
+   }
+
+   else
+   {
+      out->r = sRGB((double)in->r / in->a);
+      out->g = sRGB((double)in->g / in->a);
+      out->b = sRGB((double)in->b / in->a);
+      out->a = u8d(in->a / 257.);
+   }
+}
+
+/* gb16: composite linear onto background and convert to sRGB grayscale */
+/*  (order doesn't matter, the composite and grayscale operations permute) */
+static void
+gpc_gb16(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+      out->r = out->g = out->b = back->ig;
+
+   else if (in->a >= 65535)
+      out->r = out->g = out->b = isRGB(in->g);
+
+   else
+   {
+      double a = in->a / 65535.;
+      double a1 = 1-a;
+
+      a /= 65535;
+      out->r = out->g = out->b = sRGB(in->g * a + back->dg * a1);
+   }
+
+   out->a = 255;
+}
+
+/* cb16: composite linear onto background and convert to sRGB */
+static void
+gpc_cb16(Pixel *out, const Pixel *in, const Background *back)
+{
+   if (in->a <= 0)
+   {
+      out->r = back->ir;
+      out->g = back->ig;
+      out->b = back->ib;
+   }
+
+   else if (in->a >= 65535)
+   {
+      out->r = isRGB(in->r);
+      out->g = isRGB(in->g);
+      out->b = isRGB(in->b);
+   }
+
+   else
+   {
+      double a = in->a / 65535.;
+      double a1 = 1-a;
+
+      a /= 65535;
+      out->r = sRGB(in->r * a + back->dr * a1);
+      out->g = sRGB(in->g * a + back->dg * a1);
+      out->b = sRGB(in->b * a + back->db * a1);
+   }
+
+   out->a = 255;
+}
+
+/* 16-bit to 16-bit conversions */
+/* A:    set alpha to 65535 */
+static void
+gpc_A(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+   out->r = in->r;
+   out->g = in->g;
+   out->b = in->b;
+   out->a = 65535;
+}
+
+/* g16:  convert linear RGB to linear grayscale (alpha := 65535) */
+static void
+gpc_g16(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+   out->r = out->g = out->b = u16d(YfromRGBint(in->r, in->g, in->b));
+   out->a = 65535;
+}
+
+/* g16': as 'g16' but alpha is unchanged */
+static void
+gpc_g16q(Pixel *out, const Pixel *in, const Background *back)
+{
+   (void)back;
+   out->r = out->g = out->b = u16d(YfromRGBint(in->r, in->g, in->b));
+   out->a = in->a;
+}
+
+#if ALLOW_UNUSED_GPC
+/* Unused functions (to hide them from GCC unused function warnings) */
+void (* const gpc_unused[])
+   (Pixel *out, const Pixel *in, const Background *back) =
+{
+   gpc_Pre, gpc_Preq, gpc_Glin, gpc_Gpre, gpc_Gprq, gpc_nop8, gpc_nop6
+};
+#endif
+
+/*   OUT:    ----- 8-bit -----    ----- 16-bit -----
+ *   IN     G    GA   RGB  RGBA  G    GA   RGB  RGBA
+ *  8 G     .    .    .    .     lin  lin  lin  lin
+ *  8 GA    bckg .    bckc .     pre' pre  pre' pre
+ *  8 RGB   g8   g8   .    .     glin glin lin  lin
+ *  8 RGBA  g8b  g8   bckc .     gpr' gpre pre' pre
+ * 16 G     sRGB sRGB sRGB sRGB  .    .    .    .
+ * 16 GA    b16g unpg b16c unpc  A    .    A    .
+ * 16 RGB   sG   sG   sRGB sRGB  g16  g16  .    .
+ * 16 RGBA  gb16 sGp  cb16 sCp   g16  g16' A    .
+ *
+ * The matrix is held in an array indexed thus:
+ *
+ *   gpc_fn[out_format & BASE_FORMATS][in_format & BASE_FORMATS];
+ */
+/* This will produce a compile time error if the FORMAT_FLAG values don't
+ * match the above matrix!
+ */
+#if PNG_FORMAT_FLAG_ALPHA == 1 && PNG_FORMAT_FLAG_COLOR == 2 &&\
+   PNG_FORMAT_FLAG_LINEAR == 4
+static void (* const gpc_fn[8/*in*/][8/*out*/])
+   (Pixel *out, const Pixel *in, const Background *back) =
+{
+/*out: G-8     GA-8     RGB-8    RGBA-8    G-16     GA-16   RGB-16  RGBA-16 */
+   {gpc_noop,gpc_noop,gpc_noop,gpc_noop, gpc_Lin, gpc_Lin, gpc_Lin, gpc_Lin },
+   {gpc_bckg,gpc_noop,gpc_bckc,gpc_noop, gpc_preq,gpc_pre, gpc_preq,gpc_pre },
+   {gpc_g8,  gpc_g8,  gpc_noop,gpc_noop, gpc_glin,gpc_glin,gpc_lin, gpc_lin },
+   {gpc_g8b, gpc_g8,  gpc_bckc,gpc_noop, gpc_gprq,gpc_gpre,gpc_preq,gpc_pre },
+   {gpc_sRGB,gpc_sRGB,gpc_sRGB,gpc_sRGB, gpc_noop,gpc_noop,gpc_noop,gpc_noop},
+   {gpc_b16g,gpc_unpg,gpc_b16c,gpc_unpc, gpc_A,   gpc_noop,gpc_A,   gpc_noop},
+   {gpc_sG,  gpc_sG,  gpc_sRGB,gpc_sRGB, gpc_g16, gpc_g16, gpc_noop,gpc_noop},
+   {gpc_gb16,gpc_sGp, gpc_cb16,gpc_sCp,  gpc_g16, gpc_g16q,gpc_A,   gpc_noop}
+};
+
+/* The array is repeated for the cases where both the input and output are color
+ * mapped because then different algorithms are used.
+ */
+static void (* const gpc_fn_colormapped[8/*in*/][8/*out*/])
+   (Pixel *out, const Pixel *in, const Background *back) =
+{
+/*out: G-8     GA-8     RGB-8    RGBA-8    G-16     GA-16   RGB-16  RGBA-16 */
+   {gpc_noop,gpc_noop,gpc_noop,gpc_noop, gpc_lin, gpc_lin, gpc_lin, gpc_lin },
+   {gpc_bckg,gpc_noop,gpc_bckc,gpc_noop, gpc_preq,gpc_pre, gpc_preq,gpc_pre },
+   {gpc_g8,  gpc_g8,  gpc_noop,gpc_noop, gpc_glin,gpc_glin,gpc_lin, gpc_lin },
+   {gpc_g8b, gpc_g8,  gpc_bckc,gpc_noop, gpc_gprq,gpc_gpre,gpc_preq,gpc_pre },
+   {gpc_sRGB,gpc_sRGB,gpc_sRGB,gpc_sRGB, gpc_noop,gpc_noop,gpc_noop,gpc_noop},
+   {gpc_b16g,gpc_unpg,gpc_b16c,gpc_unpc, gpc_A,   gpc_noop,gpc_A,   gpc_noop},
+   {gpc_sG,  gpc_sG,  gpc_sRGB,gpc_sRGB, gpc_g16, gpc_g16, gpc_noop,gpc_noop},
+   {gpc_gb16,gpc_sGp, gpc_cb16,gpc_sCp,  gpc_g16, gpc_g16q,gpc_A,   gpc_noop}
+};
+
+/* The error arrays record the error in the same matrix; 64 entries, however
+ * the different algorithms used in libpng for colormap and direct conversions
+ * mean that four separate matrices are used (for each combination of
+ * colormapped and direct.)
+ *
+ * In some cases the conversion between sRGB formats goes via a linear
+ * intermediate; an sRGB to linear conversion (as above) is followed by a simple
+ * linear to sRGB step with no other conversions.  This is done by a separate
+ * error array from an arbitrary 'in' format to one of the four basic outputs
+ * (since final output is always sRGB not colormapped).
+ *
+ * These arrays may be modified if the --accumulate flag is set during the run;
+ * then instead of logging errors they are simply added in.
+ *
+ * The three entries are currently for transparent, partially transparent and
+ * opaque input pixel values.  Notice that alpha should be exact in each case.
+ *
+ * Errors in alpha should only occur when converting from a direct format
+ * to a colormapped format, when alpha is effectively smashed (so large
+ * errors can occur.)  There should be no error in the '0' and 'opaque'
+ * values.  The fourth entry in the array is used for the alpha error (and it
+ * should always be zero for the 'via linear' case since this is never color
+ * mapped.)
+ *
+ * Mapping to a colormap smashes the colors, it is necessary to have separate
+ * values for these cases because they are much larger; it is very much
+ * impossible to obtain a reasonable result, these are held in
+ * gpc_error_to_colormap.
+ */
+#if PNG_FORMAT_FLAG_COLORMAP == 8 /* extra check also required */
+#  include "pngstest-errors.h" /* machine generated */
+#endif /* COLORMAP flag check */
+#endif /* flag checks */
+
+typedef struct
+{
+   /* Basic pixel information: */
+   Image*       in_image;   /* Input image */
+   const Image* out_image;  /* Output image */
+
+   /* 'background' is the value passed to the gpc_ routines, it may be NULL if
+    * it should not be used (*this* program has an error if it crashes as a
+    * result!)
+    */
+   Background        background_color;
+   const Background* background;
+
+   /* Precalculated values: */
+   int          in_opaque;   /* Value of input alpha that is opaque */
+   int          is_palette;  /* Sample values come from the palette */
+   int          accumulate;  /* Accumlate component errors (don't log) */
+   int          output_8bit; /* Output is 8-bit (else 16-bit) */
+
+   void (*in_gp)(Pixel*, png_const_voidp);
+   void (*out_gp)(Pixel*, png_const_voidp);
+
+   void (*transform)(Pixel *out, const Pixel *in, const Background *back);
+      /* A function to perform the required transform */
+
+   void (*from_linear)(Pixel *out, const Pixel *in, const Background *back);
+      /* For 'via_linear' transforms the final, from linear, step, else NULL */
+
+   png_uint_16 error[4];
+      /* Three error values for transparent, partially transparent and opaque
+       * input pixels (in turn).
+       */
+
+   png_uint_16 *error_ptr;
+      /* Where these are stored in the static array (for 'accumulate') */
+}
+Transform;
+
+/* Return a 'transform' as above for the given format conversion. */
+static void
+transform_from_formats(Transform *result, Image *in_image,
+   const Image *out_image, png_const_colorp background, int via_linear)
+{
+   png_uint_32 in_format, out_format;
+   png_uint_32 in_base, out_base;
+
+   memset(result, 0, sizeof *result);
+
+   /* Store the original images for error messages */
+   result->in_image = in_image;
+   result->out_image = out_image;
+
+   in_format = in_image->image.format;
+   out_format = out_image->image.format;
+
+   if (in_format & PNG_FORMAT_FLAG_LINEAR)
+      result->in_opaque = 65535;
+   else
+      result->in_opaque = 255;
+
+   result->output_8bit = (out_format & PNG_FORMAT_FLAG_LINEAR) == 0;
+
+   result->is_palette = 0; /* set by caller if required */
+   result->accumulate = (in_image->opts & ACCUMULATE) != 0;
+
+   /* The loaders (which need the ordering information) */
+   result->in_gp = get_pixel(in_format);
+   result->out_gp = get_pixel(out_format);
+
+   /* Remove the ordering information: */
+   in_format &= BASE_FORMATS | PNG_FORMAT_FLAG_COLORMAP;
+   in_base = in_format & BASE_FORMATS;
+   out_format &= BASE_FORMATS | PNG_FORMAT_FLAG_COLORMAP;
+   out_base = out_format & BASE_FORMATS;
+
+   if (via_linear)
+   {
+      /* Check for an error in this program: */
+      if (out_format & (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLORMAP))
+      {
+         fprintf(stderr, "internal transform via linear error 0x%x->0x%x\n",
+            in_format, out_format);
+         exit(1);
+      }
+
+      result->transform = gpc_fn[in_base][out_base | PNG_FORMAT_FLAG_LINEAR];
+      result->from_linear = gpc_fn[out_base | PNG_FORMAT_FLAG_LINEAR][out_base];
+      result->error_ptr = gpc_error_via_linear[in_format][out_format];
+   }
+
+   else if (~in_format & out_format & PNG_FORMAT_FLAG_COLORMAP)
+   {
+      /* The input is not colormapped but the output is, the errors will
+       * typically be large (only the grayscale-no-alpha case permits preserving
+       * even 8-bit values.)
+       */
+      result->transform = gpc_fn[in_base][out_base];
+      result->from_linear = NULL;
+      result->error_ptr = gpc_error_to_colormap[in_base][out_base];
+   }
+
+   else
+   {
+      /* The caller handles the colormap->pixel value conversion, so the
+       * transform function just gets a pixel value, however because libpng
+       * currently contains a different implementation for mapping a colormap if
+       * both input and output are colormapped we need different conversion
+       * functions to deal with errors in the libpng implementation.
+       */
+      if (in_format & out_format & PNG_FORMAT_FLAG_COLORMAP)
+         result->transform = gpc_fn_colormapped[in_base][out_base];
+      else
+         result->transform = gpc_fn[in_base][out_base];
+      result->from_linear = NULL;
+      result->error_ptr = gpc_error[in_format][out_format];
+   }
+
+   /* Follow the libpng simplified API rules to work out what to pass to the gpc
+    * routines as a background value, if one is not required pass NULL so that
+    * this program crashes in the even of a programming error.
+    */
+   result->background = NULL; /* default: not required */
+
+   /* Rule 1: background only need be supplied if alpha is to be removed */
+   if (in_format & ~out_format & PNG_FORMAT_FLAG_ALPHA)
+   {
+      /* The input value is 'NULL' to use the background and (otherwise) an sRGB
+       * background color (to use a solid color).  The code above uses a fixed
+       * byte value, BUFFER_INIT8, for buffer even for 16-bit output.  For
+       * linear (16-bit) output the sRGB background color is ignored; the
+       * composition is always on the background (so BUFFER_INIT8 * 257), except
+       * that for the colormap (i.e. linear colormapped output) black is used.
+       */
+      result->background = &result->background_color;
+
+      if (out_format & PNG_FORMAT_FLAG_LINEAR || via_linear)
+      {
+         if (out_format & PNG_FORMAT_FLAG_COLORMAP)
+         {
+            result->background_color.ir =
+               result->background_color.ig =
+               result->background_color.ib = 0;
+            result->background_color.dr =
+               result->background_color.dg =
+               result->background_color.db = 0;
+         }
+
+         else
+         {
+            result->background_color.ir =
+               result->background_color.ig =
+               result->background_color.ib = BUFFER_INIT8 * 257;
+            result->background_color.dr =
+               result->background_color.dg =
+               result->background_color.db = 0;
+         }
+      }
+
+      else /* sRGB output */
+      {
+         if (background != NULL)
+         {
+            if (out_format & PNG_FORMAT_FLAG_COLOR)
+            {
+               result->background_color.ir = background->red;
+               result->background_color.ig = background->green;
+               result->background_color.ib = background->blue;
+               /* TODO: sometimes libpng uses the power law conversion here, how
+                * to handle this?
+                */
+               result->background_color.dr = sRGB_to_d[background->red];
+               result->background_color.dg = sRGB_to_d[background->green];
+               result->background_color.db = sRGB_to_d[background->blue];
+            }
+
+            else /* grayscale: libpng only looks at 'g' */
+            {
+               result->background_color.ir =
+                  result->background_color.ig =
+                  result->background_color.ib = background->green;
+               /* TODO: sometimes libpng uses the power law conversion here, how
+                * to handle this?
+                */
+               result->background_color.dr =
+                  result->background_color.dg =
+                  result->background_color.db = sRGB_to_d[background->green];
+            }
+         }
+
+         else if ((out_format & PNG_FORMAT_FLAG_COLORMAP) == 0)
+         {
+            result->background_color.ir =
+               result->background_color.ig =
+               result->background_color.ib = BUFFER_INIT8;
+            /* TODO: sometimes libpng uses the power law conversion here, how
+             * to handle this?
+             */
+            result->background_color.dr =
+               result->background_color.dg =
+               result->background_color.db = sRGB_to_d[BUFFER_INIT8];
+         }
+
+         /* Else the output is colormapped and a background color must be
+          * provided; if pngstest crashes then that is a bug in this program
+          * (though libpng should png_error as well.)
+          */
+         else
+            result->background = NULL;
+      }
+   }
+
+   if (result->background == NULL)
+   {
+      result->background_color.ir =
+         result->background_color.ig =
+         result->background_color.ib = -1; /* not used */
+      result->background_color.dr =
+         result->background_color.dg =
+         result->background_color.db = 1E30; /* not used */
+   }
+
+
+   /* Copy the error values into the Transform: */
+   result->error[0] = result->error_ptr[0];
+   result->error[1] = result->error_ptr[1];
+   result->error[2] = result->error_ptr[2];
+   result->error[3] = result->error_ptr[3];
+}
+
+
+/* Compare two pixels.
+ *
+ * OLD error values:
+static int error_to_linear = 811; * by experiment *
+static int error_to_linear_grayscale = 424; * by experiment *
+static int error_to_sRGB = 6; * by experiment *
+static int error_to_sRGB_grayscale = 17; * libpng error by calculation +
+                                            2 by experiment *
+static int error_in_compose = 2; * by experiment *
+static int error_in_premultiply = 1;
+ *
+ * The following is *just* the result of a round trip from 8-bit sRGB to linear
+ * then back to 8-bit sRGB when it is done by libpng.  There are two problems:
+ *
+ * 1) libpng currently uses a 2.2 power law with no linear segment, this results
+ * in instability in the low values and even with 16-bit precision sRGB(1) ends
+ * up mapping to sRGB(0) as a result of rounding in the 16-bit representation.
+ * This gives an error of 1 in the handling of value 1 only.
+ *
+ * 2) libpng currently uses an intermediate 8-bit linear value in gamma
+ * correction of 8-bit values.  This results in many more errors, the worse of
+ * which is mapping sRGB(14) to sRGB(0).
+ *
+ * The general 'error_via_linear' is more complex because of pre-multiplication,
+ * this compounds the 8-bit errors according to the alpha value of the pixel.
+ * As a result 256 values are pre-calculated for error_via_linear.
+ */
+#if 0
+static int error_in_libpng_gamma;
+static int error_via_linear[256]; /* Indexed by 8-bit alpha */
+
+static void
+init_error_via_linear(void)
+{
+   int alpha;
+
+   error_via_linear[0] = 255; /* transparent pixel */
+
+   for (alpha=1; alpha<=255; ++alpha)
+   {
+      /* 16-bit values less than 128.5 get rounded to 8-bit 0 and so the worst
+       * case error arises with 16-bit 128.5, work out what sRGB
+       * (non-associated) value generates 128.5; any value less than this is
+       * going to map to 0, so the worst error is floor(value).
+       *
+       * Note that errors are considerably higher (more than a factor of 2)
+       * because libpng uses a simple power law for sRGB data at present.
+       *
+       * Add .1 for arithmetic errors inside libpng.
+       */
+      double v = floor(255*pow(.5/*(128.5 * 255 / 65535)*/ / alpha, 1/2.2)+.1);
+
+      error_via_linear[alpha] = (int)v;
+   }
+
+   /* This is actually 14.99, but, despite the closeness to 15, 14 seems to work
+    * ok in this case.
+    */
+   error_in_libpng_gamma = 14;
+}
+#endif
+
+static void
+print_pixel(char string[64], const Pixel *pixel, png_uint_32 format)
+{
+   switch (format & (PNG_FORMAT_FLAG_ALPHA|PNG_FORMAT_FLAG_COLOR))
+   {
+      case 0:
+         sprintf(string, "%s(%d)", format_names[format], pixel->g);
+         break;
+
+      case PNG_FORMAT_FLAG_ALPHA:
+         sprintf(string, "%s(%d,%d)", format_names[format], pixel->g,
+            pixel->a);
+         break;
+
+      case PNG_FORMAT_FLAG_COLOR:
+         sprintf(string, "%s(%d,%d,%d)", format_names[format],
+            pixel->r, pixel->g, pixel->b);
+         break;
+
+      case PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA:
+         sprintf(string, "%s(%d,%d,%d,%d)", format_names[format],
+            pixel->r, pixel->g, pixel->b, pixel->a);
+         break;
+
+      default:
+         sprintf(string, "invalid-format");
+         break;
+   }
+}
+
+static int
+logpixel(const Transform *transform, png_uint_32 x, png_uint_32 y,
+   const Pixel *in, const Pixel *calc, const Pixel *out, const char *reason)
+{
+   const png_uint_32 in_format = transform->in_image->image.format;
+   const png_uint_32 out_format = transform->out_image->image.format;
+
+   png_uint_32 back_format = out_format & ~PNG_FORMAT_FLAG_ALPHA;
+   const char *via_linear = "";
+
+   char pixel_in[64], pixel_calc[64], pixel_out[64], pixel_loc[64];
+   char background_info[100];
+
+   print_pixel(pixel_in, in, in_format);
+   print_pixel(pixel_calc, calc, out_format);
+   print_pixel(pixel_out, out, out_format);
+
+   if (transform->is_palette)
+      sprintf(pixel_loc, "palette: %lu", (unsigned long)y);
+   else
+      sprintf(pixel_loc, "%lu,%lu", (unsigned long)x, (unsigned long)y);
+
+   if (transform->from_linear != NULL)
+   {
+      via_linear = " (via linear)";
+      /* And as a result the *read* format which did any background processing
+       * was itself linear, so the background color information is also
+       * linear.
+       */
+      back_format |= PNG_FORMAT_FLAG_LINEAR;
+   }
+
+   if (transform->background != NULL)
+   {
+      Pixel back;
+      char pixel_back[64];
+
+      back.r = transform->background->ir;
+      back.g = transform->background->ig;
+      back.b = transform->background->ib;
+      back.a = -1; /* not used */
+
+      print_pixel(pixel_back, &back, back_format);
+      sprintf(background_info, " on background %s", pixel_back);
+   }
+
+   else
+      background_info[0] = 0;
+
+   if (transform->in_image->file_name != transform->out_image->file_name)
+   {
+      char error_buffer[512];
+      sprintf(error_buffer,
+         "(%s) %s error%s:\n %s%s ->\n       %s\n  not: %s.\n"
+         "Use --preserve and examine: ", pixel_loc, reason, via_linear,
+         pixel_in, background_info, pixel_out, pixel_calc);
+      return logerror(transform->in_image, transform->in_image->file_name,
+         error_buffer, transform->out_image->file_name);
+   }
+
+   else
+   {
+      char error_buffer[512];
+      sprintf(error_buffer,
+         "(%s) %s error%s:\n %s%s ->\n       %s\n  not: %s.\n"
+         " The error happened when reading the original file with this format.",
+         pixel_loc, reason, via_linear, pixel_in, background_info, pixel_out,
+         pixel_calc);
+      return logerror(transform->in_image, transform->in_image->file_name,
+         error_buffer, "");
+   }
+}
+
+static int
+cmppixel(Transform *transform, png_const_voidp in, png_const_voidp out,
+   png_uint_32 x, png_uint_32 y/*or palette index*/)
+{
+   int maxerr;
+   png_const_charp errmsg;
+   Pixel pixel_in, pixel_calc, pixel_out;
+
+   transform->in_gp(&pixel_in, in);
+
+   if (transform->from_linear == NULL)
+      transform->transform(&pixel_calc, &pixel_in, transform->background);
+
+   else
+   {
+      transform->transform(&pixel_out, &pixel_in, transform->background);
+      transform->from_linear(&pixel_calc, &pixel_out, NULL);
+   }
+
+   transform->out_gp(&pixel_out, out);
+
+   /* Eliminate the case where the input and output values match exactly. */
+   if (pixel_calc.a == pixel_out.a && pixel_calc.r == pixel_out.r &&
+      pixel_calc.g == pixel_out.g && pixel_calc.b == pixel_out.b)
+      return 1;
+
+   /* Eliminate the case where the output pixel is transparent and the output
+    * is 8-bit - any component values are valid.  Don't check the input alpha
+    * here to also skip the 16-bit small alpha cases.
+    */
+   if (transform->output_8bit && pixel_calc.a == 0 && pixel_out.a == 0)
+      return 1;
+
+   /* Check for alpha errors first; an alpha error can damage the components too
+    * so avoid spurious checks on components if one is found.
+    */
+   errmsg = NULL;
+   {
+      int err_a = abs(pixel_calc.a-pixel_out.a);
+
+      if (err_a > transform->error[3])
+      {
+         /* If accumulating check the components too */
+         if (transform->accumulate)
+            transform->error[3] = (png_uint_16)err_a;
+
+         else
+            errmsg = "alpha";
+      }
+   }
+
+   /* Now if *either* of the output alphas are 0 but alpha is within tolerance
+    * eliminate the 8-bit component comparison.
+    */
+   if (errmsg == NULL && transform->output_8bit &&
+      (pixel_calc.a == 0 || pixel_out.a == 0))
+      return 1;
+
+   if (errmsg == NULL) /* else just signal an alpha error */
+   {
+      int err_r = abs(pixel_calc.r - pixel_out.r);
+      int err_g = abs(pixel_calc.g - pixel_out.g);
+      int err_b = abs(pixel_calc.b - pixel_out.b);
+      int limit;
+
+      if ((err_r | err_g | err_b) == 0)
+         return 1; /* exact match */
+
+      /* Mismatch on a component, check the input alpha */
+      if (pixel_in.a >= transform->in_opaque)
+      {
+         errmsg = "opaque component";
+         limit = 2; /* opaque */
+      }
+
+      else if (pixel_in.a > 0)
+      {
+         errmsg = "alpha component";
+         limit = 1; /* partially transparent */
+      }
+
+      else
+      {
+         errmsg = "transparent component (background)";
+         limit = 0; /* transparent */
+      }
+
+      maxerr = err_r;
+      if (maxerr < err_g) maxerr = err_g;
+      if (maxerr < err_b) maxerr = err_b;
+
+      if (maxerr <= transform->error[limit])
+         return 1; /* within the error limits */
+
+      /* Handle a component mis-match; log it, just return an error code, or
+       * accumulate it.
+       */
+      if (transform->accumulate)
+      {
+         transform->error[limit] = (png_uint_16)maxerr;
+         return 1; /* to cause the caller to keep going */
+      }
+   }
+
+   /* Failure to match and not accumulating, so the error must be logged. */
+   return logpixel(transform, x, y, &pixel_in, &pixel_calc, &pixel_out, errmsg);
+}
+
+static png_byte
+component_loc(png_byte loc[4], png_uint_32 format)
+{
+   /* Given a format return the number of channels and the location of
+    * each channel.
+    *
+    * The mask 'loc' contains the component offset of the channels in the
+    * following order.  Note that if 'format' is grayscale the entries 1-3 must
+    * all contain the location of the gray channel.
+    *
+    * 0: alpha
+    * 1: red or gray
+    * 2: green or gray
+    * 3: blue or gray
+    */
+   png_byte channels;
+
+   if (format & PNG_FORMAT_FLAG_COLOR)
+   {
+      channels = 3;
+
+      loc[2] = 1;
+
+#     ifdef PNG_FORMAT_BGR_SUPPORTED
+         if (format & PNG_FORMAT_FLAG_BGR)
+         {
+            loc[1] = 2;
+            loc[3] = 0;
+         }
+
+         else
+#     endif
+      {
+         loc[1] = 0;
+         loc[3] = 2;
+      }
+   }
+
+   else
+   {
+      channels = 1;
+      loc[1] = loc[2] = loc[3] = 0;
+   }
+
+   if (format & PNG_FORMAT_FLAG_ALPHA)
+   {
+#     ifdef PNG_FORMAT_AFIRST_SUPPORTED
+         if (format & PNG_FORMAT_FLAG_AFIRST)
+         {
+            loc[0] = 0;
+            ++loc[1];
+            ++loc[2];
+            ++loc[3];
+         }
+
+         else
+#     endif
+         loc[0] = channels;
+
+      ++channels;
+   }
+
+   else
+      loc[0] = 4; /* not present */
+
+   return channels;
+}
+
+/* Compare two images, the original 'a', which was written out then read back in
+ * to * give image 'b'.  The formats may have been changed.
+ */
+static int
+compare_two_images(Image *a, Image *b, int via_linear,
+   png_const_colorp background)
+{
+   ptrdiff_t stridea = a->stride;
+   ptrdiff_t strideb = b->stride;
+   png_const_bytep rowa = a->buffer+16;
+   png_const_bytep rowb = b->buffer+16;
+   const png_uint_32 width = a->image.width;
+   const png_uint_32 height = a->image.height;
+   const png_uint_32 formata = a->image.format;
+   const png_uint_32 formatb = b->image.format;
+   const unsigned int a_sample = PNG_IMAGE_SAMPLE_SIZE(formata);
+   const unsigned int b_sample = PNG_IMAGE_SAMPLE_SIZE(formatb);
+   int alpha_added, alpha_removed;
+   int bchannels;
+   int btoa[4];
+   png_uint_32 y;
+   Transform tr;
+
+   /* This should never happen: */
+   if (width != b->image.width || height != b->image.height)
+      return logerror(a, a->file_name, ": width x height changed: ",
+         b->file_name);
+
+   /* Set up the background and the transform */
+   transform_from_formats(&tr, a, b, background, via_linear);
+
+   /* Find the first row and inter-row space. */
+   if (!(formata & PNG_FORMAT_FLAG_COLORMAP) &&
+      (formata & PNG_FORMAT_FLAG_LINEAR))
+      stridea *= 2;
+
+   if (!(formatb & PNG_FORMAT_FLAG_COLORMAP) &&
+      (formatb & PNG_FORMAT_FLAG_LINEAR))
+      strideb *= 2;
+
+   if (stridea < 0) rowa += (height-1) * (-stridea);
+   if (strideb < 0) rowb += (height-1) * (-strideb);
+
+   /* First shortcut the two colormap case by comparing the image data; if it
+    * matches then we expect the colormaps to match, although this is not
+    * absolutely necessary for an image match.  If the colormaps fail to match
+    * then there is a problem in libpng.
+    */
+   if (formata & formatb & PNG_FORMAT_FLAG_COLORMAP)
+   {
+      /* Only check colormap entries that actually exist; */
+      png_const_bytep ppa, ppb;
+      int match;
+      png_byte in_use[256], amax = 0, bmax = 0;
+
+      memset(in_use, 0, sizeof in_use);
+
+      ppa = rowa;
+      ppb = rowb;
+
+      /* Do this the slow way to accumulate the 'in_use' flags, don't break out
+       * of the loop until the end; this validates the color-mapped data to
+       * ensure all pixels are valid color-map indexes.
+       */
+      for (y=0, match=1; y<height && match; ++y, ppa += stridea, ppb += strideb)
+      {
+         png_uint_32 x;
+
+         for (x=0; x<width; ++x)
+         {
+            png_byte bval = ppb[x];
+            png_byte aval = ppa[x];
+
+            if (bval > bmax)
+               bmax = bval;
+
+            if (bval != aval)
+               match = 0;
+
+            in_use[aval] = 1;
+            if (aval > amax)
+               amax = aval;
+         }
+      }
+
+      /* If the buffers match then the colormaps must too. */
+      if (match)
+      {
+         /* Do the color-maps match, entry by entry?  Only check the 'in_use'
+          * entries.  An error here should be logged as a color-map error.
+          */
+         png_const_bytep a_cmap = (png_const_bytep)a->colormap;
+         png_const_bytep b_cmap = (png_const_bytep)b->colormap;
+         int result = 1; /* match by default */
+
+         /* This is used in logpixel to get the error message correct. */
+         tr.is_palette = 1;
+
+         for (y=0; y<256; ++y, a_cmap += a_sample, b_cmap += b_sample)
+            if (in_use[y])
+         {
+            /* The colormap entries should be valid, but because libpng doesn't
+             * do any checking at present the original image may contain invalid
+             * pixel values.  These cause an error here (at present) unless
+             * accumulating errors in which case the program just ignores them.
+             */
+            if (y >= a->image.colormap_entries)
+            {
+               if ((a->opts & ACCUMULATE) == 0)
+               {
+                  char pindex[9];
+                  sprintf(pindex, "%lu[%lu]", (unsigned long)y,
+                     (unsigned long)a->image.colormap_entries);
+                  logerror(a, a->file_name, ": bad pixel index: ", pindex);
+               }
+               result = 0;
+            }
+
+            else if (y >= b->image.colormap_entries)
+            {
+               if ((b->opts & ACCUMULATE) == 0)
+                  {
+                  char pindex[9];
+                  sprintf(pindex, "%lu[%lu]", (unsigned long)y,
+                     (unsigned long)b->image.colormap_entries);
+                  logerror(b, b->file_name, ": bad pixel index: ", pindex);
+                  }
+               result = 0;
+            }
+
+            /* All the mismatches are logged here; there can only be 256! */
+            else if (!cmppixel(&tr, a_cmap, b_cmap, 0, y))
+               result = 0;
+         }
+
+         /* If reqested copy the error values back from the Transform. */
+         if (a->opts & ACCUMULATE)
+         {
+            tr.error_ptr[0] = tr.error[0];
+            tr.error_ptr[1] = tr.error[1];
+            tr.error_ptr[2] = tr.error[2];
+            tr.error_ptr[3] = tr.error[3];
+            result = 1; /* force a continue */
+         }
+
+         return result;
+      }
+
+      /* else the image buffers don't match pixel-wise so compare sample values
+       * instead, but first validate that the pixel indexes are in range (but
+       * only if not accumulating, when the error is ignored.)
+       */
+      else if ((a->opts & ACCUMULATE) == 0)
+      {
+         /* Check the original image first,
+          * TODO: deal with input images with bad pixel values?
+          */
+         if (amax >= a->image.colormap_entries)
+         {
+            char pindex[9];
+            sprintf(pindex, "%d[%lu]", amax,
+               (unsigned long)a->image.colormap_entries);
+            return logerror(a, a->file_name, ": bad pixel index: ", pindex);
+         }
+
+         else if (bmax >= b->image.colormap_entries)
+         {
+            char pindex[9];
+            sprintf(pindex, "%d[%lu]", bmax,
+               (unsigned long)b->image.colormap_entries);
+            return logerror(b, b->file_name, ": bad pixel index: ", pindex);
+         }
+      }
+   }
+
+   /* We can directly compare pixel values without the need to use the read
+    * or transform support (i.e. a memory compare) if:
+    *
+    * 1) The bit depth has not changed.
+    * 2) RGB to grayscale has not been done (the reverse is ok; we just compare
+    *    the three RGB values to the original grayscale.)
+    * 3) An alpha channel has not been removed from an 8-bit format, or the
+    *    8-bit alpha value of the pixel was 255 (opaque).
+    *
+    * If an alpha channel has been *added* then it must have the relevant opaque
+    * value (255 or 65535).
+    *
+    * The fist two the tests (in the order given above) (using the boolean
+    * equivalence !a && !b == !(a || b))
+    */
+   if (!(((formata ^ formatb) & PNG_FORMAT_FLAG_LINEAR) |
+      (formata & (formatb ^ PNG_FORMAT_FLAG_COLOR) & PNG_FORMAT_FLAG_COLOR)))
+   {
+      /* Was an alpha channel changed? */
+      const png_uint_32 alpha_changed = (formata ^ formatb) &
+         PNG_FORMAT_FLAG_ALPHA;
+
+      /* Was an alpha channel removed?  (The third test.)  If so the direct
+       * comparison is only possible if the input alpha is opaque.
+       */
+      alpha_removed = (formata & alpha_changed) != 0;
+
+      /* Was an alpha channel added? */
+      alpha_added = (formatb & alpha_changed) != 0;
+
+      /* The channels may have been moved between input and output, this finds
+       * out how, recording the result in the btoa array, which says where in
+       * 'a' to find each channel of 'b'.  If alpha was added then btoa[alpha]
+       * ends up as 4 (and is not used.)
+       */
+      {
+         int i;
+         png_byte aloc[4];
+         png_byte bloc[4];
+
+         /* The following are used only if the formats match, except that
+          * 'bchannels' is a flag for matching formats.  btoa[x] says, for each
+          * channel in b, where to find the corresponding value in a, for the
+          * bchannels.  achannels may be different for a gray to rgb transform
+          * (a will be 1 or 2, b will be 3 or 4 channels.)
+          */
+         (void)component_loc(aloc, formata);
+         bchannels = component_loc(bloc, formatb);
+
+         /* Hence the btoa array. */
+         for (i=0; i<4; ++i) if (bloc[i] < 4)
+            btoa[bloc[i]] = aloc[i]; /* may be '4' for alpha */
+
+         if (alpha_added)
+            alpha_added = bloc[0]; /* location of alpha channel in image b */
+
+         else
+            alpha_added = 4; /* Won't match an image b channel */
+
+         if (alpha_removed)
+            alpha_removed = aloc[0]; /* location of alpha channel in image a */
+
+         else
+            alpha_removed = 4;
+      }
+   }
+
+   else
+   {
+      /* Direct compare is not possible, cancel out all the corresponding local
+       * variables.
+       */
+      bchannels = 0;
+      alpha_removed = alpha_added = 4;
+      btoa[3] = btoa[2] = btoa[1] = btoa[0] = 4; /* 4 == not present */
+   }
+
+   for (y=0; y<height; ++y, rowa += stridea, rowb += strideb)
+   {
+      png_const_bytep ppa, ppb;
+      png_uint_32 x;
+
+      for (x=0, ppa=rowa, ppb=rowb; x<width; ++x)
+      {
+         png_const_bytep psa, psb;
+
+         if (formata & PNG_FORMAT_FLAG_COLORMAP)
+            psa = (png_const_bytep)a->colormap + a_sample * *ppa++;
+         else
+            psa = ppa, ppa += a_sample;
+
+         if (formatb & PNG_FORMAT_FLAG_COLORMAP)
+            psb = (png_const_bytep)b->colormap + b_sample * *ppb++;
+         else
+            psb = ppb, ppb += b_sample;
+
+         /* Do the fast test if possible. */
+         if (bchannels)
+         {
+            /* Check each 'b' channel against either the corresponding 'a'
+             * channel or the opaque alpha value, as appropriate.  If
+             * alpha_removed value is set (not 4) then also do this only if the
+             * 'a' alpha channel (alpha_removed) is opaque; only relevant for
+             * the 8-bit case.
+             */
+            if (formatb & PNG_FORMAT_FLAG_LINEAR) /* 16-bit checks */
+            {
+               png_const_uint_16p pua = aligncastconst(png_const_uint_16p, psa);
+               png_const_uint_16p pub = aligncastconst(png_const_uint_16p, psb);
+
+               switch (bchannels)
+               {
+                  case 4:
+                     if (pua[btoa[3]] != pub[3]) break;
+                  case 3:
+                     if (pua[btoa[2]] != pub[2]) break;
+                  case 2:
+                     if (pua[btoa[1]] != pub[1]) break;
+                  case 1:
+                     if (pua[btoa[0]] != pub[0]) break;
+                     if (alpha_added != 4 && pub[alpha_added] != 65535) break;
+                     continue; /* x loop */
+                  default:
+                     break; /* impossible */
+               }
+            }
+
+            else if (alpha_removed == 4 || psa[alpha_removed] == 255)
+            {
+               switch (bchannels)
+               {
+                  case 4:
+                     if (psa[btoa[3]] != psb[3]) break;
+                  case 3:
+                     if (psa[btoa[2]] != psb[2]) break;
+                  case 2:
+                     if (psa[btoa[1]] != psb[1]) break;
+                  case 1:
+                     if (psa[btoa[0]] != psb[0]) break;
+                     if (alpha_added != 4 && psb[alpha_added] != 255) break;
+                     continue; /* x loop */
+                  default:
+                     break; /* impossible */
+               }
+            }
+         }
+
+         /* If we get to here the fast match failed; do the slow match for this
+          * pixel.
+          */
+         if (!cmppixel(&tr, psa, psb, x, y) && (a->opts & KEEP_GOING) == 0)
+            return 0; /* error case */
+      }
+   }
+
+   /* If reqested copy the error values back from the Transform. */
+   if (a->opts & ACCUMULATE)
+   {
+      tr.error_ptr[0] = tr.error[0];
+      tr.error_ptr[1] = tr.error[1];
+      tr.error_ptr[2] = tr.error[2];
+      tr.error_ptr[3] = tr.error[3];
+   }
+
+   return 1;
+}
+
+/* Read the file; how the read gets done depends on which of input_file and
+ * input_memory have been set.
+ */
+static int
+read_file(Image *image, png_uint_32 format, png_const_colorp background)
+{
+   memset(&image->image, 0, sizeof image->image);
+   image->image.version = PNG_IMAGE_VERSION;
+
+   if (image->input_memory != NULL)
+   {
+      if (!png_image_begin_read_from_memory(&image->image, image->input_memory,
+         image->input_memory_size))
+         return logerror(image, "memory init: ", image->file_name, "");
+   }
+
+#  ifdef PNG_STDIO_SUPPORTED
+      else if (image->input_file != NULL)
+      {
+         if (!png_image_begin_read_from_stdio(&image->image, image->input_file))
+            return logerror(image, "stdio init: ", image->file_name, "");
+      }
+
+      else
+      {
+         if (!png_image_begin_read_from_file(&image->image, image->file_name))
+            return logerror(image, "file init: ", image->file_name, "");
+      }
+#  else
+      else
+      {
+         return logerror(image, "unsupported file/stdio init: ",
+            image->file_name, "");
+      }
+#  endif
+
+   /* This must be set after the begin_read call: */
+   if (image->opts & sRGB_16BIT)
+      image->image.flags |= PNG_IMAGE_FLAG_16BIT_sRGB;
+
+   /* Have an initialized image with all the data we need plus, maybe, an
+    * allocated file (myfile) or buffer (mybuffer) that need to be freed.
+    */
+   {
+      int result;
+      png_uint_32 image_format;
+
+      /* Print both original and output formats. */
+      image_format = image->image.format;
+
+      if (image->opts & VERBOSE)
+      {
+         printf("%s %lu x %lu %s -> %s", image->file_name,
+            (unsigned long)image->image.width,
+            (unsigned long)image->image.height,
+            format_names[image_format & FORMAT_MASK],
+            (format & FORMAT_NO_CHANGE) != 0 || image->image.format == format
+            ? "no change" : format_names[format & FORMAT_MASK]);
+
+         if (background != NULL)
+            printf(" background(%d,%d,%d)\n", background->red,
+               background->green, background->blue);
+         else
+            printf("\n");
+
+         fflush(stdout);
+      }
+
+      /* 'NO_CHANGE' combined with the color-map flag forces the base format
+       * flags to be set on read to ensure that the original representation is
+       * not lost in the pass through a colormap format.
+       */
+      if ((format & FORMAT_NO_CHANGE) != 0)
+      {
+         if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+            (image_format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+            format = (image_format & ~BASE_FORMATS) | (format & BASE_FORMATS);
+
+         else
+            format = image_format;
+      }
+
+      image->image.format = format;
+
+      image->stride = PNG_IMAGE_ROW_STRIDE(image->image) + image->stride_extra;
+      allocbuffer(image);
+
+      result = png_image_finish_read(&image->image, background,
+         image->buffer+16, (png_int_32)image->stride, image->colormap);
+
+      checkbuffer(image, image->file_name);
+
+      if (result)
+         return checkopaque(image);
+
+      else
+         return logerror(image, image->file_name, ": image read failed", "");
+   }
+}
+
+/* Reads from a filename, which must be in image->file_name, but uses
+ * image->opts to choose the method.  The file is always read in its native
+ * format (the one the simplified API suggests).
+ */
+static int
+read_one_file(Image *image)
+{
+   if (!(image->opts & READ_FILE) || (image->opts & USE_STDIO))
+   {
+      /* memory or stdio. */
+      FILE *f = fopen(image->file_name, "rb");
+
+      if (f != NULL)
+      {
+         if (image->opts & READ_FILE)
+            image->input_file = f;
+
+         else /* memory */
+         {
+            if (fseek(f, 0, SEEK_END) == 0)
+            {
+               long int cb = ftell(f);
+
+               if (cb > 0)
+               {
+#ifndef __COVERITY__
+                  if ((unsigned long int)cb <= (size_t)~(size_t)0)
+#endif
+                  {
+                     png_bytep b = voidcast(png_bytep, malloc((size_t)cb));
+
+                     if (b != NULL)
+                     {
+                        rewind(f);
+
+                        if (fread(b, (size_t)cb, 1, f) == 1)
+                        {
+                           fclose(f);
+                           image->input_memory_size = cb;
+                           image->input_memory = b;
+                        }
+
+                        else
+                        {
+                           free(b);
+                           return logclose(image, f, image->file_name,
+                              ": read failed: ");
+                        }
+                     }
+
+                     else
+                        return logclose(image, f, image->file_name,
+                           ": out of memory: ");
+                  }
+
+                  else
+                     return logclose(image, f, image->file_name,
+                        ": file too big for this architecture: ");
+                     /* cb is the length of the file as a (long) and
+                      * this is greater than the maximum amount of
+                      * memory that can be requested from malloc.
+                      */
+               }
+
+               else if (cb == 0)
+                  return logclose(image, f, image->file_name,
+                     ": zero length: ");
+
+               else
+                  return logclose(image, f, image->file_name,
+                     ": tell failed: ");
+            }
+
+            else
+               return logclose(image, f, image->file_name, ": seek failed: ");
+         }
+      }
+
+      else
+         return logerror(image, image->file_name, ": open failed: ",
+            strerror(errno));
+   }
+
+   return read_file(image, FORMAT_NO_CHANGE, NULL);
+}
+
+#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+static int
+write_one_file(Image *output, Image *image, int convert_to_8bit)
+{
+   if (image->opts & FAST_WRITE)
+      image->image.flags |= PNG_IMAGE_FLAG_FAST;
+
+   if (image->opts & USE_STDIO)
+   {
+#ifndef PNG_USE_MKSTEMP
+      FILE *f = tmpfile();
+#else
+      /* Experimental. Coverity says tmpfile() is insecure because it
+       * generates predictable names.
+       *
+       * It is possible to satisfy Coverity by using mkstemp(); however,
+       * any platform supporting mkstemp() undoubtedly has a secure tmpfile()
+       * implementation as well, and doesn't need the fix.  Note that
+       * the fix won't work on platforms that don't support mkstemp().
+       *
+       * https://www.securecoding.cert.org/confluence/display/c/
+       * FIO21-C.+Do+not+create+temporary+files+in+shared+directories
+       * says that most historic implementations of tmpfile() provide
+       * only a limited number of possible temporary file names
+       * (usually 26) before file names are recycled. That article also
+       * provides a secure solution that unfortunately depends upon mkstemp().
+       */
+      char tmpfile[] = "pngstest-XXXXXX";
+      int filedes;
+      FILE *f;
+      umask(0177);
+      filedes = mkstemp(tmpfile);
+      if (filedes < 0)
+        f = NULL;
+      else
+      {
+        f = fdopen(filedes,"w+");
+        /* Hide the filename immediately and ensure that the file does
+         * not exist after the program ends
+         */
+        (void) unlink(tmpfile);
+      }
+#endif
+
+      if (f != NULL)
+      {
+         if (png_image_write_to_stdio(&image->image, f, convert_to_8bit,
+            image->buffer+16, (png_int_32)image->stride, image->colormap))
+         {
+            if (fflush(f) == 0)
+            {
+               rewind(f);
+               initimage(output, image->opts, "tmpfile", image->stride_extra);
+               output->input_file = f;
+               if (!checkopaque(image))
+                  return 0;
+            }
+
+            else
+               return logclose(image, f, "tmpfile", ": flush: ");
+         }
+
+         else
+         {
+            fclose(f);
+            return logerror(image, "tmpfile", ": write failed", "");
+         }
+      }
+
+      else
+         return logerror(image, "tmpfile", ": open: ", strerror(errno));
+   }
+
+   else
+   {
+      static int counter = 0;
+      char name[32];
+
+      sprintf(name, "%s%d.png", tmpf, ++counter);
+
+      if (png_image_write_to_file(&image->image, name, convert_to_8bit,
+         image->buffer+16, (png_int_32)image->stride, image->colormap))
+      {
+         initimage(output, image->opts, output->tmpfile_name,
+            image->stride_extra);
+         /* Afterwards, or freeimage will delete it! */
+         strcpy(output->tmpfile_name, name);
+
+         if (!checkopaque(image))
+            return 0;
+      }
+
+      else
+         return logerror(image, name, ": write failed", "");
+   }
+
+   /* 'output' has an initialized temporary image, read this back in and compare
+    * this against the original: there should be no change since the original
+    * format was written unmodified unless 'convert_to_8bit' was specified.
+    * However, if the original image was color-mapped, a simple read will zap
+    * the linear, color and maybe alpha flags, this will cause spurious failures
+    * under some circumstances.
+    */
+   if (read_file(output, image->image.format | FORMAT_NO_CHANGE, NULL))
+   {
+      png_uint_32 original_format = image->image.format;
+
+      if (convert_to_8bit)
+         original_format &= ~PNG_FORMAT_FLAG_LINEAR;
+
+      if ((output->image.format & BASE_FORMATS) !=
+         (original_format & BASE_FORMATS))
+         return logerror(image, image->file_name, ": format changed on read: ",
+            output->file_name);
+
+      return compare_two_images(image, output, 0/*via linear*/, NULL);
+   }
+
+   else
+      return logerror(output, output->tmpfile_name,
+         ": read of new file failed", "");
+}
+#endif
+
+static int
+testimage(Image *image, png_uint_32 opts, format_list *pf)
+{
+   int result;
+   Image copy;
+
+   /* Copy the original data, stealing it from 'image' */
+   checkopaque(image);
+   copy = *image;
+
+   copy.opts = opts;
+   copy.buffer = NULL;
+   copy.bufsize = 0;
+   copy.allocsize = 0;
+
+   image->input_file = NULL;
+   image->input_memory = NULL;
+   image->input_memory_size = 0;
+   image->tmpfile_name[0] = 0;
+
+   {
+      png_uint_32 counter;
+      Image output;
+
+      newimage(&output);
+
+      result = 1;
+
+      /* Use the low bit of 'counter' to indicate whether or not to do alpha
+       * removal with a background color or by composting onto the image; this
+       * step gets skipped if it isn't relevant
+       */
+      for (counter=0; counter<2*FORMAT_COUNT; ++counter)
+         if (format_isset(pf, counter >> 1))
+      {
+         png_uint_32 format = counter >> 1;
+
+         png_color background_color;
+         png_colorp background = NULL;
+
+         /* If there is a format change that removes the alpha channel then
+          * the background is relevant.  If the output is 8-bit color-mapped
+          * then a background color *must* be provided, otherwise there are
+          * two tests to do - one with a color, the other with NULL.  The
+          * NULL test happens second.
+          */
+         if ((counter & 1) == 0)
+         {
+            if ((format & PNG_FORMAT_FLAG_ALPHA) == 0 &&
+               (image->image.format & PNG_FORMAT_FLAG_ALPHA) != 0)
+            {
+               /* Alpha/transparency will be removed, the background is
+                * relevant: make it a color the first time
+                */
+               random_color(&background_color);
+               background = &background_color;
+
+               /* BUT if the output is to a color-mapped 8-bit format then
+                * the background must always be a color, so increment 'counter'
+                * to skip the NULL test.
+                */
+               if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+                  (format & PNG_FORMAT_FLAG_LINEAR) == 0)
+                  ++counter;
+            }
+
+            /* Otherwise an alpha channel is not being eliminated, just leave
+             * background NULL and skip the (counter & 1) NULL test.
+             */
+            else
+               ++counter;
+         }
+         /* else just use NULL for background */
+
+         resetimage(&copy);
+         copy.opts = opts; /* in case read_file needs to change it */
+
+         result = read_file(&copy, format, background);
+         if (!result)
+            break;
+
+         /* Make sure the file just read matches the original file. */
+         result = compare_two_images(image, &copy, 0/*via linear*/, background);
+         if (!result)
+            break;
+
+#        ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+            /* Write the *copy* just made to a new file to make sure the write
+             * side works ok.  Check the conversion to sRGB if the copy is
+             * linear.
+             */
+            output.opts = opts;
+            result = write_one_file(&output, &copy, 0/*convert to 8bit*/);
+            if (!result)
+               break;
+
+            /* Validate against the original too; the background is needed here
+             * as well so that compare_two_images knows what color was used.
+             */
+            result = compare_two_images(image, &output, 0, background);
+            if (!result)
+               break;
+
+            if ((format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
+               (format & PNG_FORMAT_FLAG_COLORMAP) == 0)
+            {
+               /* 'output' is linear, convert to the corresponding sRGB format.
+                */
+               output.opts = opts;
+               result = write_one_file(&output, &copy, 1/*convert to 8bit*/);
+               if (!result)
+                  break;
+
+               /* This may involve a conversion via linear; in the ideal world
+                * this would round-trip correctly, but libpng 1.5.7 is not the
+                * ideal world so allow a drift (error_via_linear).
+                *
+                * 'image' has an alpha channel but 'output' does not then there
+                * will a strip-alpha-channel operation (because 'output' is
+                * linear), handle this by composing on black when doing the
+                * comparison.
+                */
+               result = compare_two_images(image, &output, 1/*via_linear*/,
+                  background);
+               if (!result)
+                  break;
+            }
+#        endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */
+      }
+
+      freeimage(&output);
+   }
+
+   freeimage(&copy);
+
+   return result;
+}
+
+static int
+test_one_file(const char *file_name, format_list *formats, png_uint_32 opts,
+   int stride_extra, int log_pass)
+{
+   int result;
+   Image image;
+
+   newimage(&image);
+   initimage(&image, opts, file_name, stride_extra);
+   result = read_one_file(&image);
+   if (result)
+      result = testimage(&image, opts, formats);
+   freeimage(&image);
+
+   /* Ensure that stderr is flushed into any log file */
+   fflush(stderr);
+
+   if (log_pass)
+   {
+      if (result)
+         printf("PASS:");
+
+      else
+         printf("FAIL:");
+
+#     ifndef PNG_SIMPLIFIED_WRITE_SUPPORTED
+         printf(" (no write)");
+#     endif
+
+      print_opts(opts);
+      printf(" %s\n", file_name);
+      /* stdout may not be line-buffered if it is piped to a file, so: */
+      fflush(stdout);
+   }
+
+   else if (!result)
+      exit(1);
+
+   return result;
+}
+
+int
+main(int argc, char **argv)
+{
+   png_uint_32 opts = FAST_WRITE;
+   format_list formats;
+   const char *touch = NULL;
+   int log_pass = 0;
+   int redundant = 0;
+   int stride_extra = 0;
+   int retval = 0;
+   int c;
+
+   init_sRGB_to_d();
+#if 0
+   init_error_via_linear();
+#endif
+   format_init(&formats);
+
+   for (c=1; c<argc; ++c)
+   {
+      const char *arg = argv[c];
+
+      if (strcmp(arg, "--log") == 0)
+         log_pass = 1;
+      else if (strcmp(arg, "--fresh") == 0)
+      {
+         memset(gpc_error, 0, sizeof gpc_error);
+         memset(gpc_error_via_linear, 0, sizeof gpc_error_via_linear);
+      }
+      else if (strcmp(arg, "--file") == 0)
+#        ifdef PNG_STDIO_SUPPORTED
+            opts |= READ_FILE;
+#        else
+            return 77; /* skipped: no support */
+#        endif
+      else if (strcmp(arg, "--memory") == 0)
+         opts &= ~READ_FILE;
+      else if (strcmp(arg, "--stdio") == 0)
+#        ifdef PNG_STDIO_SUPPORTED
+            opts |= USE_STDIO;
+#        else
+            return 77; /* skipped: no support */
+#        endif
+      else if (strcmp(arg, "--name") == 0)
+         opts &= ~USE_STDIO;
+      else if (strcmp(arg, "--verbose") == 0)
+         opts |= VERBOSE;
+      else if (strcmp(arg, "--quiet") == 0)
+         opts &= ~VERBOSE;
+      else if (strcmp(arg, "--preserve") == 0)
+         opts |= KEEP_TMPFILES;
+      else if (strcmp(arg, "--nopreserve") == 0)
+         opts &= ~KEEP_TMPFILES;
+      else if (strcmp(arg, "--keep-going") == 0)
+         opts |= KEEP_GOING;
+      else if (strcmp(arg, "--fast") == 0)
+         opts |= FAST_WRITE;
+      else if (strcmp(arg, "--slow") == 0)
+         opts &= ~FAST_WRITE;
+      else if (strcmp(arg, "--accumulate") == 0)
+         opts |= ACCUMULATE;
+      else if (strcmp(arg, "--redundant") == 0)
+         redundant = 1;
+      else if (strcmp(arg, "--stop") == 0)
+         opts &= ~KEEP_GOING;
+      else if (strcmp(arg, "--strict") == 0)
+         opts |= STRICT;
+      else if (strcmp(arg, "--sRGB-16bit") == 0)
+         opts |= sRGB_16BIT;
+      else if (strcmp(arg, "--linear-16bit") == 0)
+         opts &= ~sRGB_16BIT;
+      else if (strcmp(arg, "--tmpfile") == 0)
+      {
+         if (c+1 < argc)
+         {
+            if (strlen(argv[++c]) >= sizeof tmpf)
+            {
+               fflush(stdout);
+               fprintf(stderr, "%s: %s is too long for a temp file prefix\n",
+                  argv[0], argv[c]);
+               exit(99);
+            }
+
+            /* Safe: checked above */
+            strncpy(tmpf, argv[c], sizeof (tmpf)-1);
+         }
+
+         else
+         {
+            fflush(stdout);
+            fprintf(stderr, "%s: %s requires a temporary file prefix\n",
+               argv[0], arg);
+            exit(99);
+         }
+      }
+      else if (strcmp(arg, "--touch") == 0)
+      {
+         if (c+1 < argc)
+            touch = argv[++c];
+
+         else
+         {
+            fflush(stdout);
+            fprintf(stderr, "%s: %s requires a file name argument\n",
+               argv[0], arg);
+            exit(99);
+         }
+      }
+      else if (arg[0] == '+')
+      {
+         png_uint_32 format = formatof(arg+1);
+
+         if (format > FORMAT_COUNT)
+            exit(99);
+
+         format_set(&formats, format);
+      }
+      else if (arg[0] == '-' && arg[1] != 0 && (arg[1] != '0' || arg[2] != 0))
+      {
+         fflush(stdout);
+         fprintf(stderr, "%s: unknown option: %s\n", argv[0], arg);
+         exit(99);
+      }
+      else
+      {
+         if (format_is_initial(&formats))
+            format_default(&formats, redundant);
+
+         if (arg[0] == '-')
+         {
+            const int term = (arg[1] == '0' ? 0 : '\n');
+            unsigned int ich = 0;
+
+            /* Loop reading files, use a static buffer to simplify this and just
+             * stop if the name gets to long.
+             */
+            static char buffer[4096];
+
+            do
+            {
+               int ch = getchar();
+
+               /* Don't allow '\0' in file names, and terminate with '\n' or,
+                * for -0, just '\0' (use -print0 to find to make this work!)
+                */
+               if (ch == EOF || ch == term || ch == 0)
+               {
+                  buffer[ich] = 0;
+
+                  if (ich > 0 && !test_one_file(buffer, &formats, opts,
+                     stride_extra, log_pass))
+                     retval = 1;
+
+                  if (ch == EOF)
+                     break;
+
+                  ich = 0;
+                  --ich; /* so that the increment below sets it to 0 again */
+               }
+
+               else
+                  buffer[ich] = (char)ch;
+            } while (++ich < sizeof buffer);
+
+            if (ich)
+            {
+               buffer[32] = 0;
+               buffer[4095] = 0;
+               fprintf(stderr, "%s...%s: file name too long\n", buffer,
+                  buffer+(4096-32));
+               exit(99);
+            }
+         }
+
+         else if (!test_one_file(arg, &formats, opts, stride_extra, log_pass))
+            retval = 1;
+      }
+   }
+
+   if (opts & ACCUMULATE)
+   {
+      unsigned int in;
+
+      printf("/* contrib/libtests/pngstest-errors.h\n");
+      printf(" *\n");
+      printf(" * BUILT USING:" PNG_HEADER_VERSION_STRING);
+      printf(" *\n");
+      printf(" * This code is released under the libpng license.\n");
+      printf(" * For conditions of distribution and use, see the disclaimer\n");
+      printf(" * and license in png.h\n");
+      printf(" *\n");
+      printf(" * THIS IS A MACHINE GENERATED FILE: do not edit it directly!\n");
+      printf(" * Instead run:\n");
+      printf(" *\n");
+      printf(" *    pngstest --accumulate\n");
+      printf(" *\n");
+      printf(" * on as many PNG files as possible; at least PNGSuite and\n");
+      printf(" * contrib/libtests/testpngs.\n");
+      printf(" */\n");
+
+      printf("static png_uint_16 gpc_error[16/*in*/][16/*out*/][4/*a*/] =\n");
+      printf("{\n");
+      for (in=0; in<16; ++in)
+      {
+         unsigned int out;
+         printf(" { /* input: %s */\n ", format_names[in]);
+         for (out=0; out<16; ++out)
+         {
+            unsigned int alpha;
+            printf(" {");
+            for (alpha=0; alpha<4; ++alpha)
+            {
+               printf(" %d", gpc_error[in][out][alpha]);
+               if (alpha < 3) putchar(',');
+            }
+            printf(" }");
+            if (out < 15)
+            {
+               putchar(',');
+               if (out % 4 == 3) printf("\n ");
+            }
+         }
+         printf("\n }");
+
+         if (in < 15)
+            putchar(',');
+         else
+            putchar('\n');
+      }
+      printf("};\n");
+
+      printf("static png_uint_16 gpc_error_via_linear[16][4/*out*/][4] =\n");
+      printf("{\n");
+      for (in=0; in<16; ++in)
+      {
+         unsigned int out;
+         printf(" { /* input: %s */\n ", format_names[in]);
+         for (out=0; out<4; ++out)
+         {
+            unsigned int alpha;
+            printf(" {");
+            for (alpha=0; alpha<4; ++alpha)
+            {
+               printf(" %d", gpc_error_via_linear[in][out][alpha]);
+               if (alpha < 3) putchar(',');
+            }
+            printf(" }");
+            if (out < 3)
+               putchar(',');
+         }
+         printf("\n }");
+
+         if (in < 15)
+            putchar(',');
+         else
+            putchar('\n');
+      }
+      printf("};\n");
+
+      printf("static png_uint_16 gpc_error_to_colormap[8/*i*/][8/*o*/][4] =\n");
+      printf("{\n");
+      for (in=0; in<8; ++in)
+      {
+         unsigned int out;
+         printf(" { /* input: %s */\n ", format_names[in]);
+         for (out=0; out<8; ++out)
+         {
+            unsigned int alpha;
+            printf(" {");
+            for (alpha=0; alpha<4; ++alpha)
+            {
+               printf(" %d", gpc_error_to_colormap[in][out][alpha]);
+               if (alpha < 3) putchar(',');
+            }
+            printf(" }");
+            if (out < 7)
+            {
+               putchar(',');
+               if (out % 4 == 3) printf("\n ");
+            }
+         }
+         printf("\n }");
+
+         if (in < 7)
+            putchar(',');
+         else
+            putchar('\n');
+      }
+      printf("};\n");
+      printf("/* END MACHINE GENERATED */\n");
+   }
+
+   if (retval == 0 && touch != NULL)
+   {
+      FILE *fsuccess = fopen(touch, "wt");
+
+      if (fsuccess != NULL)
+      {
+         int error = 0;
+         fprintf(fsuccess, "PNG simple API tests succeeded\n");
+         fflush(fsuccess);
+         error = ferror(fsuccess);
+
+         if (fclose(fsuccess) || error)
+         {
+            fflush(stdout);
+            fprintf(stderr, "%s: write failed\n", touch);
+            exit(99);
+         }
+      }
+
+      else
+      {
+         fflush(stdout);
+         fprintf(stderr, "%s: open failed\n", touch);
+         exit(99);
+      }
+   }
+
+   return retval;
+}
+
+#else /* !PNG_SIMPLIFIED_READ_SUPPORTED */
+int main(void)
+{
+   fprintf(stderr, "pngstest: no read support in libpng, test skipped\n");
+   /* So the test is skipped: */
+   return 77;
+}
+#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
diff --git a/contrib/libtests/pngunknown.c b/contrib/libtests/pngunknown.c
new file mode 100644
index 0000000..0d52ff8
--- /dev/null
+++ b/contrib/libtests/pngunknown.c
@@ -0,0 +1,1261 @@
+
+/* pngunknown.c - test the read side unknown chunk handling
+ *
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by John Cunningham Bowler
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * NOTES:
+ *   This is a C program that is intended to be linked against libpng.  It
+ *   allows the libpng unknown handling code to be tested by interpreting
+ *   arguments to save or discard combinations of chunks.  The program is
+ *   currently just a minimal validation for the built-in libpng facilities.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <setjmp.h>
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+/* Since this program tests the ability to change the unknown chunk handling
+ * these must be defined:
+ */
+#if defined(PNG_SET_UNKNOWN_CHUNKS_SUPPORTED) &&\
+   defined(PNG_READ_SUPPORTED)
+
+/* One of these must be defined to allow us to find out what happened.  It is
+ * still useful to set unknown chunk handling without either of these in order
+ * to cause *known* chunks to be discarded.  This can be a significant
+ * efficiency gain, but it can't really be tested here.
+ */
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) ||\
+   defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+
+#if PNG_LIBPNG_VER < 10500
+/* This deliberately lacks the PNG_CONST. */
+typedef png_byte *png_const_bytep;
+
+/* This is copied from 1.5.1 png.h: */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+   -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+   -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
+   (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(xIn, pass) \
+   (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+#define PNG_PASS_MASK(pass,off) ( \
+   ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
+   ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+   ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+   ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
+/* These are needed too for the default build: */
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+
+/* This comes from pnglibconf.h afer 1.5: */
+#define PNG_FP_1 100000
+#define PNG_GAMMA_THRESHOLD_FIXED\
+   ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1))
+#endif
+
+#if PNG_LIBPNG_VER < 10600
+   /* 1.6.0 constifies many APIs. The following exists to allow pngvalid to be
+    * compiled against earlier versions.
+    */
+#  define png_const_structp png_structp
+#endif
+
+#if PNG_LIBPNG_VER < 10700
+   /* Copied from libpng 1.7.0 png.h */
+#define PNG_u2(b1, b2) (((unsigned int)(b1) << 8) + (b2))
+
+#define PNG_U16(b1, b2) ((png_uint_16)PNG_u2(b1, b2))
+#define PNG_U32(b1, b2, b3, b4)\
+   (((png_uint_32)PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4))
+
+/* Constants for known chunk types.
+ */
+#define png_IDAT PNG_U32( 73,  68,  65,  84)
+#define png_IEND PNG_U32( 73,  69,  78,  68)
+#define png_IHDR PNG_U32( 73,  72,  68,  82)
+#define png_PLTE PNG_U32( 80,  76,  84,  69)
+#define png_bKGD PNG_U32( 98,  75,  71,  68)
+#define png_cHRM PNG_U32( 99,  72,  82,  77)
+#define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
+#define png_gAMA PNG_U32(103,  65,  77,  65)
+#define png_gIFg PNG_U32(103,  73,  70, 103)
+#define png_gIFt PNG_U32(103,  73,  70, 116) /* deprecated */
+#define png_gIFx PNG_U32(103,  73,  70, 120)
+#define png_hIST PNG_U32(104,  73,  83,  84)
+#define png_iCCP PNG_U32(105,  67,  67,  80)
+#define png_iTXt PNG_U32(105,  84,  88, 116)
+#define png_oFFs PNG_U32(111,  70,  70, 115)
+#define png_pCAL PNG_U32(112,  67,  65,  76)
+#define png_pHYs PNG_U32(112,  72,  89, 115)
+#define png_sBIT PNG_U32(115,  66,  73,  84)
+#define png_sCAL PNG_U32(115,  67,  65,  76)
+#define png_sPLT PNG_U32(115,  80,  76,  84)
+#define png_sRGB PNG_U32(115,  82,  71,  66)
+#define png_sTER PNG_U32(115,  84,  69,  82)
+#define png_tEXt PNG_U32(116,  69,  88, 116)
+#define png_tIME PNG_U32(116,  73,  77,  69)
+#define png_tRNS PNG_U32(116,  82,  78,  83)
+#define png_zTXt PNG_U32(122,  84,  88, 116)
+
+/* Test on flag values as defined in the spec (section 5.4): */
+#define PNG_CHUNK_ANCILLARY(c)    (1 & ((c) >> 29))
+#define PNG_CHUNK_CRITICAL(c)     (!PNG_CHUNK_ANCILLARY(c))
+#define PNG_CHUNK_PRIVATE(c)      (1 & ((c) >> 21))
+#define PNG_CHUNK_RESERVED(c)     (1 & ((c) >> 13))
+#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >>  5))
+
+#endif /* PNG_LIBPNG_VER < 10700 */
+
+#ifdef __cplusplus
+#  define this not_the_cpp_this
+#  define new not_the_cpp_new
+#  define voidcast(type, value) static_cast<type>(value)
+#else
+#  define voidcast(type, value) (value)
+#endif /* __cplusplus */
+
+/* Unused formal parameter errors are removed using the following macro which is
+ * expected to have no bad effects on performance.
+ */
+#ifndef UNUSED
+#  if defined(__GNUC__) || defined(_MSC_VER)
+#     define UNUSED(param) (void)param;
+#  else
+#     define UNUSED(param)
+#  endif
+#endif
+
+/* Types of chunks not known to libpng */
+#define png_vpAg PNG_U32(118, 112, 65, 103)
+
+/* Chunk information */
+#define PNG_INFO_tEXt 0x10000000U
+#define PNG_INFO_iTXt 0x20000000U
+#define PNG_INFO_zTXt 0x40000000U
+
+#define PNG_INFO_sTER 0x01000000U
+#define PNG_INFO_vpAg 0x02000000U
+
+#define ABSENT  0
+#define START   1
+#define END     2
+
+static struct
+{
+   char        name[5];
+   png_uint_32 flag;
+   png_uint_32 tag;
+   int         unknown;    /* Chunk not known to libpng */
+   int         all;        /* Chunk set by the '-1' option */
+   int         position;   /* position in pngtest.png */
+   int         keep;       /* unknown handling setting */
+} chunk_info[] = {
+   /* Critical chunks */
+   { "IDAT", PNG_INFO_IDAT, png_IDAT, 0, 0,  START, 0 }, /* must be [0] */
+   { "PLTE", PNG_INFO_PLTE, png_PLTE, 0, 0, ABSENT, 0 },
+
+   /* Non-critical chunks that libpng handles */
+   /* This is a mess but it seems to be the only way to do it - there is no way
+    * to check for a definition outside a #if.
+    */
+   { "bKGD", PNG_INFO_bKGD, png_bKGD,
+#     ifdef PNG_READ_bKGD_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "cHRM", PNG_INFO_cHRM, png_cHRM,
+#     ifdef PNG_READ_cHRM_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "gAMA", PNG_INFO_gAMA, png_gAMA,
+#     ifdef PNG_READ_gAMA_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "hIST", PNG_INFO_hIST, png_hIST,
+#     ifdef PNG_READ_hIST_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1, ABSENT, 0 },
+   { "iCCP", PNG_INFO_iCCP, png_iCCP,
+#     ifdef PNG_READ_iCCP_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1, ABSENT, 0 },
+   { "iTXt", PNG_INFO_iTXt, png_iTXt,
+#     ifdef PNG_READ_iTXt_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1, ABSENT, 0 },
+   { "oFFs", PNG_INFO_oFFs, png_oFFs,
+#     ifdef PNG_READ_oFFs_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "pCAL", PNG_INFO_pCAL, png_pCAL,
+#     ifdef PNG_READ_pCAL_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "pHYs", PNG_INFO_pHYs, png_pHYs,
+#     ifdef PNG_READ_pHYs_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "sBIT", PNG_INFO_sBIT, png_sBIT,
+#     ifdef PNG_READ_sBIT_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "sCAL", PNG_INFO_sCAL, png_sCAL,
+#     ifdef PNG_READ_sCAL_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "sPLT", PNG_INFO_sPLT, png_sPLT,
+#     ifdef PNG_READ_sPLT_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1, ABSENT, 0 },
+   { "sRGB", PNG_INFO_sRGB, png_sRGB,
+#     ifdef PNG_READ_sRGB_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "tEXt", PNG_INFO_tEXt, png_tEXt,
+#     ifdef PNG_READ_tEXt_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "tIME", PNG_INFO_tIME, png_tIME,
+#     ifdef PNG_READ_tIME_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  START, 0 },
+   { "tRNS", PNG_INFO_tRNS, png_tRNS,
+#     ifdef PNG_READ_tRNS_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      0, ABSENT, 0 },
+   { "zTXt", PNG_INFO_zTXt, png_zTXt,
+#     ifdef PNG_READ_zTXt_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,    END, 0 },
+
+   /* No libpng handling */
+   { "sTER", PNG_INFO_sTER, png_sTER, 1, 1,  START, 0 },
+   { "vpAg", PNG_INFO_vpAg, png_vpAg, 1, 0,  START, 0 },
+};
+
+#define NINFO ((int)((sizeof chunk_info)/(sizeof chunk_info[0])))
+
+static void
+clear_keep(void)
+{
+   int i = NINFO;
+   while (--i >= 0)
+      chunk_info[i].keep = 0;
+}
+
+static int
+find(const char *name)
+{
+   int i = NINFO;
+   while (--i >= 0)
+   {
+      if (memcmp(chunk_info[i].name, name, 4) == 0)
+         break;
+   }
+
+   return i;
+}
+
+static int
+findb(const png_byte *name)
+{
+   int i = NINFO;
+   while (--i >= 0)
+   {
+      if (memcmp(chunk_info[i].name, name, 4) == 0)
+         break;
+   }
+
+   return i;
+}
+
+static int
+find_by_flag(png_uint_32 flag)
+{
+   int i = NINFO;
+
+   while (--i >= 0) if (chunk_info[i].flag == flag) return i;
+
+   fprintf(stderr, "pngunknown: internal error\n");
+   exit(4);
+}
+
+static int
+ancillary(const char *name)
+{
+   return PNG_CHUNK_ANCILLARY(PNG_U32(name[0], name[1], name[2], name[3]));
+}
+
+#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+static int
+ancillaryb(const png_byte *name)
+{
+   return PNG_CHUNK_ANCILLARY(PNG_U32(name[0], name[1], name[2], name[3]));
+}
+#endif
+
+/* Type of an error_ptr */
+typedef struct
+{
+   jmp_buf     error_return;
+   png_structp png_ptr;
+   png_infop   info_ptr, end_ptr;
+   png_uint_32 before_IDAT;
+   png_uint_32 after_IDAT;
+   int         error_count;
+   int         warning_count;
+   int         keep; /* the default value */
+   const char *program;
+   const char *file;
+   const char *test;
+} display;
+
+static const char init[] = "initialization";
+static const char cmd[] = "command line";
+
+static void
+init_display(display *d, const char *program)
+{
+   memset(d, 0, sizeof *d);
+   d->png_ptr = NULL;
+   d->info_ptr = d->end_ptr = NULL;
+   d->error_count = d->warning_count = 0;
+   d->program = program;
+   d->file = program;
+   d->test = init;
+}
+
+static void
+clean_display(display *d)
+{
+   png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_ptr);
+
+   /* This must not happen - it might cause an app crash */
+   if (d->png_ptr != NULL || d->info_ptr != NULL || d->end_ptr != NULL)
+   {
+      fprintf(stderr, "%s(%s): png_destroy_read_struct error\n", d->file,
+         d->test);
+      exit(1);
+   }
+}
+
+PNG_FUNCTION(void, display_exit, (display *d), static PNG_NORETURN)
+{
+   ++(d->error_count);
+
+   if (d->png_ptr != NULL)
+      clean_display(d);
+
+   /* During initialization and if this is a single command line argument set
+    * exit now - there is only one test, otherwise longjmp to do the next test.
+    */
+   if (d->test == init || d->test == cmd)
+      exit(1);
+
+   longjmp(d->error_return, 1);
+}
+
+static int
+display_rc(const display *d, int strict)
+{
+   return d->error_count + (strict ? d->warning_count : 0);
+}
+
+/* libpng error and warning callbacks */
+PNG_FUNCTION(void, (PNGCBAPI error), (png_structp png_ptr, const char *message),
+   static PNG_NORETURN)
+{
+   display *d = (display*)png_get_error_ptr(png_ptr);
+
+   fprintf(stderr, "%s(%s): libpng error: %s\n", d->file, d->test, message);
+   display_exit(d);
+}
+
+static void PNGCBAPI
+warning(png_structp png_ptr, const char *message)
+{
+   display *d = (display*)png_get_error_ptr(png_ptr);
+
+   fprintf(stderr, "%s(%s): libpng warning: %s\n", d->file, d->test, message);
+   ++(d->warning_count);
+}
+
+static png_uint_32
+get_valid(display *d, png_infop info_ptr)
+{
+   png_uint_32 flags = png_get_valid(d->png_ptr, info_ptr, (png_uint_32)~0);
+
+   /* Map the text chunks back into the flags */
+   {
+      png_textp text;
+      png_uint_32 ntext = png_get_text(d->png_ptr, info_ptr, &text, NULL);
+
+      while (ntext-- > 0) switch (text[ntext].compression)
+      {
+         case -1:
+            flags |= PNG_INFO_tEXt;
+            break;
+         case 0:
+            flags |= PNG_INFO_zTXt;
+            break;
+         case 1:
+         case 2:
+            flags |= PNG_INFO_iTXt;
+            break;
+         default:
+            fprintf(stderr, "%s(%s): unknown text compression %d\n", d->file,
+               d->test, text[ntext].compression);
+            display_exit(d);
+      }
+   }
+
+   return flags;
+}
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+static int PNGCBAPI
+read_callback(png_structp pp, png_unknown_chunkp pc)
+{
+   /* This function mimics the behavior of png_set_keep_unknown_chunks by
+    * returning '0' to keep the chunk and '1' to discard it.
+    */
+   display *d = voidcast(display*, png_get_user_chunk_ptr(pp));
+   int chunk = findb(pc->name);
+   int keep, discard;
+
+   if (chunk < 0) /* not one in our list, so not a known chunk */
+      keep = d->keep;
+
+   else
+   {
+      keep = chunk_info[chunk].keep;
+      if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+      {
+         /* See the comments in png.h - use the default for unknown chunks,
+          * do not keep known chunks.
+          */
+         if (chunk_info[chunk].unknown)
+            keep = d->keep;
+
+         else
+            keep = PNG_HANDLE_CHUNK_NEVER;
+      }
+   }
+
+   switch (keep)
+   {
+      default:
+         fprintf(stderr, "%s(%s): %d: unrecognized chunk option\n", d->file,
+            d->test, chunk_info[chunk].keep);
+         display_exit(d);
+
+      case PNG_HANDLE_CHUNK_AS_DEFAULT:
+      case PNG_HANDLE_CHUNK_NEVER:
+         discard = 1/*handled; discard*/;
+         break;
+
+      case PNG_HANDLE_CHUNK_IF_SAFE:
+      case PNG_HANDLE_CHUNK_ALWAYS:
+         discard = 0/*not handled; keep*/;
+         break;
+   }
+
+   /* Also store information about this chunk in the display, the relevant flag
+    * is set if the chunk is to be kept ('not handled'.)
+    */
+   if (chunk >= 0) if (!discard) /* stupidity to stop a GCC warning */
+   {
+      png_uint_32 flag = chunk_info[chunk].flag;
+
+      if (pc->location & PNG_AFTER_IDAT)
+         d->after_IDAT |= flag;
+
+      else
+         d->before_IDAT |= flag;
+   }
+
+   /* However if there is no support to store unknown chunks don't ask libpng to
+    * do it; there will be an png_error.
+    */
+#  ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+      return discard;
+#  else
+      return 1; /*handled; discard*/
+#  endif
+}
+#endif /* READ_USER_CHUNKS_SUPPORTED */
+
+#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+static png_uint_32
+get_unknown(display *d, png_infop info_ptr, int after_IDAT)
+{
+   /* Create corresponding 'unknown' flags */
+   png_uint_32 flags = 0;
+
+   UNUSED(after_IDAT)
+
+   {
+      png_unknown_chunkp unknown;
+      int num_unknown = png_get_unknown_chunks(d->png_ptr, info_ptr, &unknown);
+
+      while (--num_unknown >= 0)
+      {
+         int chunk = findb(unknown[num_unknown].name);
+
+         /* Chunks not known to pngunknown must be validated here; since they
+          * must also be unknown to libpng the 'display->keep' behavior should
+          * have been used.
+          */
+         if (chunk < 0) switch (d->keep)
+         {
+            default: /* impossible */
+            case PNG_HANDLE_CHUNK_AS_DEFAULT:
+            case PNG_HANDLE_CHUNK_NEVER:
+               fprintf(stderr, "%s(%s): %s: %s: unknown chunk saved\n",
+                  d->file, d->test, d->keep ? "discard" : "default",
+                  unknown[num_unknown].name);
+               ++(d->error_count);
+               break;
+
+            case PNG_HANDLE_CHUNK_IF_SAFE:
+               if (!ancillaryb(unknown[num_unknown].name))
+               {
+                  fprintf(stderr,
+                     "%s(%s): if-safe: %s: unknown critical chunk saved\n",
+                     d->file, d->test, unknown[num_unknown].name);
+                  ++(d->error_count);
+                  break;
+               }
+               /* FALL THROUGH (safe) */
+            case PNG_HANDLE_CHUNK_ALWAYS:
+               break;
+         }
+
+         else
+            flags |= chunk_info[chunk].flag;
+      }
+   }
+
+   return flags;
+}
+#else
+static png_uint_32
+get_unknown(display *d, png_infop info_ptr, int after_IDAT)
+   /* Otherwise this will return the cached values set by any user callback */
+{
+   UNUSED(info_ptr);
+
+   if (after_IDAT)
+      return d->after_IDAT;
+
+   else
+      return d->before_IDAT;
+}
+
+#  ifndef PNG_READ_USER_CHUNKS_SUPPORTED
+      /* The #defines above should mean this is never reached, it's just here as
+       * a check to ensure the logic is correct.
+       */
+#     error No store support and no user chunk support, this will not work
+#  endif
+#endif
+
+static int
+check(FILE *fp, int argc, const char **argv, png_uint_32p flags/*out*/,
+   display *d, int set_callback)
+{
+   int i, npasses, ipass;
+   png_uint_32 height;
+
+   d->keep = PNG_HANDLE_CHUNK_AS_DEFAULT;
+   d->before_IDAT = 0;
+   d->after_IDAT = 0;
+
+   /* Some of these errors are permanently fatal and cause an exit here, others
+    * are per-test and cause an error return.
+    */
+   d->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, d, error,
+      warning);
+   if (d->png_ptr == NULL)
+   {
+      fprintf(stderr, "%s(%s): could not allocate png struct\n", d->file,
+         d->test);
+      /* Terminate here, this error is not test specific. */
+      exit(1);
+   }
+
+   d->info_ptr = png_create_info_struct(d->png_ptr);
+   d->end_ptr = png_create_info_struct(d->png_ptr);
+   if (d->info_ptr == NULL || d->end_ptr == NULL)
+   {
+      fprintf(stderr, "%s(%s): could not allocate png info\n", d->file,
+         d->test);
+      clean_display(d);
+      exit(1);
+   }
+
+   png_init_io(d->png_ptr, fp);
+
+#  ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+      /* This is only done if requested by the caller; it interferes with the
+       * standard store/save mechanism.
+       */
+      if (set_callback)
+         png_set_read_user_chunk_fn(d->png_ptr, d, read_callback);
+#  else
+      UNUSED(set_callback)
+#  endif
+
+   /* Handle each argument in turn; multiple settings are possible for the same
+    * chunk and multiple calls will occur (the last one should override all
+    * preceding ones).
+    */
+   for (i=0; i<argc; ++i)
+   {
+      const char *equals = strchr(argv[i], '=');
+
+      if (equals != NULL)
+      {
+         int chunk, option;
+
+         if (strcmp(equals+1, "default") == 0)
+            option = PNG_HANDLE_CHUNK_AS_DEFAULT;
+         else if (strcmp(equals+1, "discard") == 0)
+            option = PNG_HANDLE_CHUNK_NEVER;
+         else if (strcmp(equals+1, "if-safe") == 0)
+            option = PNG_HANDLE_CHUNK_IF_SAFE;
+         else if (strcmp(equals+1, "save") == 0)
+            option = PNG_HANDLE_CHUNK_ALWAYS;
+         else
+         {
+            fprintf(stderr, "%s(%s): %s: unrecognized chunk option\n", d->file,
+               d->test, argv[i]);
+            display_exit(d);
+         }
+
+         switch (equals - argv[i])
+         {
+            case 4: /* chunk name */
+               chunk = find(argv[i]);
+
+               if (chunk >= 0)
+               {
+                  /* These #if tests have the effect of skipping the arguments
+                   * if SAVE support is unavailable - we can't do a useful test
+                   * in this case, so we just check the arguments!  This could
+                   * be improved in the future by using the read callback.
+                   */
+#                 if PNG_LIBPNG_VER >= 10700 &&\
+                     !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+                     if (option < PNG_HANDLE_CHUNK_IF_SAFE)
+#                 endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
+                  {
+                     png_byte name[5];
+
+                     memcpy(name, chunk_info[chunk].name, 5);
+                     png_set_keep_unknown_chunks(d->png_ptr, option, name, 1);
+                     chunk_info[chunk].keep = option;
+                  }
+                  continue;
+               }
+
+               break;
+
+            case 7: /* default */
+               if (memcmp(argv[i], "default", 7) == 0)
+               {
+#                 if PNG_LIBPNG_VER >= 10700 &&\
+                     !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+                     if (option < PNG_HANDLE_CHUNK_IF_SAFE)
+#                 endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
+                     png_set_keep_unknown_chunks(d->png_ptr, option, NULL, 0);
+
+                  d->keep = option;
+                  continue;
+               }
+
+               break;
+
+            case 3: /* all */
+               if (memcmp(argv[i], "all", 3) == 0)
+               {
+#                 if PNG_LIBPNG_VER >= 10700 &&\
+                     !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+                     if (option < PNG_HANDLE_CHUNK_IF_SAFE)
+#                 endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
+                     png_set_keep_unknown_chunks(d->png_ptr, option, NULL, -1);
+
+                  d->keep = option;
+
+                  for (chunk = 0; chunk < NINFO; ++chunk)
+                     if (chunk_info[chunk].all)
+                        chunk_info[chunk].keep = option;
+                  continue;
+               }
+
+               break;
+
+            default: /* some misplaced = */
+
+               break;
+         }
+      }
+
+      fprintf(stderr, "%s(%s): %s: unrecognized chunk argument\n", d->file,
+         d->test, argv[i]);
+      display_exit(d);
+   }
+
+   png_read_info(d->png_ptr, d->info_ptr);
+
+   switch (png_get_interlace_type(d->png_ptr, d->info_ptr))
+   {
+      case PNG_INTERLACE_NONE:
+         npasses = 1;
+         break;
+
+      case PNG_INTERLACE_ADAM7:
+         npasses = PNG_INTERLACE_ADAM7_PASSES;
+         break;
+
+      default:
+         /* Hard error because it is not test specific */
+         fprintf(stderr, "%s(%s): invalid interlace type\n", d->file, d->test);
+         clean_display(d);
+         exit(1);
+   }
+
+   /* Skip the image data, if IDAT is not being handled then don't do this
+    * because it will cause a CRC error.
+    */
+   if (chunk_info[0/*IDAT*/].keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+   {
+      png_start_read_image(d->png_ptr);
+      height = png_get_image_height(d->png_ptr, d->info_ptr);
+
+      if (npasses > 1)
+      {
+         png_uint_32 width = png_get_image_width(d->png_ptr, d->info_ptr);
+
+         for (ipass=0; ipass<npasses; ++ipass)
+         {
+            png_uint_32 wPass = PNG_PASS_COLS(width, ipass);
+
+            if (wPass > 0)
+            {
+               png_uint_32 y;
+
+               for (y=0; y<height; ++y) if (PNG_ROW_IN_INTERLACE_PASS(y, ipass))
+                  png_read_row(d->png_ptr, NULL, NULL);
+            }
+         }
+      } /* interlaced */
+
+      else /* not interlaced */
+      {
+         png_uint_32 y;
+
+         for (y=0; y<height; ++y)
+            png_read_row(d->png_ptr, NULL, NULL);
+      }
+   }
+
+   png_read_end(d->png_ptr, d->end_ptr);
+
+   flags[0] = get_valid(d, d->info_ptr);
+   flags[1] = get_unknown(d, d->info_ptr, 0/*before IDAT*/);
+
+   /* Only png_read_png sets PNG_INFO_IDAT! */
+   flags[chunk_info[0/*IDAT*/].keep != PNG_HANDLE_CHUNK_AS_DEFAULT] |=
+      PNG_INFO_IDAT;
+
+   flags[2] = get_valid(d, d->end_ptr);
+   flags[3] = get_unknown(d, d->end_ptr, 1/*after IDAT*/);
+
+   clean_display(d);
+
+   return d->keep;
+}
+
+static void
+check_error(display *d, png_uint_32 flags, const char *message)
+{
+   while (flags)
+   {
+      png_uint_32 flag = flags & -(png_int_32)flags;
+      int i = find_by_flag(flag);
+
+      fprintf(stderr, "%s(%s): chunk %s: %s\n", d->file, d->test,
+         chunk_info[i].name, message);
+      ++(d->error_count);
+
+      flags &= ~flag;
+   }
+}
+
+static void
+check_handling(display *d, int def, png_uint_32 chunks, png_uint_32 known,
+   png_uint_32 unknown, const char *position, int set_callback)
+{
+   while (chunks)
+   {
+      png_uint_32 flag = chunks & -(png_int_32)chunks;
+      int i = find_by_flag(flag);
+      int keep = chunk_info[i].keep;
+      const char *type;
+      const char *errorx = NULL;
+
+      if (chunk_info[i].unknown)
+      {
+         if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+         {
+            type = "UNKNOWN (default)";
+            keep = def;
+         }
+
+         else
+            type = "UNKNOWN (specified)";
+
+         if (flag & known)
+            errorx = "chunk processed";
+
+         else switch (keep)
+         {
+            case PNG_HANDLE_CHUNK_AS_DEFAULT:
+               if (flag & unknown)
+                  errorx = "DEFAULT: unknown chunk saved";
+               break;
+
+            case PNG_HANDLE_CHUNK_NEVER:
+               if (flag & unknown)
+                  errorx = "DISCARD: unknown chunk saved";
+               break;
+
+            case PNG_HANDLE_CHUNK_IF_SAFE:
+               if (ancillary(chunk_info[i].name))
+               {
+                  if (!(flag & unknown))
+                     errorx = "IF-SAFE: unknown ancillary chunk lost";
+               }
+
+               else if (flag & unknown)
+                  errorx = "IF-SAFE: unknown critical chunk saved";
+               break;
+
+            case PNG_HANDLE_CHUNK_ALWAYS:
+               if (!(flag & unknown))
+                  errorx = "SAVE: unknown chunk lost";
+               break;
+
+            default:
+               errorx = "internal error: bad keep";
+               break;
+         }
+      } /* unknown chunk */
+
+      else /* known chunk */
+      {
+         type = "KNOWN";
+
+         if (flag & known)
+         {
+            /* chunk was processed, it won't have been saved because that is
+             * caught below when checking for inconsistent processing.
+             */
+            if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)
+               errorx = "!DEFAULT: known chunk processed";
+         }
+
+         else /* not processed */ switch (keep)
+         {
+            case PNG_HANDLE_CHUNK_AS_DEFAULT:
+               errorx = "DEFAULT: known chunk not processed";
+               break;
+
+            case PNG_HANDLE_CHUNK_NEVER:
+               if (flag & unknown)
+                  errorx = "DISCARD: known chunk saved";
+               break;
+
+            case PNG_HANDLE_CHUNK_IF_SAFE:
+               if (ancillary(chunk_info[i].name))
+               {
+                  if (!(flag & unknown))
+                     errorx = "IF-SAFE: known ancillary chunk lost";
+               }
+
+               else if (flag & unknown)
+                  errorx = "IF-SAFE: known critical chunk saved";
+               break;
+
+            case PNG_HANDLE_CHUNK_ALWAYS:
+               if (!(flag & unknown))
+                  errorx = "SAVE: known chunk lost";
+               break;
+
+            default:
+               errorx = "internal error: bad keep (2)";
+               break;
+         }
+      }
+
+      if (errorx != NULL)
+      {
+         ++(d->error_count);
+         fprintf(stderr, "%s(%s%s): %s %s %s: %s\n", d->file, d->test,
+            set_callback ? ",callback" : "",
+            type, chunk_info[i].name, position, errorx);
+      }
+
+      chunks &= ~flag;
+   }
+}
+
+static void
+perform_one_test(FILE *fp, int argc, const char **argv,
+   png_uint_32 *default_flags, display *d, int set_callback)
+{
+   int def;
+   png_uint_32 flags[2][4];
+
+   rewind(fp);
+   clear_keep();
+   memcpy(flags[0], default_flags, sizeof flags[0]);
+
+   def = check(fp, argc, argv, flags[1], d, set_callback);
+
+   /* Chunks should either be known or unknown, never both and this should apply
+    * whether the chunk is before or after the IDAT (actually, the app can
+    * probably change this by swapping the handling after the image, but this
+    * test does not do that.)
+    */
+   check_error(d, (flags[0][0]|flags[0][2]) & (flags[0][1]|flags[0][3]),
+      "chunk handled inconsistently in count tests");
+   check_error(d, (flags[1][0]|flags[1][2]) & (flags[1][1]|flags[1][3]),
+      "chunk handled inconsistently in option tests");
+
+   /* Now find out what happened to each chunk before and after the IDAT and
+    * determine if the behavior was correct.  First some basic sanity checks,
+    * any known chunk should be known in the original count, any unknown chunk
+    * should be either known or unknown in the original.
+    */
+   {
+      png_uint_32 test;
+
+      test = flags[1][0] & ~flags[0][0];
+      check_error(d, test, "new known chunk before IDAT");
+      test = flags[1][1] & ~(flags[0][0] | flags[0][1]);
+      check_error(d, test, "new unknown chunk before IDAT");
+      test = flags[1][2] & ~flags[0][2];
+      check_error(d, test, "new known chunk after IDAT");
+      test = flags[1][3] & ~(flags[0][2] | flags[0][3]);
+      check_error(d, test, "new unknown chunk after IDAT");
+   }
+
+   /* Now each chunk in the original list should have been handled according to
+    * the options set for that chunk, regardless of whether libpng knows about
+    * it or not.
+    */
+   check_handling(d, def, flags[0][0] | flags[0][1], flags[1][0], flags[1][1],
+      "before IDAT", set_callback);
+   check_handling(d, def, flags[0][2] | flags[0][3], flags[1][2], flags[1][3],
+      "after IDAT", set_callback);
+}
+
+static void
+perform_one_test_safe(FILE *fp, int argc, const char **argv,
+   png_uint_32 *default_flags, display *d, const char *test)
+{
+   if (setjmp(d->error_return) == 0)
+   {
+      d->test = test; /* allow use of d->error_return */
+#     ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+         perform_one_test(fp, argc, argv, default_flags, d, 0);
+#     endif
+#     ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+         perform_one_test(fp, argc, argv, default_flags, d, 1);
+#     endif
+      d->test = init; /* prevent use of d->error_return */
+   }
+}
+
+static const char *standard_tests[] =
+{
+ "discard", "default=discard", 0,
+ "save", "default=save", 0,
+ "if-safe", "default=if-safe", 0,
+ "vpAg", "vpAg=if-safe", 0,
+ "sTER", "sTER=if-safe", 0,
+ "IDAT", "default=discard", "IDAT=save", 0,
+ "sAPI", "bKGD=save", "cHRM=save", "gAMA=save", "all=discard", "iCCP=save",
+   "sBIT=save", "sRGB=save", 0,
+ 0/*end*/
+};
+
+static PNG_NORETURN void
+usage(const char *program, const char *reason)
+{
+   fprintf(stderr, "pngunknown: %s: usage:\n %s [--strict] "
+      "--default|{(CHNK|default|all)=(default|discard|if-safe|save)} "
+      "testfile.png\n", reason, program);
+   exit(99);
+}
+
+int
+main(int argc, const char **argv)
+{
+   FILE *fp;
+   png_uint_32 default_flags[4/*valid,unknown{before,after}*/];
+   int strict = 0, default_tests = 0;
+   const char *count_argv = "default=save";
+   const char *touch_file = NULL;
+   display d;
+
+   init_display(&d, argv[0]);
+
+   while (++argv, --argc > 0)
+   {
+      if (strcmp(*argv, "--strict") == 0)
+         strict = 1;
+
+      else if (strcmp(*argv, "--default") == 0)
+         default_tests = 1;
+
+      else if (strcmp(*argv, "--touch") == 0)
+      {
+         if (argc > 1)
+            touch_file = *++argv, --argc;
+
+         else
+            usage(d.program, "--touch: missing file name");
+      }
+
+      else
+         break;
+   }
+
+   /* A file name is required, but there should be no other arguments if
+    * --default was specified.
+    */
+   if (argc <= 0)
+      usage(d.program, "missing test file");
+
+   /* GCC BUG: if (default_tests && argc != 1) triggers some weird GCC argc
+    * optimization which causes warnings with -Wstrict-overflow!
+    */
+   else if (default_tests) if (argc != 1)
+      usage(d.program, "extra arguments");
+
+   /* The name of the test file is the last argument; remove it. */
+   d.file = argv[--argc];
+
+   fp = fopen(d.file, "rb");
+   if (fp == NULL)
+   {
+      perror(d.file);
+      exit(99);
+   }
+
+   /* First find all the chunks, known and unknown, in the test file, a failure
+    * here aborts the whole test.
+    *
+    * If 'save' is supported then the normal saving method should happen,
+    * otherwise if 'read' is supported then the read callback will do the
+    * same thing.  If both are supported the 'read' callback won't be
+    * instantiated by default.  If 'save' is *not* supported then a user
+    * callback is required even though we can call png_get_unknown_chunks.
+    */
+   if (check(fp, 1, &count_argv, default_flags, &d,
+#     ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+         0
+#     else
+         1
+#     endif
+      ) != PNG_HANDLE_CHUNK_ALWAYS)
+   {
+      fprintf(stderr, "%s: %s: internal error\n", d.program, d.file);
+      exit(99);
+   }
+
+   /* Now find what the various supplied options cause to change: */
+   if (!default_tests)
+   {
+      d.test = cmd; /* acts as a flag to say exit, do not longjmp */
+#     ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+         perform_one_test(fp, argc, argv, default_flags, &d, 0);
+#     endif
+#     ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+         perform_one_test(fp, argc, argv, default_flags, &d, 1);
+#     endif
+      d.test = init;
+   }
+
+   else
+   {
+      const char **test = standard_tests;
+
+      /* Set the exit_test pointer here so we can continue after a libpng error.
+       * NOTE: this leaks memory because the png_struct data from the failing
+       * test is never freed.
+       */
+      while (*test)
+      {
+         const char *this_test = *test++;
+         const char **next = test;
+         int count = display_rc(&d, strict), new_count;
+         const char *result;
+         int arg_count = 0;
+
+         while (*next) ++next, ++arg_count;
+
+         perform_one_test_safe(fp, arg_count, test, default_flags, &d,
+            this_test);
+
+         new_count = display_rc(&d, strict);
+
+         if (new_count == count)
+            result = "PASS";
+
+         else
+            result = "FAIL";
+
+         printf("%s: %s %s\n", result, d.program, this_test);
+
+         test = next+1;
+      }
+   }
+
+   fclose(fp);
+
+   if (display_rc(&d, strict) == 0)
+   {
+      /* Success, touch the success file if appropriate */
+      if (touch_file != NULL)
+      {
+         FILE *fsuccess = fopen(touch_file, "wt");
+
+         if (fsuccess != NULL)
+         {
+            int err = 0;
+            fprintf(fsuccess, "PNG unknown tests succeeded\n");
+            fflush(fsuccess);
+            err = ferror(fsuccess);
+
+            if (fclose(fsuccess) || err)
+            {
+               fprintf(stderr, "%s: write failed\n", touch_file);
+               exit(99);
+            }
+         }
+
+         else
+         {
+            fprintf(stderr, "%s: open failed\n", touch_file);
+            exit(99);
+         }
+      }
+
+      return 0;
+   }
+
+   return 1;
+}
+
+#else /* !(READ_USER_CHUNKS || SAVE_UNKNOWN_CHUNKS) */
+int
+main(void)
+{
+   fprintf(stderr,
+      " test ignored: no support to find out about unknown chunks\n");
+   /* So the test is skipped: */
+   return 77;
+}
+#endif /* READ_USER_CHUNKS || SAVE_UNKNOWN_CHUNKS */
+
+#else /* !(SET_UNKNOWN_CHUNKS && READ) */
+int
+main(void)
+{
+   fprintf(stderr,
+      " test ignored: no support to modify unknown chunk handling\n");
+   /* So the test is skipped: */
+   return 77;
+}
+#endif /* SET_UNKNOWN_CHUNKS && READ*/
diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c
new file mode 100644
index 0000000..bdd3c91
--- /dev/null
+++ b/contrib/libtests/pngvalid.c
@@ -0,0 +1,11746 @@
+
+/* pngvalid.c - validate libpng by constructing then reading png files.
+ *
+ * Last changed in libpng 1.5.25 [December 3, 2015]
+ * Copyright (c) 2014-2015 Glenn Randers-Pehrson
+ * Written by John Cunningham Bowler
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * NOTES:
+ *   This is a C program that is intended to be linked against libpng.  It
+ *   generates bitmaps internally, stores them as PNG files (using the
+ *   sequential write code) then reads them back (using the sequential
+ *   read code) and validates that the result has the correct data.
+ *
+ *   The program can be modified and extended to test the correctness of
+ *   transformations performed by libpng.
+ */
+
+#define _POSIX_SOURCE 1
+#define _ISOC99_SOURCE 1 /* For floating point */
+#define _GNU_SOURCE 1 /* For the floating point exception extension */
+
+#include <signal.h>
+#include <stdio.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+#ifdef HAVE_FEENABLEEXCEPT /* from config.h, if included */
+#  include <fenv.h>
+#endif
+
+#ifndef FE_DIVBYZERO
+#  define FE_DIVBYZERO 0
+#endif
+#ifndef FE_INVALID
+#  define FE_INVALID 0
+#endif
+#ifndef FE_OVERFLOW
+#  define FE_OVERFLOW 0
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+#ifdef PNG_ZLIB_HEADER
+#  include PNG_ZLIB_HEADER
+#else
+#  include <zlib.h>   /* For crc32 */
+#endif
+
+/* 1.6.1 added support for the configure test harness, which uses 77 to indicate
+ * a skipped test, in earlier versions we need to succeed on a skipped test, so:
+ */
+#if PNG_LIBPNG_VER < 10601
+#  define SKIP 0
+#else
+#  define SKIP 77
+#endif
+
+/* pngvalid requires write support and one of the fixed or floating point APIs.
+ */
+#if defined(PNG_WRITE_SUPPORTED) &&\
+   (defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED))
+
+#if PNG_LIBPNG_VER < 10500
+/* This deliberately lacks the const. */
+typedef png_byte *png_const_bytep;
+
+/* This is copied from 1.5.1 png.h: */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+   -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+   -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
+   (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(xIn, pass) \
+   (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+#define PNG_PASS_MASK(pass,off) ( \
+   ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
+   ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+   ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+   ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
+/* These are needed too for the default build: */
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+
+/* This comes from pnglibconf.h afer 1.5: */
+#define PNG_FP_1 100000
+#define PNG_GAMMA_THRESHOLD_FIXED\
+   ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1))
+#endif
+
+#if PNG_LIBPNG_VER < 10600
+   /* 1.6.0 constifies many APIs, the following exists to allow pngvalid to be
+    * compiled against earlier versions.
+    */
+#  define png_const_structp png_structp
+#endif
+
+#include <float.h>  /* For floating point constants */
+#include <stdlib.h> /* For malloc */
+#include <string.h> /* For memcpy, memset */
+#include <math.h>   /* For floor */
+
+/* Unused formal parameter errors are removed using the following macro which is
+ * expected to have no bad effects on performance.
+ */
+#ifndef UNUSED
+#  if defined(__GNUC__) || defined(_MSC_VER)
+#     define UNUSED(param) (void)param;
+#  else
+#     define UNUSED(param)
+#  endif
+#endif
+
+/***************************** EXCEPTION HANDLING *****************************/
+#ifdef PNG_FREESTANDING_TESTS
+#  include <cexcept.h>
+#else
+#  include "../visupng/cexcept.h"
+#endif
+
+#ifdef __cplusplus
+#  define this not_the_cpp_this
+#  define new not_the_cpp_new
+#  define voidcast(type, value) static_cast<type>(value)
+#else
+#  define voidcast(type, value) (value)
+#endif /* __cplusplus */
+
+struct png_store;
+define_exception_type(struct png_store*);
+
+/* The following are macros to reduce typing everywhere where the well known
+ * name 'the_exception_context' must be defined.
+ */
+#define anon_context(ps) struct exception_context *the_exception_context = \
+   &(ps)->exception_context
+#define context(ps,fault) anon_context(ps); png_store *fault
+
+/* This macro returns the number of elements in an array as an (unsigned int),
+ * it is necessary to avoid the inability of certain versions of GCC to use
+ * the value of a compile-time constant when performing range checks.  It must
+ * be passed an array name.
+ */
+#define ARRAY_SIZE(a) ((unsigned int)((sizeof (a))/(sizeof (a)[0])))
+
+/* GCC BUG 66447 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66447) requires
+ * some broken GCC versions to be fixed up to avoid invalid whining about auto
+ * variables that are *not* changed within the scope of a setjmp being changed.
+ *
+ * Feel free to extend the list of broken versions.
+ */
+#define is_gnu(major,minor)\
+   (defined __GNUC__) && __GNUC__ == (major) && __GNUC_MINOR__ == (minor)
+#define is_gnu_patch(major,minor,patch)\
+   is_gnu(major,minor) && __GNUC_PATCHLEVEL__ == 0
+/* For the moment just do it always; all versions of GCC seem to be broken: */
+#ifdef __GNUC__
+   const void * volatile make_volatile_for_gnu;
+#  define gnu_volatile(x) make_volatile_for_gnu = &x;
+#else /* !GNUC broken versions */
+#  define gnu_volatile(x)
+#endif /* !GNUC broken versions */
+
+/******************************* UTILITIES ************************************/
+/* Error handling is particularly problematic in production code - error
+ * handlers often themselves have bugs which lead to programs that detect
+ * minor errors crashing.  The following functions deal with one very
+ * common class of errors in error handlers - attempting to format error or
+ * warning messages into buffers that are too small.
+ */
+static size_t safecat(char *buffer, size_t bufsize, size_t pos,
+   const char *cat)
+{
+   while (pos < bufsize && cat != NULL && *cat != 0)
+      buffer[pos++] = *cat++;
+
+   if (pos >= bufsize)
+      pos = bufsize-1;
+
+   buffer[pos] = 0;
+   return pos;
+}
+
+static size_t safecatn(char *buffer, size_t bufsize, size_t pos, int n)
+{
+   char number[64];
+   sprintf(number, "%d", n);
+   return safecat(buffer, bufsize, pos, number);
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d,
+    int precision)
+{
+   char number[64];
+   sprintf(number, "%.*f", precision, d);
+   return safecat(buffer, bufsize, pos, number);
+}
+#endif
+
+static const char invalid[] = "invalid";
+static const char sep[] = ": ";
+
+static const char *colour_types[8] =
+{
+   "grayscale", invalid, "truecolour", "indexed-colour",
+   "grayscale with alpha", invalid, "truecolour with alpha", invalid
+};
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Convert a double precision value to fixed point. */
+static png_fixed_point
+fix(double d)
+{
+   d = floor(d * PNG_FP_1 + .5);
+   return (png_fixed_point)d;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Generate random bytes.  This uses a boring repeatable algorithm and it
+ * is implemented here so that it gives the same set of numbers on every
+ * architecture.  It's a linear congruential generator (Knuth or Sedgewick
+ * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
+ * Hill, "The Art of Electronics" (Pseudo-Random Bit Sequences and Noise
+ * Generation.)
+ */
+static void
+make_random_bytes(png_uint_32* seed, void* pv, size_t size)
+{
+   png_uint_32 u0 = seed[0], u1 = seed[1];
+   png_bytep bytes = voidcast(png_bytep, pv);
+
+   /* There are thirty three bits, the next bit in the sequence is bit-33 XOR
+    * bit-20.  The top 1 bit is in u1, the bottom 32 are in u0.
+    */
+   size_t i;
+   for (i=0; i<size; ++i)
+   {
+      /* First generate 8 new bits then shift them in at the end. */
+      png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
+      u1 <<= 8;
+      u1 |= u0 >> 24;
+      u0 <<= 8;
+      u0 |= u;
+      *bytes++ = (png_byte)u;
+   }
+
+   seed[0] = u0;
+   seed[1] = u1;
+}
+
+static void
+make_four_random_bytes(png_uint_32* seed, png_bytep bytes)
+{
+   make_random_bytes(seed, bytes, 4);
+}
+
+#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED
+static void
+randomize(void *pv, size_t size)
+{
+   static png_uint_32 random_seed[2] = {0x56789abc, 0xd};
+   make_random_bytes(random_seed, pv, size);
+}
+
+#define RANDOMIZE(this) randomize(&(this), sizeof (this))
+#endif /* READ || WRITE_tRNS */
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static unsigned int
+random_mod(unsigned int max)
+{
+   unsigned int x;
+
+   RANDOMIZE(x);
+
+   return x % max; /* 0 .. max-1 */
+}
+
+#if (defined PNG_READ_RGB_TO_GRAY_SUPPORTED) ||\
+    (defined PNG_READ_FILLER_SUPPORTED)
+static int
+random_choice(void)
+{
+   unsigned char x;
+
+   RANDOMIZE(x);
+
+   return x & 1;
+}
+#endif
+#endif /* PNG_READ_SUPPORTED */
+
+/* A numeric ID based on PNG file characteristics.  The 'do_interlace' field
+ * simply records whether pngvalid did the interlace itself or whether it
+ * was done by libpng.  Width and height must be less than 256.  'palette' is an
+ * index of the palette to use for formats with a palette otherwise a boolean
+ * indicating if a tRNS chunk was generated.
+ */
+#define FILEID(col, depth, palette, interlace, width, height, do_interlace) \
+   ((png_uint_32)((col) + ((depth)<<3) + ((palette)<<8) + ((interlace)<<13) + \
+    (((do_interlace)!=0)<<15) + ((width)<<16) + ((height)<<24)))
+
+#define COL_FROM_ID(id) ((png_byte)((id)& 0x7U))
+#define DEPTH_FROM_ID(id) ((png_byte)(((id) >> 3) & 0x1fU))
+#define PALETTE_FROM_ID(id) (((id) >> 8) & 0x1f)
+#define INTERLACE_FROM_ID(id) ((png_byte)(((id) >> 13) & 0x3))
+#define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1))
+#define WIDTH_FROM_ID(id) (((id)>>16) & 0xff)
+#define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff)
+
+/* Utility to construct a standard name for a standard image. */
+static size_t
+standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
+    int bit_depth, unsigned int npalette, int interlace_type,
+    png_uint_32 w, png_uint_32 h, int do_interlace)
+{
+   pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
+   if (colour_type == 3) /* must have a palette */
+   {
+      pos = safecat(buffer, bufsize, pos, "[");
+      pos = safecatn(buffer, bufsize, pos, npalette);
+      pos = safecat(buffer, bufsize, pos, "]");
+   }
+
+   else if (npalette != 0)
+      pos = safecat(buffer, bufsize, pos, "+tRNS");
+
+   pos = safecat(buffer, bufsize, pos, " ");
+   pos = safecatn(buffer, bufsize, pos, bit_depth);
+   pos = safecat(buffer, bufsize, pos, " bit");
+
+   if (interlace_type != PNG_INTERLACE_NONE)
+   {
+      pos = safecat(buffer, bufsize, pos, " interlaced");
+      if (do_interlace)
+         pos = safecat(buffer, bufsize, pos, "(pngvalid)");
+      else
+         pos = safecat(buffer, bufsize, pos, "(libpng)");
+   }
+
+   if (w > 0 || h > 0)
+   {
+      pos = safecat(buffer, bufsize, pos, " ");
+      pos = safecatn(buffer, bufsize, pos, w);
+      pos = safecat(buffer, bufsize, pos, "x");
+      pos = safecatn(buffer, bufsize, pos, h);
+   }
+
+   return pos;
+}
+
+static size_t
+standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
+{
+   return standard_name(buffer, bufsize, pos, COL_FROM_ID(id),
+      DEPTH_FROM_ID(id), PALETTE_FROM_ID(id), INTERLACE_FROM_ID(id),
+      WIDTH_FROM_ID(id), HEIGHT_FROM_ID(id), DO_INTERLACE_FROM_ID(id));
+}
+
+/* Convenience API and defines to list valid formats.  Note that 16 bit read and
+ * write support is required to do 16 bit read tests (we must be able to make a
+ * 16 bit image to test!)
+ */
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+#  define WRITE_BDHI 4
+#  ifdef PNG_READ_16BIT_SUPPORTED
+#     define READ_BDHI 4
+#     define DO_16BIT
+#  endif
+#else
+#  define WRITE_BDHI 3
+#endif
+#ifndef DO_16BIT
+#  define READ_BDHI 3
+#endif
+
+/* The following defines the number of different palettes to generate for
+ * each log bit depth of a colour type 3 standard image.
+ */
+#define PALETTE_COUNT(bit_depth) ((bit_depth) > 4 ? 1U : 16U)
+
+static int
+next_format(png_bytep colour_type, png_bytep bit_depth,
+   unsigned int* palette_number, int low_depth_gray, int tRNS)
+{
+   if (*bit_depth == 0)
+   {
+      *colour_type = 0;
+      if (low_depth_gray)
+         *bit_depth = 1;
+      else
+         *bit_depth = 8;
+      *palette_number = 0;
+      return 1;
+   }
+
+   if  (*colour_type < 4/*no alpha channel*/)
+   {
+      /* Add multiple palettes for colour type 3, one image with tRNS
+       * and one without for other non-alpha formats:
+       */
+      unsigned int pn = ++*palette_number;
+      png_byte ct = *colour_type;
+
+      if (((ct == 0/*GRAY*/ || ct/*RGB*/ == 2) && tRNS && pn < 2) ||
+          (ct == 3/*PALETTE*/ && pn < PALETTE_COUNT(*bit_depth)))
+         return 1;
+
+      /* No: next bit depth */
+      *palette_number = 0;
+   }
+
+   *bit_depth = (png_byte)(*bit_depth << 1);
+
+   /* Palette images are restricted to 8 bit depth */
+   if (*bit_depth <= 8
+#ifdef DO_16BIT
+         || (*colour_type != 3 && *bit_depth <= 16)
+#endif
+      )
+      return 1;
+
+   /* Move to the next color type, or return 0 at the end. */
+   switch (*colour_type)
+   {
+      case 0:
+         *colour_type = 2;
+         *bit_depth = 8;
+         return 1;
+
+      case 2:
+         *colour_type = 3;
+         *bit_depth = 1;
+         return 1;
+
+      case 3:
+         *colour_type = 4;
+         *bit_depth = 8;
+         return 1;
+
+      case 4:
+         *colour_type = 6;
+         *bit_depth = 8;
+         return 1;
+
+      default:
+         return 0;
+   }
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static unsigned int
+sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
+    png_uint_32 x, unsigned int sample_index, int swap16, int littleendian)
+{
+   png_uint_32 bit_index, result;
+
+   /* Find a sample index for the desired sample: */
+   x *= bit_depth;
+   bit_index = x;
+
+   if ((colour_type & 1) == 0) /* !palette */
+   {
+      if (colour_type & 2)
+         bit_index *= 3;
+
+      if (colour_type & 4)
+         bit_index += x; /* Alpha channel */
+
+      /* Multiple channels; select one: */
+      if (colour_type & (2+4))
+         bit_index += sample_index * bit_depth;
+   }
+
+   /* Return the sample from the row as an integer. */
+   row += bit_index >> 3;
+   result = *row;
+
+   if (bit_depth == 8)
+      return result;
+
+   else if (bit_depth > 8)
+   {
+      if (swap16)
+         return (*++row << 8) + result;
+      else
+         return (result << 8) + *++row;
+   }
+
+   /* Less than 8 bits per sample.  By default PNG has the big end of
+    * the egg on the left of the screen, but if littleendian is set
+    * then the big end is on the right.
+    */
+   bit_index &= 7;
+
+   if (!littleendian)
+      bit_index = 8-bit_index-bit_depth;
+
+   return (result >> bit_index) & ((1U<<bit_depth)-1);
+}
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/* Copy a single pixel, of a given size, from one buffer to another -
+ * while this is basically bit addressed there is an implicit assumption
+ * that pixels 8 or more bits in size are byte aligned and that pixels
+ * do not otherwise cross byte boundaries.  (This is, so far as I know,
+ * universally true in bitmap computer graphics.  [JCB 20101212])
+ *
+ * NOTE: The to and from buffers may be the same.
+ */
+static void
+pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
+   png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize,
+   int littleendian)
+{
+   /* Assume we can multiply by 'size' without overflow because we are
+    * just working in a single buffer.
+    */
+   toIndex *= pixelSize;
+   fromIndex *= pixelSize;
+   if (pixelSize < 8) /* Sub-byte */
+   {
+      /* Mask to select the location of the copied pixel: */
+      unsigned int destMask = ((1U<<pixelSize)-1) <<
+         (littleendian ? toIndex&7 : 8-pixelSize-(toIndex&7));
+      /* The following read the entire pixels and clears the extra: */
+      unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
+      unsigned int sourceByte = fromBuffer[fromIndex >> 3];
+
+      /* Don't rely on << or >> supporting '0' here, just in case: */
+      fromIndex &= 7;
+      if (littleendian)
+      {
+         if (fromIndex > 0) sourceByte >>= fromIndex;
+         if ((toIndex & 7) > 0) sourceByte <<= toIndex & 7;
+      }
+
+      else
+      {
+         if (fromIndex > 0) sourceByte <<= fromIndex;
+         if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
+      }
+
+      toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
+   }
+   else /* One or more bytes */
+      memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3);
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Copy a complete row of pixels, taking into account potential partial
+ * bytes at the end.
+ */
+static void
+row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth,
+      int littleendian)
+{
+   memcpy(toBuffer, fromBuffer, bitWidth >> 3);
+
+   if ((bitWidth & 7) != 0)
+   {
+      unsigned int mask;
+
+      toBuffer += bitWidth >> 3;
+      fromBuffer += bitWidth >> 3;
+      if (littleendian)
+         mask = 0xff << (bitWidth & 7);
+      else
+         mask = 0xff >> (bitWidth & 7);
+      *toBuffer = (png_byte)((*toBuffer & mask) | (*fromBuffer & ~mask));
+   }
+}
+
+/* Compare pixels - they are assumed to start at the first byte in the
+ * given buffers.
+ */
+static int
+pixel_cmp(png_const_bytep pa, png_const_bytep pb, png_uint_32 bit_width)
+{
+#if PNG_LIBPNG_VER < 10506
+   if (memcmp(pa, pb, bit_width>>3) == 0)
+   {
+      png_uint_32 p;
+
+      if ((bit_width & 7) == 0) return 0;
+
+      /* Ok, any differences? */
+      p = pa[bit_width >> 3];
+      p ^= pb[bit_width >> 3];
+
+      if (p == 0) return 0;
+
+      /* There are, but they may not be significant, remove the bits
+       * after the end (the low order bits in PNG.)
+       */
+      bit_width &= 7;
+      p >>= 8-bit_width;
+
+      if (p == 0) return 0;
+   }
+#else
+   /* From libpng-1.5.6 the overwrite should be fixed, so compare the trailing
+    * bits too:
+    */
+   if (memcmp(pa, pb, (bit_width+7)>>3) == 0)
+      return 0;
+#endif
+
+   /* Return the index of the changed byte. */
+   {
+      png_uint_32 where = 0;
+
+      while (pa[where] == pb[where]) ++where;
+      return 1+where;
+   }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/*************************** BASIC PNG FILE WRITING ***************************/
+/* A png_store takes data from the sequential writer or provides data
+ * to the sequential reader.  It can also store the result of a PNG
+ * write for later retrieval.
+ */
+#define STORE_BUFFER_SIZE 500 /* arbitrary */
+typedef struct png_store_buffer
+{
+   struct png_store_buffer*  prev;    /* NOTE: stored in reverse order */
+   png_byte                  buffer[STORE_BUFFER_SIZE];
+} png_store_buffer;
+
+#define FILE_NAME_SIZE 64
+
+typedef struct store_palette_entry /* record of a single palette entry */
+{
+   png_byte red;
+   png_byte green;
+   png_byte blue;
+   png_byte alpha;
+} store_palette_entry, store_palette[256];
+
+typedef struct png_store_file
+{
+   struct png_store_file*  next;      /* as many as you like... */
+   char                    name[FILE_NAME_SIZE];
+   png_uint_32             id;        /* must be correct (see FILEID) */
+   png_size_t              datacount; /* In this (the last) buffer */
+   png_store_buffer        data;      /* Last buffer in file */
+   int                     npalette;  /* Number of entries in palette */
+   store_palette_entry*    palette;   /* May be NULL */
+} png_store_file;
+
+/* The following is a pool of memory allocated by a single libpng read or write
+ * operation.
+ */
+typedef struct store_pool
+{
+   struct png_store    *store;   /* Back pointer */
+   struct store_memory *list;    /* List of allocated memory */
+   png_byte             mark[4]; /* Before and after data */
+
+   /* Statistics for this run. */
+   png_alloc_size_t     max;     /* Maximum single allocation */
+   png_alloc_size_t     current; /* Current allocation */
+   png_alloc_size_t     limit;   /* Highest current allocation */
+   png_alloc_size_t     total;   /* Total allocation */
+
+   /* Overall statistics (retained across successive runs). */
+   png_alloc_size_t     max_max;
+   png_alloc_size_t     max_limit;
+   png_alloc_size_t     max_total;
+} store_pool;
+
+typedef struct png_store
+{
+   /* For cexcept.h exception handling - simply store one of these;
+    * the context is a self pointer but it may point to a different
+    * png_store (in fact it never does in this program.)
+    */
+   struct exception_context
+                      exception_context;
+
+   unsigned int       verbose :1;
+   unsigned int       treat_warnings_as_errors :1;
+   unsigned int       expect_error :1;
+   unsigned int       expect_warning :1;
+   unsigned int       saw_warning :1;
+   unsigned int       speed :1;
+   unsigned int       progressive :1; /* use progressive read */
+   unsigned int       validated :1;   /* used as a temporary flag */
+   int                nerrors;
+   int                nwarnings;
+   int                noptions;       /* number of options below: */
+   struct {
+      unsigned char   option;         /* option number, 0..30 */
+      unsigned char   setting;        /* setting (unset,invalid,on,off) */
+   }                  options[16];
+   char               test[128];      /* Name of test */
+   char               error[256];
+
+   /* Read fields */
+   png_structp        pread;    /* Used to read a saved file */
+   png_infop          piread;
+   png_store_file*    current;  /* Set when reading */
+   png_store_buffer*  next;     /* Set when reading */
+   png_size_t         readpos;  /* Position in *next */
+   png_byte*          image;    /* Buffer for reading interlaced images */
+   png_size_t         cb_image; /* Size of this buffer */
+   png_size_t         cb_row;   /* Row size of the image(s) */
+   png_uint_32        image_h;  /* Number of rows in a single image */
+   store_pool         read_memory_pool;
+
+   /* Write fields */
+   png_store_file*    saved;
+   png_structp        pwrite;   /* Used when writing a new file */
+   png_infop          piwrite;
+   png_size_t         writepos; /* Position in .new */
+   char               wname[FILE_NAME_SIZE];
+   png_store_buffer   new;      /* The end of the new PNG file being written. */
+   store_pool         write_memory_pool;
+   store_palette_entry* palette;
+   int                  npalette;
+} png_store;
+
+/* Initialization and cleanup */
+static void
+store_pool_mark(png_bytep mark)
+{
+   static png_uint_32 store_seed[2] = { 0x12345678, 1};
+
+   make_four_random_bytes(store_seed, mark);
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Use this for random 32 bit values; this function makes sure the result is
+ * non-zero.
+ */
+static png_uint_32
+random_32(void)
+{
+
+   for (;;)
+   {
+      png_byte mark[4];
+      png_uint_32 result;
+
+      store_pool_mark(mark);
+      result = png_get_uint_32(mark);
+
+      if (result != 0)
+         return result;
+   }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+static void
+store_pool_init(png_store *ps, store_pool *pool)
+{
+   memset(pool, 0, sizeof *pool);
+
+   pool->store = ps;
+   pool->list = NULL;
+   pool->max = pool->current = pool->limit = pool->total = 0;
+   pool->max_max = pool->max_limit = pool->max_total = 0;
+   store_pool_mark(pool->mark);
+}
+
+static void
+store_init(png_store* ps)
+{
+   memset(ps, 0, sizeof *ps);
+   init_exception_context(&ps->exception_context);
+   store_pool_init(ps, &ps->read_memory_pool);
+   store_pool_init(ps, &ps->write_memory_pool);
+   ps->verbose = 0;
+   ps->treat_warnings_as_errors = 0;
+   ps->expect_error = 0;
+   ps->expect_warning = 0;
+   ps->saw_warning = 0;
+   ps->speed = 0;
+   ps->progressive = 0;
+   ps->validated = 0;
+   ps->nerrors = ps->nwarnings = 0;
+   ps->pread = NULL;
+   ps->piread = NULL;
+   ps->saved = ps->current = NULL;
+   ps->next = NULL;
+   ps->readpos = 0;
+   ps->image = NULL;
+   ps->cb_image = 0;
+   ps->cb_row = 0;
+   ps->image_h = 0;
+   ps->pwrite = NULL;
+   ps->piwrite = NULL;
+   ps->writepos = 0;
+   ps->new.prev = NULL;
+   ps->palette = NULL;
+   ps->npalette = 0;
+   ps->noptions = 0;
+}
+
+static void
+store_freebuffer(png_store_buffer* psb)
+{
+   if (psb->prev)
+   {
+      store_freebuffer(psb->prev);
+      free(psb->prev);
+      psb->prev = NULL;
+   }
+}
+
+static void
+store_freenew(png_store *ps)
+{
+   store_freebuffer(&ps->new);
+   ps->writepos = 0;
+   if (ps->palette != NULL)
+   {
+      free(ps->palette);
+      ps->palette = NULL;
+      ps->npalette = 0;
+   }
+}
+
+static void
+store_storenew(png_store *ps)
+{
+   png_store_buffer *pb;
+
+   if (ps->writepos != STORE_BUFFER_SIZE)
+      png_error(ps->pwrite, "invalid store call");
+
+   pb = voidcast(png_store_buffer*, malloc(sizeof *pb));
+
+   if (pb == NULL)
+      png_error(ps->pwrite, "store new: OOM");
+
+   *pb = ps->new;
+   ps->new.prev = pb;
+   ps->writepos = 0;
+}
+
+static void
+store_freefile(png_store_file **ppf)
+{
+   if (*ppf != NULL)
+   {
+      store_freefile(&(*ppf)->next);
+
+      store_freebuffer(&(*ppf)->data);
+      (*ppf)->datacount = 0;
+      if ((*ppf)->palette != NULL)
+      {
+         free((*ppf)->palette);
+         (*ppf)->palette = NULL;
+         (*ppf)->npalette = 0;
+      }
+      free(*ppf);
+      *ppf = NULL;
+   }
+}
+
+/* Main interface to file storeage, after writing a new PNG file (see the API
+ * below) call store_storefile to store the result with the given name and id.
+ */
+static void
+store_storefile(png_store *ps, png_uint_32 id)
+{
+   png_store_file *pf = voidcast(png_store_file*, malloc(sizeof *pf));
+   if (pf == NULL)
+      png_error(ps->pwrite, "storefile: OOM");
+   safecat(pf->name, sizeof pf->name, 0, ps->wname);
+   pf->id = id;
+   pf->data = ps->new;
+   pf->datacount = ps->writepos;
+   ps->new.prev = NULL;
+   ps->writepos = 0;
+   pf->palette = ps->palette;
+   pf->npalette = ps->npalette;
+   ps->palette = 0;
+   ps->npalette = 0;
+
+   /* And save it. */
+   pf->next = ps->saved;
+   ps->saved = pf;
+}
+
+/* Generate an error message (in the given buffer) */
+static size_t
+store_message(png_store *ps, png_const_structp pp, char *buffer, size_t bufsize,
+   size_t pos, const char *msg)
+{
+   if (pp != NULL && pp == ps->pread)
+   {
+      /* Reading a file */
+      pos = safecat(buffer, bufsize, pos, "read: ");
+
+      if (ps->current != NULL)
+      {
+         pos = safecat(buffer, bufsize, pos, ps->current->name);
+         pos = safecat(buffer, bufsize, pos, sep);
+      }
+   }
+
+   else if (pp != NULL && pp == ps->pwrite)
+   {
+      /* Writing a file */
+      pos = safecat(buffer, bufsize, pos, "write: ");
+      pos = safecat(buffer, bufsize, pos, ps->wname);
+      pos = safecat(buffer, bufsize, pos, sep);
+   }
+
+   else
+   {
+      /* Neither reading nor writing (or a memory error in struct delete) */
+      pos = safecat(buffer, bufsize, pos, "pngvalid: ");
+   }
+
+   if (ps->test[0] != 0)
+   {
+      pos = safecat(buffer, bufsize, pos, ps->test);
+      pos = safecat(buffer, bufsize, pos, sep);
+   }
+   pos = safecat(buffer, bufsize, pos, msg);
+   return pos;
+}
+
+/* Verbose output to the error stream: */
+static void
+store_verbose(png_store *ps, png_const_structp pp, png_const_charp prefix,
+   png_const_charp message)
+{
+   char buffer[512];
+
+   if (prefix)
+      fputs(prefix, stderr);
+
+   (void)store_message(ps, pp, buffer, sizeof buffer, 0, message);
+   fputs(buffer, stderr);
+   fputc('\n', stderr);
+}
+
+/* Log an error or warning - the relevant count is always incremented. */
+static void
+store_log(png_store* ps, png_const_structp pp, png_const_charp message,
+   int is_error)
+{
+   /* The warning is copied to the error buffer if there are no errors and it is
+    * the first warning.  The error is copied to the error buffer if it is the
+    * first error (overwriting any prior warnings).
+    */
+   if (is_error ? (ps->nerrors)++ == 0 :
+       (ps->nwarnings)++ == 0 && ps->nerrors == 0)
+      store_message(ps, pp, ps->error, sizeof ps->error, 0, message);
+
+   if (ps->verbose)
+      store_verbose(ps, pp, is_error ? "error: " : "warning: ", message);
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Internal error function, called with a png_store but no libpng stuff. */
+static void
+internal_error(png_store *ps, png_const_charp message)
+{
+   store_log(ps, NULL, message, 1 /* error */);
+
+   /* And finally throw an exception. */
+   {
+      struct exception_context *the_exception_context = &ps->exception_context;
+      Throw ps;
+   }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Functions to use as PNG callbacks. */
+static void PNGCBAPI
+store_error(png_structp ppIn, png_const_charp message) /* PNG_NORETURN */
+{
+   png_const_structp pp = ppIn;
+   png_store *ps = voidcast(png_store*, png_get_error_ptr(pp));
+
+   if (!ps->expect_error)
+      store_log(ps, pp, message, 1 /* error */);
+
+   /* And finally throw an exception. */
+   {
+      struct exception_context *the_exception_context = &ps->exception_context;
+      Throw ps;
+   }
+}
+
+static void PNGCBAPI
+store_warning(png_structp ppIn, png_const_charp message)
+{
+   png_const_structp pp = ppIn;
+   png_store *ps = voidcast(png_store*, png_get_error_ptr(pp));
+
+   if (!ps->expect_warning)
+      store_log(ps, pp, message, 0 /* warning */);
+   else
+      ps->saw_warning = 1;
+}
+
+/* These somewhat odd functions are used when reading an image to ensure that
+ * the buffer is big enough, the png_structp is for errors.
+ */
+/* Return a single row from the correct image. */
+static png_bytep
+store_image_row(const png_store* ps, png_const_structp pp, int nImage,
+   png_uint_32 y)
+{
+   png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
+
+   if (ps->image == NULL)
+      png_error(pp, "no allocated image");
+
+   if (coffset + ps->cb_row + 3 > ps->cb_image)
+      png_error(pp, "image too small");
+
+   return ps->image + coffset;
+}
+
+static void
+store_image_free(png_store *ps, png_const_structp pp)
+{
+   if (ps->image != NULL)
+   {
+      png_bytep image = ps->image;
+
+      if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
+      {
+         if (pp != NULL)
+            png_error(pp, "png_store image overwrite (1)");
+         else
+            store_log(ps, NULL, "png_store image overwrite (2)", 1);
+      }
+
+      ps->image = NULL;
+      ps->cb_image = 0;
+      --image;
+      free(image);
+   }
+}
+
+static void
+store_ensure_image(png_store *ps, png_const_structp pp, int nImages,
+   png_size_t cbRow, png_uint_32 cRows)
+{
+   png_size_t cb = nImages * cRows * (cbRow + 5);
+
+   if (ps->cb_image < cb)
+   {
+      png_bytep image;
+
+      store_image_free(ps, pp);
+
+      /* The buffer is deliberately mis-aligned. */
+      image = voidcast(png_bytep, malloc(cb+2));
+      if (image == NULL)
+      {
+         /* Called from the startup - ignore the error for the moment. */
+         if (pp == NULL)
+            return;
+
+         png_error(pp, "OOM allocating image buffer");
+      }
+
+      /* These magic tags are used to detect overwrites above. */
+      ++image;
+      image[-1] = 0xed;
+      image[cb] = 0xfe;
+
+      ps->image = image;
+      ps->cb_image = cb;
+   }
+
+   /* We have an adequate sized image; lay out the rows.  There are 2 bytes at
+    * the start and three at the end of each (this ensures that the row
+    * alignment starts out odd - 2+1 and changes for larger images on each row.)
+    */
+   ps->cb_row = cbRow;
+   ps->image_h = cRows;
+
+   /* For error checking, the whole buffer is set to 10110010 (0xb2 - 178).
+    * This deliberately doesn't match the bits in the size test image which are
+    * outside the image; these are set to 0xff (all 1).  To make the row
+    * comparison work in the 'size' test case the size rows are pre-initialized
+    * to the same value prior to calling 'standard_row'.
+    */
+   memset(ps->image, 178, cb);
+
+   /* Then put in the marks. */
+   while (--nImages >= 0)
+   {
+      png_uint_32 y;
+
+      for (y=0; y<cRows; ++y)
+      {
+         png_bytep row = store_image_row(ps, pp, nImages, y);
+
+         /* The markers: */
+         row[-2] = 190;
+         row[-1] = 239;
+         row[cbRow] = 222;
+         row[cbRow+1] = 173;
+         row[cbRow+2] = 17;
+      }
+   }
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+store_image_check(const png_store* ps, png_const_structp pp, int iImage)
+{
+   png_const_bytep image = ps->image;
+
+   if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
+      png_error(pp, "image overwrite");
+   else
+   {
+      png_size_t cbRow = ps->cb_row;
+      png_uint_32 rows = ps->image_h;
+
+      image += iImage * (cbRow+5) * ps->image_h;
+
+      image += 2; /* skip image first row markers */
+
+      while (rows-- > 0)
+      {
+         if (image[-2] != 190 || image[-1] != 239)
+            png_error(pp, "row start overwritten");
+
+         if (image[cbRow] != 222 || image[cbRow+1] != 173 ||
+            image[cbRow+2] != 17)
+            png_error(pp, "row end overwritten");
+
+         image += cbRow+5;
+      }
+   }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+static void PNGCBAPI
+store_write(png_structp ppIn, png_bytep pb, png_size_t st)
+{
+   png_const_structp pp = ppIn;
+   png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
+
+   if (ps->pwrite != pp)
+      png_error(pp, "store state damaged");
+
+   while (st > 0)
+   {
+      size_t cb;
+
+      if (ps->writepos >= STORE_BUFFER_SIZE)
+         store_storenew(ps);
+
+      cb = st;
+
+      if (cb > STORE_BUFFER_SIZE - ps->writepos)
+         cb = STORE_BUFFER_SIZE - ps->writepos;
+
+      memcpy(ps->new.buffer + ps->writepos, pb, cb);
+      pb += cb;
+      st -= cb;
+      ps->writepos += cb;
+   }
+}
+
+static void PNGCBAPI
+store_flush(png_structp ppIn)
+{
+   UNUSED(ppIn) /*DOES NOTHING*/
+}
+
+#ifdef PNG_READ_SUPPORTED
+static size_t
+store_read_buffer_size(png_store *ps)
+{
+   /* Return the bytes available for read in the current buffer. */
+   if (ps->next != &ps->current->data)
+      return STORE_BUFFER_SIZE;
+
+   return ps->current->datacount;
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Return total bytes available for read. */
+static size_t
+store_read_buffer_avail(png_store *ps)
+{
+   if (ps->current != NULL && ps->next != NULL)
+   {
+      png_store_buffer *next = &ps->current->data;
+      size_t cbAvail = ps->current->datacount;
+
+      while (next != ps->next && next != NULL)
+      {
+         next = next->prev;
+         cbAvail += STORE_BUFFER_SIZE;
+      }
+
+      if (next != ps->next)
+         png_error(ps->pread, "buffer read error");
+
+      if (cbAvail > ps->readpos)
+         return cbAvail - ps->readpos;
+   }
+
+   return 0;
+}
+#endif
+
+static int
+store_read_buffer_next(png_store *ps)
+{
+   png_store_buffer *pbOld = ps->next;
+   png_store_buffer *pbNew = &ps->current->data;
+   if (pbOld != pbNew)
+   {
+      while (pbNew != NULL && pbNew->prev != pbOld)
+         pbNew = pbNew->prev;
+
+      if (pbNew != NULL)
+      {
+         ps->next = pbNew;
+         ps->readpos = 0;
+         return 1;
+      }
+
+      png_error(ps->pread, "buffer lost");
+   }
+
+   return 0; /* EOF or error */
+}
+
+/* Need separate implementation and callback to allow use of the same code
+ * during progressive read, where the io_ptr is set internally by libpng.
+ */
+static void
+store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
+{
+   if (ps->current == NULL || ps->next == NULL)
+      png_error(ps->pread, "store state damaged");
+
+   while (st > 0)
+   {
+      size_t cbAvail = store_read_buffer_size(ps) - ps->readpos;
+
+      if (cbAvail > 0)
+      {
+         if (cbAvail > st) cbAvail = st;
+         memcpy(pb, ps->next->buffer + ps->readpos, cbAvail);
+         st -= cbAvail;
+         pb += cbAvail;
+         ps->readpos += cbAvail;
+      }
+
+      else if (!store_read_buffer_next(ps))
+         png_error(ps->pread, "read beyond end of file");
+   }
+}
+
+static void PNGCBAPI
+store_read(png_structp ppIn, png_bytep pb, png_size_t st)
+{
+   png_const_structp pp = ppIn;
+   png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
+
+   if (ps == NULL || ps->pread != pp)
+      png_error(pp, "bad store read call");
+
+   store_read_imp(ps, pb, st);
+}
+
+static void
+store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
+{
+   /* Notice that a call to store_read will cause this function to fail because
+    * readpos will be set.
+    */
+   if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
+      png_error(pp, "store state damaged (progressive)");
+
+   do
+   {
+      if (ps->readpos != 0)
+         png_error(pp, "store_read called during progressive read");
+
+      png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
+   }
+   while (store_read_buffer_next(ps));
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* The caller must fill this in: */
+static store_palette_entry *
+store_write_palette(png_store *ps, int npalette)
+{
+   if (ps->pwrite == NULL)
+      store_log(ps, NULL, "attempt to write palette without write stream", 1);
+
+   if (ps->palette != NULL)
+      png_error(ps->pwrite, "multiple store_write_palette calls");
+
+   /* This function can only return NULL if called with '0'! */
+   if (npalette > 0)
+   {
+      ps->palette = voidcast(store_palette_entry*, malloc(npalette *
+         sizeof *ps->palette));
+
+      if (ps->palette == NULL)
+         png_error(ps->pwrite, "store new palette: OOM");
+
+      ps->npalette = npalette;
+   }
+
+   return ps->palette;
+}
+
+#ifdef PNG_READ_SUPPORTED
+static store_palette_entry *
+store_current_palette(png_store *ps, int *npalette)
+{
+   /* This is an internal error (the call has been made outside a read
+    * operation.)
+    */
+   if (ps->current == NULL)
+   {
+      store_log(ps, ps->pread, "no current stream for palette", 1);
+      return NULL;
+   }
+
+   /* The result may be null if there is no palette. */
+   *npalette = ps->current->npalette;
+   return ps->current->palette;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/***************************** MEMORY MANAGEMENT*** ***************************/
+#ifdef PNG_USER_MEM_SUPPORTED
+/* A store_memory is simply the header for an allocated block of memory.  The
+ * pointer returned to libpng is just after the end of the header block, the
+ * allocated memory is followed by a second copy of the 'mark'.
+ */
+typedef struct store_memory
+{
+   store_pool          *pool;    /* Originating pool */
+   struct store_memory *next;    /* Singly linked list */
+   png_alloc_size_t     size;    /* Size of memory allocated */
+   png_byte             mark[4]; /* ID marker */
+} store_memory;
+
+/* Handle a fatal error in memory allocation.  This calls png_error if the
+ * libpng struct is non-NULL, else it outputs a message and returns.  This means
+ * that a memory problem while libpng is running will abort (png_error) the
+ * handling of particular file while one in cleanup (after the destroy of the
+ * struct has returned) will simply keep going and free (or attempt to free)
+ * all the memory.
+ */
+static void
+store_pool_error(png_store *ps, png_const_structp pp, const char *msg)
+{
+   if (pp != NULL)
+      png_error(pp, msg);
+
+   /* Else we have to do it ourselves.  png_error eventually calls store_log,
+    * above.  store_log accepts a NULL png_structp - it just changes what gets
+    * output by store_message.
+    */
+   store_log(ps, pp, msg, 1 /* error */);
+}
+
+static void
+store_memory_free(png_const_structp pp, store_pool *pool, store_memory *memory)
+{
+   /* Note that pp may be NULL (see store_pool_delete below), the caller has
+    * found 'memory' in pool->list *and* unlinked this entry, so this is a valid
+    * pointer (for sure), but the contents may have been trashed.
+    */
+   if (memory->pool != pool)
+      store_pool_error(pool->store, pp, "memory corrupted (pool)");
+
+   else if (memcmp(memory->mark, pool->mark, sizeof memory->mark) != 0)
+      store_pool_error(pool->store, pp, "memory corrupted (start)");
+
+   /* It should be safe to read the size field now. */
+   else
+   {
+      png_alloc_size_t cb = memory->size;
+
+      if (cb > pool->max)
+         store_pool_error(pool->store, pp, "memory corrupted (size)");
+
+      else if (memcmp((png_bytep)(memory+1)+cb, pool->mark, sizeof pool->mark)
+         != 0)
+         store_pool_error(pool->store, pp, "memory corrupted (end)");
+
+      /* Finally give the library a chance to find problems too: */
+      else
+         {
+         pool->current -= cb;
+         free(memory);
+         }
+   }
+}
+
+static void
+store_pool_delete(png_store *ps, store_pool *pool)
+{
+   if (pool->list != NULL)
+   {
+      fprintf(stderr, "%s: %s %s: memory lost (list follows):\n", ps->test,
+         pool == &ps->read_memory_pool ? "read" : "write",
+         pool == &ps->read_memory_pool ? (ps->current != NULL ?
+            ps->current->name : "unknown file") : ps->wname);
+      ++ps->nerrors;
+
+      do
+      {
+         store_memory *next = pool->list;
+         pool->list = next->next;
+         next->next = NULL;
+
+         fprintf(stderr, "\t%lu bytes @ %p\n",
+             (unsigned long)next->size, (const void*)(next+1));
+         /* The NULL means this will always return, even if the memory is
+          * corrupted.
+          */
+         store_memory_free(NULL, pool, next);
+      }
+      while (pool->list != NULL);
+   }
+
+   /* And reset the other fields too for the next time. */
+   if (pool->max > pool->max_max) pool->max_max = pool->max;
+   pool->max = 0;
+   if (pool->current != 0) /* unexpected internal error */
+      fprintf(stderr, "%s: %s %s: memory counter mismatch (internal error)\n",
+         ps->test, pool == &ps->read_memory_pool ? "read" : "write",
+         pool == &ps->read_memory_pool ? (ps->current != NULL ?
+            ps->current->name : "unknown file") : ps->wname);
+   pool->current = 0;
+
+   if (pool->limit > pool->max_limit)
+      pool->max_limit = pool->limit;
+
+   pool->limit = 0;
+
+   if (pool->total > pool->max_total)
+      pool->max_total = pool->total;
+
+   pool->total = 0;
+
+   /* Get a new mark too. */
+   store_pool_mark(pool->mark);
+}
+
+/* The memory callbacks: */
+static png_voidp PNGCBAPI
+store_malloc(png_structp ppIn, png_alloc_size_t cb)
+{
+   png_const_structp pp = ppIn;
+   store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp));
+   store_memory *new = voidcast(store_memory*, malloc(cb + (sizeof *new) +
+      (sizeof pool->mark)));
+
+   if (new != NULL)
+   {
+      if (cb > pool->max)
+         pool->max = cb;
+
+      pool->current += cb;
+
+      if (pool->current > pool->limit)
+         pool->limit = pool->current;
+
+      pool->total += cb;
+
+      new->size = cb;
+      memcpy(new->mark, pool->mark, sizeof new->mark);
+      memcpy((png_byte*)(new+1) + cb, pool->mark, sizeof pool->mark);
+      new->pool = pool;
+      new->next = pool->list;
+      pool->list = new;
+      ++new;
+   }
+
+   else
+   {
+      /* NOTE: the PNG user malloc function cannot use the png_ptr it is passed
+       * other than to retrieve the allocation pointer!  libpng calls the
+       * store_malloc callback in two basic cases:
+       *
+       * 1) From png_malloc; png_malloc will do a png_error itself if NULL is
+       *    returned.
+       * 2) From png_struct or png_info structure creation; png_malloc is
+       *    to return so cleanup can be performed.
+       *
+       * To handle this store_malloc can log a message, but can't do anything
+       * else.
+       */
+      store_log(pool->store, pp, "out of memory", 1 /* is_error */);
+   }
+
+   return new;
+}
+
+static void PNGCBAPI
+store_free(png_structp ppIn, png_voidp memory)
+{
+   png_const_structp pp = ppIn;
+   store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp));
+   store_memory *this = voidcast(store_memory*, memory), **test;
+
+   /* Because libpng calls store_free with a dummy png_struct when deleting
+    * png_struct or png_info via png_destroy_struct_2 it is necessary to check
+    * the passed in png_structp to ensure it is valid, and not pass it to
+    * png_error if it is not.
+    */
+   if (pp != pool->store->pread && pp != pool->store->pwrite)
+      pp = NULL;
+
+   /* First check that this 'memory' really is valid memory - it must be in the
+    * pool list.  If it is, use the shared memory_free function to free it.
+    */
+   --this;
+   for (test = &pool->list; *test != this; test = &(*test)->next)
+   {
+      if (*test == NULL)
+      {
+         store_pool_error(pool->store, pp, "bad pointer to free");
+         return;
+      }
+   }
+
+   /* Unlink this entry, *test == this. */
+   *test = this->next;
+   this->next = NULL;
+   store_memory_free(pp, pool, this);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+/* Setup functions. */
+/* Cleanup when aborting a write or after storing the new file. */
+static void
+store_write_reset(png_store *ps)
+{
+   if (ps->pwrite != NULL)
+   {
+      anon_context(ps);
+
+      Try
+         png_destroy_write_struct(&ps->pwrite, &ps->piwrite);
+
+      Catch_anonymous
+      {
+         /* memory corruption: continue. */
+      }
+
+      ps->pwrite = NULL;
+      ps->piwrite = NULL;
+   }
+
+   /* And make sure that all the memory has been freed - this will output
+    * spurious errors in the case of memory corruption above, but this is safe.
+    */
+#  ifdef PNG_USER_MEM_SUPPORTED
+      store_pool_delete(ps, &ps->write_memory_pool);
+#  endif
+
+   store_freenew(ps);
+}
+
+/* The following is the main write function, it returns a png_struct and,
+ * optionally, a png_info suitable for writiing a new PNG file.  Use
+ * store_storefile above to record this file after it has been written.  The
+ * returned libpng structures as destroyed by store_write_reset above.
+ */
+static png_structp
+set_store_for_write(png_store *ps, png_infopp ppi, const char *name)
+{
+   anon_context(ps);
+
+   Try
+   {
+      if (ps->pwrite != NULL)
+         png_error(ps->pwrite, "write store already in use");
+
+      store_write_reset(ps);
+      safecat(ps->wname, sizeof ps->wname, 0, name);
+
+      /* Don't do the slow memory checks if doing a speed test, also if user
+       * memory is not supported we can't do it anyway.
+       */
+#     ifdef PNG_USER_MEM_SUPPORTED
+         if (!ps->speed)
+            ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
+               ps, store_error, store_warning, &ps->write_memory_pool,
+               store_malloc, store_free);
+
+         else
+#     endif
+         ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+            ps, store_error, store_warning);
+
+      png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
+
+#     ifdef PNG_SET_OPTION_SUPPORTED
+         {
+            int opt;
+            for (opt=0; opt<ps->noptions; ++opt)
+               if (png_set_option(ps->pwrite, ps->options[opt].option,
+                  ps->options[opt].setting) == PNG_OPTION_INVALID)
+                  png_error(ps->pwrite, "png option invalid");
+         }
+#     endif
+
+      if (ppi != NULL)
+         *ppi = ps->piwrite = png_create_info_struct(ps->pwrite);
+   }
+
+   Catch_anonymous
+      return NULL;
+
+   return ps->pwrite;
+}
+
+/* Cleanup when finished reading (either due to error or in the success case).
+ * This routine exists even when there is no read support to make the code
+ * tidier (avoid a mass of ifdefs) and so easier to maintain.
+ */
+static void
+store_read_reset(png_store *ps)
+{
+#  ifdef PNG_READ_SUPPORTED
+      if (ps->pread != NULL)
+      {
+         anon_context(ps);
+
+         Try
+            png_destroy_read_struct(&ps->pread, &ps->piread, NULL);
+
+         Catch_anonymous
+         {
+            /* error already output: continue */
+         }
+
+         ps->pread = NULL;
+         ps->piread = NULL;
+      }
+#  endif
+
+#  ifdef PNG_USER_MEM_SUPPORTED
+      /* Always do this to be safe. */
+      store_pool_delete(ps, &ps->read_memory_pool);
+#  endif
+
+   ps->current = NULL;
+   ps->next = NULL;
+   ps->readpos = 0;
+   ps->validated = 0;
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+store_read_set(png_store *ps, png_uint_32 id)
+{
+   png_store_file *pf = ps->saved;
+
+   while (pf != NULL)
+   {
+      if (pf->id == id)
+      {
+         ps->current = pf;
+         ps->next = NULL;
+         store_read_buffer_next(ps);
+         return;
+      }
+
+      pf = pf->next;
+   }
+
+   {
+      size_t pos;
+      char msg[FILE_NAME_SIZE+64];
+
+      pos = standard_name_from_id(msg, sizeof msg, 0, id);
+      pos = safecat(msg, sizeof msg, pos, ": file not found");
+      png_error(ps->pread, msg);
+   }
+}
+
+/* The main interface for reading a saved file - pass the id number of the file
+ * to retrieve.  Ids must be unique or the earlier file will be hidden.  The API
+ * returns a png_struct and, optionally, a png_info.  Both of these will be
+ * destroyed by store_read_reset above.
+ */
+static png_structp
+set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
+   const char *name)
+{
+   /* Set the name for png_error */
+   safecat(ps->test, sizeof ps->test, 0, name);
+
+   if (ps->pread != NULL)
+      png_error(ps->pread, "read store already in use");
+
+   store_read_reset(ps);
+
+   /* Both the create APIs can return NULL if used in their default mode
+    * (because there is no other way of handling an error because the jmp_buf
+    * by default is stored in png_struct and that has not been allocated!)
+    * However, given that store_error works correctly in these circumstances
+    * we don't ever expect NULL in this program.
+    */
+#  ifdef PNG_USER_MEM_SUPPORTED
+      if (!ps->speed)
+         ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
+             store_error, store_warning, &ps->read_memory_pool, store_malloc,
+             store_free);
+
+      else
+#  endif
+   ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps, store_error,
+      store_warning);
+
+   if (ps->pread == NULL)
+   {
+      struct exception_context *the_exception_context = &ps->exception_context;
+
+      store_log(ps, NULL, "png_create_read_struct returned NULL (unexpected)",
+         1 /*error*/);
+
+      Throw ps;
+   }
+
+#  ifdef PNG_SET_OPTION_SUPPORTED
+      {
+         int opt;
+         for (opt=0; opt<ps->noptions; ++opt)
+            if (png_set_option(ps->pread, ps->options[opt].option,
+               ps->options[opt].setting) == PNG_OPTION_INVALID)
+                  png_error(ps->pread, "png option invalid");
+      }
+#  endif
+
+   store_read_set(ps, id);
+
+   if (ppi != NULL)
+      *ppi = ps->piread = png_create_info_struct(ps->pread);
+
+   return ps->pread;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* The overall cleanup of a store simply calls the above then removes all the
+ * saved files.  This does not delete the store itself.
+ */
+static void
+store_delete(png_store *ps)
+{
+   store_write_reset(ps);
+   store_read_reset(ps);
+   store_freefile(&ps->saved);
+   store_image_free(ps, NULL);
+}
+
+/*********************** PNG FILE MODIFICATION ON READ ************************/
+/* Files may be modified on read.  The following structure contains a complete
+ * png_store together with extra members to handle modification and a special
+ * read callback for libpng.  To use this the 'modifications' field must be set
+ * to a list of png_modification structures that actually perform the
+ * modification, otherwise a png_modifier is functionally equivalent to a
+ * png_store.  There is a special read function, set_modifier_for_read, which
+ * replaces set_store_for_read.
+ */
+typedef enum modifier_state
+{
+   modifier_start,                        /* Initial value */
+   modifier_signature,                    /* Have a signature */
+   modifier_IHDR                          /* Have an IHDR */
+} modifier_state;
+
+typedef struct CIE_color
+{
+   /* A single CIE tristimulus value, representing the unique response of a
+    * standard observer to a variety of light spectra.  The observer recognizes
+    * all spectra that produce this response as the same color, therefore this
+    * is effectively a description of a color.
+    */
+   double X, Y, Z;
+} CIE_color;
+
+typedef struct color_encoding
+{
+   /* A description of an (R,G,B) encoding of color (as defined above); this
+    * includes the actual colors of the (R,G,B) triples (1,0,0), (0,1,0) and
+    * (0,0,1) plus an encoding value that is used to encode the linear
+    * components R, G and B to give the actual values R^gamma, G^gamma and
+    * B^gamma that are stored.
+    */
+   double    gamma;            /* Encoding (file) gamma of space */
+   CIE_color red, green, blue; /* End points */
+} color_encoding;
+
+#ifdef PNG_READ_SUPPORTED
+#if defined PNG_READ_TRANSFORMS_SUPPORTED && defined PNG_READ_cHRM_SUPPORTED
+static double
+chromaticity_x(CIE_color c)
+{
+   return c.X / (c.X + c.Y + c.Z);
+}
+
+static double
+chromaticity_y(CIE_color c)
+{
+   return c.Y / (c.X + c.Y + c.Z);
+}
+
+static CIE_color
+white_point(const color_encoding *encoding)
+{
+   CIE_color white;
+
+   white.X = encoding->red.X + encoding->green.X + encoding->blue.X;
+   white.Y = encoding->red.Y + encoding->green.Y + encoding->blue.Y;
+   white.Z = encoding->red.Z + encoding->green.Z + encoding->blue.Z;
+
+   return white;
+}
+#endif /* READ_TRANSFORMS && READ_cHRM */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+static void
+normalize_color_encoding(color_encoding *encoding)
+{
+   const double whiteY = encoding->red.Y + encoding->green.Y +
+      encoding->blue.Y;
+
+   if (whiteY != 1)
+   {
+      encoding->red.X /= whiteY;
+      encoding->red.Y /= whiteY;
+      encoding->red.Z /= whiteY;
+      encoding->green.X /= whiteY;
+      encoding->green.Y /= whiteY;
+      encoding->green.Z /= whiteY;
+      encoding->blue.X /= whiteY;
+      encoding->blue.Y /= whiteY;
+      encoding->blue.Z /= whiteY;
+   }
+}
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static size_t
+safecat_color_encoding(char *buffer, size_t bufsize, size_t pos,
+   const color_encoding *e, double encoding_gamma)
+{
+   if (e != 0)
+   {
+      if (encoding_gamma != 0)
+         pos = safecat(buffer, bufsize, pos, "(");
+      pos = safecat(buffer, bufsize, pos, "R(");
+      pos = safecatd(buffer, bufsize, pos, e->red.X, 4);
+      pos = safecat(buffer, bufsize, pos, ",");
+      pos = safecatd(buffer, bufsize, pos, e->red.Y, 4);
+      pos = safecat(buffer, bufsize, pos, ",");
+      pos = safecatd(buffer, bufsize, pos, e->red.Z, 4);
+      pos = safecat(buffer, bufsize, pos, "),G(");
+      pos = safecatd(buffer, bufsize, pos, e->green.X, 4);
+      pos = safecat(buffer, bufsize, pos, ",");
+      pos = safecatd(buffer, bufsize, pos, e->green.Y, 4);
+      pos = safecat(buffer, bufsize, pos, ",");
+      pos = safecatd(buffer, bufsize, pos, e->green.Z, 4);
+      pos = safecat(buffer, bufsize, pos, "),B(");
+      pos = safecatd(buffer, bufsize, pos, e->blue.X, 4);
+      pos = safecat(buffer, bufsize, pos, ",");
+      pos = safecatd(buffer, bufsize, pos, e->blue.Y, 4);
+      pos = safecat(buffer, bufsize, pos, ",");
+      pos = safecatd(buffer, bufsize, pos, e->blue.Z, 4);
+      pos = safecat(buffer, bufsize, pos, ")");
+      if (encoding_gamma != 0)
+         pos = safecat(buffer, bufsize, pos, ")");
+   }
+
+   if (encoding_gamma != 0)
+   {
+      pos = safecat(buffer, bufsize, pos, "^");
+      pos = safecatd(buffer, bufsize, pos, encoding_gamma, 5);
+   }
+
+   return pos;
+}
+#endif /* READ_TRANSFORMS */
+#endif /* PNG_READ_SUPPORTED */
+
+typedef struct png_modifier
+{
+   png_store               this;             /* I am a png_store */
+   struct png_modification *modifications;   /* Changes to make */
+
+   modifier_state           state;           /* My state */
+
+   /* Information from IHDR: */
+   png_byte                 bit_depth;       /* From IHDR */
+   png_byte                 colour_type;     /* From IHDR */
+
+   /* While handling PLTE, IDAT and IEND these chunks may be pended to allow
+    * other chunks to be inserted.
+    */
+   png_uint_32              pending_len;
+   png_uint_32              pending_chunk;
+
+   /* Test values */
+   double                   *gammas;
+   unsigned int              ngammas;
+   unsigned int              ngamma_tests;     /* Number of gamma tests to run*/
+   double                    current_gamma;    /* 0 if not set */
+   const color_encoding *encodings;
+   unsigned int              nencodings;
+   const color_encoding *current_encoding; /* If an encoding has been set */
+   unsigned int              encoding_counter; /* For iteration */
+   int                       encoding_ignored; /* Something overwrote it */
+
+   /* Control variables used to iterate through possible encodings, the
+    * following must be set to 0 and tested by the function that uses the
+    * png_modifier because the modifier only sets it to 1 (true.)
+    */
+   unsigned int              repeat :1;   /* Repeat this transform test. */
+   unsigned int              test_uses_encoding :1;
+
+   /* Lowest sbit to test (pre-1.7 libpng fails for sbit < 8) */
+   png_byte                 sbitlow;
+
+   /* Error control - these are the limits on errors accepted by the gamma tests
+    * below.
+    */
+   double                   maxout8;  /* Maximum output value error */
+   double                   maxabs8;  /* Absolute sample error 0..1 */
+   double                   maxcalc8; /* Absolute sample error 0..1 */
+   double                   maxpc8;   /* Percentage sample error 0..100% */
+   double                   maxout16; /* Maximum output value error */
+   double                   maxabs16; /* Absolute sample error 0..1 */
+   double                   maxcalc16;/* Absolute sample error 0..1 */
+   double                   maxcalcG; /* Absolute sample error 0..1 */
+   double                   maxpc16;  /* Percentage sample error 0..100% */
+
+   /* This is set by transforms that need to allow a higher limit, it is an
+    * internal check on pngvalid to ensure that the calculated error limits are
+    * not ridiculous; without this it is too easy to make a mistake in pngvalid
+    * that allows any value through.
+    */
+   double                   limit;    /* limit on error values, normally 4E-3 */
+
+   /* Log limits - values above this are logged, but not necessarily
+    * warned.
+    */
+   double                   log8;     /* Absolute error in 8 bits to log */
+   double                   log16;    /* Absolute error in 16 bits to log */
+
+   /* Logged 8 and 16 bit errors ('output' values): */
+   double                   error_gray_2;
+   double                   error_gray_4;
+   double                   error_gray_8;
+   double                   error_gray_16;
+   double                   error_color_8;
+   double                   error_color_16;
+   double                   error_indexed;
+
+   /* Flags: */
+   /* Whether to call png_read_update_info, not png_read_start_image, and how
+    * many times to call it.
+    */
+   int                      use_update_info;
+
+   /* Whether or not to interlace. */
+   int                      interlace_type :9; /* int, but must store '1' */
+
+   /* Run the standard tests? */
+   unsigned int             test_standard :1;
+
+   /* Run the odd-sized image and interlace read/write tests? */
+   unsigned int             test_size :1;
+
+   /* Run tests on reading with a combination of transforms, */
+   unsigned int             test_transform :1;
+   unsigned int             test_tRNS :1; /* Includes tRNS images */
+
+   /* When to use the use_input_precision option, this controls the gamma
+    * validation code checks.  If set any value that is within the transformed
+    * range input-.5 to input+.5 will be accepted, otherwise the value must be
+    * within the normal limits.  It should not be necessary to set this; the
+    * result should always be exact within the permitted error limits.
+    */
+   unsigned int             use_input_precision :1;
+   unsigned int             use_input_precision_sbit :1;
+   unsigned int             use_input_precision_16to8 :1;
+
+   /* If set assume that the calculation bit depth is set by the input
+    * precision, not the output precision.
+    */
+   unsigned int             calculations_use_input_precision :1;
+
+   /* If set assume that the calculations are done in 16 bits even if the sample
+    * depth is 8 bits.
+    */
+   unsigned int             assume_16_bit_calculations :1;
+
+   /* Which gamma tests to run: */
+   unsigned int             test_gamma_threshold :1;
+   unsigned int             test_gamma_transform :1; /* main tests */
+   unsigned int             test_gamma_sbit :1;
+   unsigned int             test_gamma_scale16 :1;
+   unsigned int             test_gamma_background :1;
+   unsigned int             test_gamma_alpha_mode :1;
+   unsigned int             test_gamma_expand16 :1;
+   unsigned int             test_exhaustive :1;
+
+   /* Whether or not to run the low-bit-depth grayscale tests.  This fails on
+    * gamma images in some cases because of gross inaccuracies in the grayscale
+    * gamma handling for low bit depth.
+    */
+   unsigned int             test_lbg :1;
+   unsigned int             test_lbg_gamma_threshold :1;
+   unsigned int             test_lbg_gamma_transform :1;
+   unsigned int             test_lbg_gamma_sbit :1;
+   unsigned int             test_lbg_gamma_composition :1;
+
+   unsigned int             log :1;   /* Log max error */
+
+   /* Buffer information, the buffer size limits the size of the chunks that can
+    * be modified - they must fit (including header and CRC) into the buffer!
+    */
+   size_t                   flush;           /* Count of bytes to flush */
+   size_t                   buffer_count;    /* Bytes in buffer */
+   size_t                   buffer_position; /* Position in buffer */
+   png_byte                 buffer[1024];
+} png_modifier;
+
+/* This returns true if the test should be stopped now because it has already
+ * failed and it is running silently.
+  */
+static int fail(png_modifier *pm)
+{
+   return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 ||
+       (pm->this.treat_warnings_as_errors && pm->this.nwarnings > 0));
+}
+
+static void
+modifier_init(png_modifier *pm)
+{
+   memset(pm, 0, sizeof *pm);
+   store_init(&pm->this);
+   pm->modifications = NULL;
+   pm->state = modifier_start;
+   pm->sbitlow = 1U;
+   pm->ngammas = 0;
+   pm->ngamma_tests = 0;
+   pm->gammas = 0;
+   pm->current_gamma = 0;
+   pm->encodings = 0;
+   pm->nencodings = 0;
+   pm->current_encoding = 0;
+   pm->encoding_counter = 0;
+   pm->encoding_ignored = 0;
+   pm->repeat = 0;
+   pm->test_uses_encoding = 0;
+   pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0;
+   pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0;
+   pm->maxcalcG = 0;
+   pm->limit = 4E-3;
+   pm->log8 = pm->log16 = 0; /* Means 'off' */
+   pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
+   pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
+   pm->error_indexed = 0;
+   pm->use_update_info = 0;
+   pm->interlace_type = PNG_INTERLACE_NONE;
+   pm->test_standard = 0;
+   pm->test_size = 0;
+   pm->test_transform = 0;
+#  ifdef PNG_WRITE_tRNS_SUPPORTED
+      pm->test_tRNS = 1;
+#  else
+      pm->test_tRNS = 0;
+#  endif
+   pm->use_input_precision = 0;
+   pm->use_input_precision_sbit = 0;
+   pm->use_input_precision_16to8 = 0;
+   pm->calculations_use_input_precision = 0;
+   pm->assume_16_bit_calculations = 0;
+   pm->test_gamma_threshold = 0;
+   pm->test_gamma_transform = 0;
+   pm->test_gamma_sbit = 0;
+   pm->test_gamma_scale16 = 0;
+   pm->test_gamma_background = 0;
+   pm->test_gamma_alpha_mode = 0;
+   pm->test_gamma_expand16 = 0;
+   pm->test_lbg = 1;
+   pm->test_lbg_gamma_threshold = 1;
+   pm->test_lbg_gamma_transform = 1;
+   pm->test_lbg_gamma_sbit = 1;
+   pm->test_lbg_gamma_composition = 1;
+   pm->test_exhaustive = 0;
+   pm->log = 0;
+
+   /* Rely on the memset for all the other fields - there are no pointers */
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+
+/* This controls use of checks that explicitly know how libpng digitizes the
+ * samples in calculations; setting this circumvents simple error limit checking
+ * in the rgb_to_gray check, replacing it with an exact copy of the libpng 1.5
+ * algorithm.
+ */
+#define DIGITIZE PNG_LIBPNG_VER < 10700
+
+/* If pm->calculations_use_input_precision is set then operations will happen
+ * with the precision of the input, not the precision of the output depth.
+ *
+ * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16
+ * bit precision.  This only affects those of the following limits that pertain
+ * to a calculation - not a digitization operation - unless the following API is
+ * called directly.
+ */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+#if DIGITIZE
+static double digitize(double value, int depth, int do_round)
+{
+   /* 'value' is in the range 0 to 1, the result is the same value rounded to a
+    * multiple of the digitization factor - 8 or 16 bits depending on both the
+    * sample depth and the 'assume' setting.  Digitization is normally by
+    * rounding and 'do_round' should be 1, if it is 0 the digitized value will
+    * be truncated.
+    */
+   const unsigned int digitization_factor = (1U << depth) -1;
+
+   /* Limiting the range is done as a convenience to the caller - it's easier to
+    * do it once here than every time at the call site.
+    */
+   if (value <= 0)
+      value = 0;
+
+   else if (value >= 1)
+      value = 1;
+
+   value *= digitization_factor;
+   if (do_round) value += .5;
+   return floor(value)/digitization_factor;
+}
+#endif
+#endif /* RGB_TO_GRAY */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+static double abserr(const png_modifier *pm, int in_depth, int out_depth)
+{
+   /* Absolute error permitted in linear values - affected by the bit depth of
+    * the calculations.
+    */
+   if (pm->assume_16_bit_calculations ||
+      (pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+      return pm->maxabs16;
+   else
+      return pm->maxabs8;
+}
+
+static double calcerr(const png_modifier *pm, int in_depth, int out_depth)
+{
+   /* Error in the linear composition arithmetic - only relevant when
+    * composition actually happens (0 < alpha < 1).
+    */
+   if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+      return pm->maxcalc16;
+   else if (pm->assume_16_bit_calculations)
+      return pm->maxcalcG;
+   else
+      return pm->maxcalc8;
+}
+
+static double pcerr(const png_modifier *pm, int in_depth, int out_depth)
+{
+   /* Percentage error permitted in the linear values.  Note that the specified
+    * value is a percentage but this routine returns a simple number.
+    */
+   if (pm->assume_16_bit_calculations ||
+      (pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+      return pm->maxpc16 * .01;
+   else
+      return pm->maxpc8 * .01;
+}
+
+/* Output error - the error in the encoded value.  This is determined by the
+ * digitization of the output so can be +/-0.5 in the actual output value.  In
+ * the expand_16 case with the current code in libpng the expand happens after
+ * all the calculations are done in 8 bit arithmetic, so even though the output
+ * depth is 16 the output error is determined by the 8 bit calculation.
+ *
+ * This limit is not determined by the bit depth of internal calculations.
+ *
+ * The specified parameter does *not* include the base .5 digitization error but
+ * it is added here.
+ */
+static double outerr(const png_modifier *pm, int in_depth, int out_depth)
+{
+   /* There is a serious error in the 2 and 4 bit grayscale transform because
+    * the gamma table value (8 bits) is simply shifted, not rounded, so the
+    * error in 4 bit grayscale gamma is up to the value below.  This is a hack
+    * to allow pngvalid to succeed:
+    *
+    * TODO: fix this in libpng
+    */
+   if (out_depth == 2)
+      return .73182-.5;
+
+   if (out_depth == 4)
+      return .90644-.5;
+
+   if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+      return pm->maxout16;
+
+   /* This is the case where the value was calculated at 8-bit precision then
+    * scaled to 16 bits.
+    */
+   else if (out_depth == 16)
+      return pm->maxout8 * 257;
+
+   else
+      return pm->maxout8;
+}
+
+/* This does the same thing as the above however it returns the value to log,
+ * rather than raising a warning.  This is useful for debugging to track down
+ * exactly what set of parameters cause high error values.
+ */
+static double outlog(const png_modifier *pm, int in_depth, int out_depth)
+{
+   /* The command line parameters are either 8 bit (0..255) or 16 bit (0..65535)
+    * and so must be adjusted for low bit depth grayscale:
+    */
+   if (out_depth <= 8)
+   {
+      if (pm->log8 == 0) /* switched off */
+         return 256;
+
+      if (out_depth < 8)
+         return pm->log8 / 255 * ((1<<out_depth)-1);
+
+      return pm->log8;
+   }
+
+   if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+   {
+      if (pm->log16 == 0)
+         return 65536;
+
+      return pm->log16;
+   }
+
+   /* This is the case where the value was calculated at 8-bit precision then
+    * scaled to 16 bits.
+    */
+   if (pm->log8 == 0)
+      return 65536;
+
+   return pm->log8 * 257;
+}
+
+/* This complements the above by providing the appropriate quantization for the
+ * final value.  Normally this would just be quantization to an integral value,
+ * but in the 8 bit calculation case it's actually quantization to a multiple of
+ * 257!
+ */
+static int output_quantization_factor(const png_modifier *pm, int in_depth,
+   int out_depth)
+{
+   if (out_depth == 16 && in_depth != 16 &&
+      pm->calculations_use_input_precision)
+      return 257;
+   else
+      return 1;
+}
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+
+/* One modification structure must be provided for each chunk to be modified (in
+ * fact more than one can be provided if multiple separate changes are desired
+ * for a single chunk.)  Modifications include adding a new chunk when a
+ * suitable chunk does not exist.
+ *
+ * The caller of modify_fn will reset the CRC of the chunk and record 'modified'
+ * or 'added' as appropriate if the modify_fn returns 1 (true).  If the
+ * modify_fn is NULL the chunk is simply removed.
+ */
+typedef struct png_modification
+{
+   struct png_modification *next;
+   png_uint_32              chunk;
+
+   /* If the following is NULL all matching chunks will be removed: */
+   int                    (*modify_fn)(struct png_modifier *pm,
+                               struct png_modification *me, int add);
+
+   /* If the following is set to PLTE, IDAT or IEND and the chunk has not been
+    * found and modified (and there is a modify_fn) the modify_fn will be called
+    * to add the chunk before the relevant chunk.
+    */
+   png_uint_32              add;
+   unsigned int             modified :1;     /* Chunk was modified */
+   unsigned int             added    :1;     /* Chunk was added */
+   unsigned int             removed  :1;     /* Chunk was removed */
+} png_modification;
+
+static void
+modification_reset(png_modification *pmm)
+{
+   if (pmm != NULL)
+   {
+      pmm->modified = 0;
+      pmm->added = 0;
+      pmm->removed = 0;
+      modification_reset(pmm->next);
+   }
+}
+
+static void
+modification_init(png_modification *pmm)
+{
+   memset(pmm, 0, sizeof *pmm);
+   pmm->next = NULL;
+   pmm->chunk = 0;
+   pmm->modify_fn = NULL;
+   pmm->add = 0;
+   modification_reset(pmm);
+}
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+static void
+modifier_current_encoding(const png_modifier *pm, color_encoding *ce)
+{
+   if (pm->current_encoding != 0)
+      *ce = *pm->current_encoding;
+
+   else
+      memset(ce, 0, sizeof *ce);
+
+   ce->gamma = pm->current_gamma;
+}
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static size_t
+safecat_current_encoding(char *buffer, size_t bufsize, size_t pos,
+   const png_modifier *pm)
+{
+   pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding,
+      pm->current_gamma);
+
+   if (pm->encoding_ignored)
+      pos = safecat(buffer, bufsize, pos, "[overridden]");
+
+   return pos;
+}
+#endif
+
+/* Iterate through the usefully testable color encodings.  An encoding is one
+ * of:
+ *
+ * 1) Nothing (no color space, no gamma).
+ * 2) Just a gamma value from the gamma array (including 1.0)
+ * 3) A color space from the encodings array with the corresponding gamma.
+ * 4) The same, but with gamma 1.0 (only really useful with 16 bit calculations)
+ *
+ * The iterator selects these in turn, the randomizer selects one at random,
+ * which is used depends on the setting of the 'test_exhaustive' flag.  Notice
+ * that this function changes the colour space encoding so it must only be
+ * called on completion of the previous test.  This is what 'modifier_reset'
+ * does, below.
+ *
+ * After the function has been called the 'repeat' flag will still be set; the
+ * caller of modifier_reset must reset it at the start of each run of the test!
+ */
+static unsigned int
+modifier_total_encodings(const png_modifier *pm)
+{
+   return 1 +                 /* (1) nothing */
+      pm->ngammas +           /* (2) gamma values to test */
+      pm->nencodings +        /* (3) total number of encodings */
+      /* The following test only works after the first time through the
+       * png_modifier code because 'bit_depth' is set when the IHDR is read.
+       * modifier_reset, below, preserves the setting until after it has called
+       * the iterate function (also below.)
+       *
+       * For this reason do not rely on this function outside a call to
+       * modifier_reset.
+       */
+      ((pm->bit_depth == 16 || pm->assume_16_bit_calculations) ?
+         pm->nencodings : 0); /* (4) encodings with gamma == 1.0 */
+}
+
+static void
+modifier_encoding_iterate(png_modifier *pm)
+{
+   if (!pm->repeat && /* Else something needs the current encoding again. */
+      pm->test_uses_encoding) /* Some transform is encoding dependent */
+   {
+      if (pm->test_exhaustive)
+      {
+         if (++pm->encoding_counter >= modifier_total_encodings(pm))
+            pm->encoding_counter = 0; /* This will stop the repeat */
+      }
+
+      else
+      {
+         /* Not exhaustive - choose an encoding at random; generate a number in
+          * the range 1..(max-1), so the result is always non-zero:
+          */
+         if (pm->encoding_counter == 0)
+            pm->encoding_counter = random_mod(modifier_total_encodings(pm)-1)+1;
+         else
+            pm->encoding_counter = 0;
+      }
+
+      if (pm->encoding_counter > 0)
+         pm->repeat = 1;
+   }
+
+   else if (!pm->repeat)
+      pm->encoding_counter = 0;
+}
+
+static void
+modifier_reset(png_modifier *pm)
+{
+   store_read_reset(&pm->this);
+   pm->limit = 4E-3;
+   pm->pending_len = pm->pending_chunk = 0;
+   pm->flush = pm->buffer_count = pm->buffer_position = 0;
+   pm->modifications = NULL;
+   pm->state = modifier_start;
+   modifier_encoding_iterate(pm);
+   /* The following must be set in the next run.  In particular
+    * test_uses_encodings must be set in the _ini function of each transform
+    * that looks at the encodings.  (Not the 'add' function!)
+    */
+   pm->test_uses_encoding = 0;
+   pm->current_gamma = 0;
+   pm->current_encoding = 0;
+   pm->encoding_ignored = 0;
+   /* These only become value after IHDR is read: */
+   pm->bit_depth = pm->colour_type = 0;
+}
+
+/* The following must be called before anything else to get the encoding set up
+ * on the modifier.  In particular it must be called before the transform init
+ * functions are called.
+ */
+static void
+modifier_set_encoding(png_modifier *pm)
+{
+   /* Set the encoding to the one specified by the current encoding counter,
+    * first clear out all the settings - this corresponds to an encoding_counter
+    * of 0.
+    */
+   pm->current_gamma = 0;
+   pm->current_encoding = 0;
+   pm->encoding_ignored = 0; /* not ignored yet - happens in _ini functions. */
+
+   /* Now, if required, set the gamma and encoding fields. */
+   if (pm->encoding_counter > 0)
+   {
+      /* The gammas[] array is an array of screen gammas, not encoding gammas,
+       * so we need the inverse:
+       */
+      if (pm->encoding_counter <= pm->ngammas)
+         pm->current_gamma = 1/pm->gammas[pm->encoding_counter-1];
+
+      else
+      {
+         unsigned int i = pm->encoding_counter - pm->ngammas;
+
+         if (i >= pm->nencodings)
+         {
+            i %= pm->nencodings;
+            pm->current_gamma = 1; /* Linear, only in the 16 bit case */
+         }
+
+         else
+            pm->current_gamma = pm->encodings[i].gamma;
+
+         pm->current_encoding = pm->encodings + i;
+      }
+   }
+}
+
+/* Enquiry functions to find out what is set.  Notice that there is an implicit
+ * assumption below that the first encoding in the list is the one for sRGB.
+ */
+static int
+modifier_color_encoding_is_sRGB(const png_modifier *pm)
+{
+   return pm->current_encoding != 0 && pm->current_encoding == pm->encodings &&
+      pm->current_encoding->gamma == pm->current_gamma;
+}
+
+static int
+modifier_color_encoding_is_set(const png_modifier *pm)
+{
+   return pm->current_gamma != 0;
+}
+
+/* Convenience macros. */
+#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+#define CHUNK_IHDR CHUNK(73,72,68,82)
+#define CHUNK_PLTE CHUNK(80,76,84,69)
+#define CHUNK_IDAT CHUNK(73,68,65,84)
+#define CHUNK_IEND CHUNK(73,69,78,68)
+#define CHUNK_cHRM CHUNK(99,72,82,77)
+#define CHUNK_gAMA CHUNK(103,65,77,65)
+#define CHUNK_sBIT CHUNK(115,66,73,84)
+#define CHUNK_sRGB CHUNK(115,82,71,66)
+
+/* The guts of modification are performed during a read. */
+static void
+modifier_crc(png_bytep buffer)
+{
+   /* Recalculate the chunk CRC - a complete chunk must be in
+    * the buffer, at the start.
+    */
+   uInt datalen = png_get_uint_32(buffer);
+   uLong crc = crc32(0, buffer+4, datalen+4);
+   /* The cast to png_uint_32 is safe because a crc32 is always a 32 bit value.
+    */
+   png_save_uint_32(buffer+datalen+8, (png_uint_32)crc);
+}
+
+static void
+modifier_setbuffer(png_modifier *pm)
+{
+   modifier_crc(pm->buffer);
+   pm->buffer_count = png_get_uint_32(pm->buffer)+12;
+   pm->buffer_position = 0;
+}
+
+/* Separate the callback into the actual implementation (which is passed the
+ * png_modifier explicitly) and the callback, which gets the modifier from the
+ * png_struct.
+ */
+static void
+modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
+{
+   while (st > 0)
+   {
+      size_t cb;
+      png_uint_32 len, chunk;
+      png_modification *mod;
+
+      if (pm->buffer_position >= pm->buffer_count) switch (pm->state)
+      {
+         static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+         case modifier_start:
+            store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
+            pm->buffer_count = 8;
+            pm->buffer_position = 0;
+
+            if (memcmp(pm->buffer, sign, 8) != 0)
+               png_error(pm->this.pread, "invalid PNG file signature");
+            pm->state = modifier_signature;
+            break;
+
+         case modifier_signature:
+            store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
+            pm->buffer_count = 13+12;
+            pm->buffer_position = 0;
+
+            if (png_get_uint_32(pm->buffer) != 13 ||
+                png_get_uint_32(pm->buffer+4) != CHUNK_IHDR)
+               png_error(pm->this.pread, "invalid IHDR");
+
+            /* Check the list of modifiers for modifications to the IHDR. */
+            mod = pm->modifications;
+            while (mod != NULL)
+            {
+               if (mod->chunk == CHUNK_IHDR && mod->modify_fn &&
+                   (*mod->modify_fn)(pm, mod, 0))
+                  {
+                  mod->modified = 1;
+                  modifier_setbuffer(pm);
+                  }
+
+               /* Ignore removal or add if IHDR! */
+               mod = mod->next;
+            }
+
+            /* Cache information from the IHDR (the modified one.) */
+            pm->bit_depth = pm->buffer[8+8];
+            pm->colour_type = pm->buffer[8+8+1];
+
+            pm->state = modifier_IHDR;
+            pm->flush = 0;
+            break;
+
+         case modifier_IHDR:
+         default:
+            /* Read a new chunk and process it until we see PLTE, IDAT or
+             * IEND.  'flush' indicates that there is still some data to
+             * output from the preceding chunk.
+             */
+            if ((cb = pm->flush) > 0)
+            {
+               if (cb > st) cb = st;
+               pm->flush -= cb;
+               store_read_imp(&pm->this, pb, cb);
+               pb += cb;
+               st -= cb;
+               if (st == 0) return;
+            }
+
+            /* No more bytes to flush, read a header, or handle a pending
+             * chunk.
+             */
+            if (pm->pending_chunk != 0)
+            {
+               png_save_uint_32(pm->buffer, pm->pending_len);
+               png_save_uint_32(pm->buffer+4, pm->pending_chunk);
+               pm->pending_len = 0;
+               pm->pending_chunk = 0;
+            }
+            else
+               store_read_imp(&pm->this, pm->buffer, 8);
+
+            pm->buffer_count = 8;
+            pm->buffer_position = 0;
+
+            /* Check for something to modify or a terminator chunk. */
+            len = png_get_uint_32(pm->buffer);
+            chunk = png_get_uint_32(pm->buffer+4);
+
+            /* Terminators first, they may have to be delayed for added
+             * chunks
+             */
+            if (chunk == CHUNK_PLTE || chunk == CHUNK_IDAT ||
+                chunk == CHUNK_IEND)
+            {
+               mod = pm->modifications;
+
+               while (mod != NULL)
+               {
+                  if ((mod->add == chunk ||
+                      (mod->add == CHUNK_PLTE && chunk == CHUNK_IDAT)) &&
+                      mod->modify_fn != NULL && !mod->modified && !mod->added)
+                  {
+                     /* Regardless of what the modify function does do not run
+                      * this again.
+                      */
+                     mod->added = 1;
+
+                     if ((*mod->modify_fn)(pm, mod, 1 /*add*/))
+                     {
+                        /* Reset the CRC on a new chunk */
+                        if (pm->buffer_count > 0)
+                           modifier_setbuffer(pm);
+
+                        else
+                           {
+                           pm->buffer_position = 0;
+                           mod->removed = 1;
+                           }
+
+                        /* The buffer has been filled with something (we assume)
+                         * so output this.  Pend the current chunk.
+                         */
+                        pm->pending_len = len;
+                        pm->pending_chunk = chunk;
+                        break; /* out of while */
+                     }
+                  }
+
+                  mod = mod->next;
+               }
+
+               /* Don't do any further processing if the buffer was modified -
+                * otherwise the code will end up modifying a chunk that was
+                * just added.
+                */
+               if (mod != NULL)
+                  break; /* out of switch */
+            }
+
+            /* If we get to here then this chunk may need to be modified.  To
+             * do this it must be less than 1024 bytes in total size, otherwise
+             * it just gets flushed.
+             */
+            if (len+12 <= sizeof pm->buffer)
+            {
+               store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
+                   len+12-pm->buffer_count);
+               pm->buffer_count = len+12;
+
+               /* Check for a modification, else leave it be. */
+               mod = pm->modifications;
+               while (mod != NULL)
+               {
+                  if (mod->chunk == chunk)
+                  {
+                     if (mod->modify_fn == NULL)
+                     {
+                        /* Remove this chunk */
+                        pm->buffer_count = pm->buffer_position = 0;
+                        mod->removed = 1;
+                        break; /* Terminate the while loop */
+                     }
+
+                     else if ((*mod->modify_fn)(pm, mod, 0))
+                     {
+                        mod->modified = 1;
+                        /* The chunk may have been removed: */
+                        if (pm->buffer_count == 0)
+                        {
+                           pm->buffer_position = 0;
+                           break;
+                        }
+                        modifier_setbuffer(pm);
+                     }
+                  }
+
+                  mod = mod->next;
+               }
+            }
+
+            else
+               pm->flush = len+12 - pm->buffer_count; /* data + crc */
+
+            /* Take the data from the buffer (if there is any). */
+            break;
+      }
+
+      /* Here to read from the modifier buffer (not directly from
+       * the store, as in the flush case above.)
+       */
+      cb = pm->buffer_count - pm->buffer_position;
+
+      if (cb > st)
+         cb = st;
+
+      memcpy(pb, pm->buffer + pm->buffer_position, cb);
+      st -= cb;
+      pb += cb;
+      pm->buffer_position += cb;
+   }
+}
+
+/* The callback: */
+static void PNGCBAPI
+modifier_read(png_structp ppIn, png_bytep pb, png_size_t st)
+{
+   png_const_structp pp = ppIn;
+   png_modifier *pm = voidcast(png_modifier*, png_get_io_ptr(pp));
+
+   if (pm == NULL || pm->this.pread != pp)
+      png_error(pp, "bad modifier_read call");
+
+   modifier_read_imp(pm, pb, st);
+}
+
+/* Like store_progressive_read but the data is getting changed as we go so we
+ * need a local buffer.
+ */
+static void
+modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
+{
+   if (pm->this.pread != pp || pm->this.current == NULL ||
+       pm->this.next == NULL)
+      png_error(pp, "store state damaged (progressive)");
+
+   /* This is another Horowitz and Hill random noise generator.  In this case
+    * the aim is to stress the progressive reader with truly horrible variable
+    * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers
+    * is generated.  We could probably just count from 1 to 32767 and get as
+    * good a result.
+    */
+   for (;;)
+   {
+      static png_uint_32 noise = 1;
+      png_size_t cb, cbAvail;
+      png_byte buffer[512];
+
+      /* Generate 15 more bits of stuff: */
+      noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
+      cb = noise & 0x1ff;
+
+      /* Check that this number of bytes are available (in the current buffer.)
+       * (This doesn't quite work - the modifier might delete a chunk; unlikely
+       * but possible, it doesn't happen at present because the modifier only
+       * adds chunks to standard images.)
+       */
+      cbAvail = store_read_buffer_avail(&pm->this);
+      if (pm->buffer_count > pm->buffer_position)
+         cbAvail += pm->buffer_count - pm->buffer_position;
+
+      if (cb > cbAvail)
+      {
+         /* Check for EOF: */
+         if (cbAvail == 0)
+            break;
+
+         cb = cbAvail;
+      }
+
+      modifier_read_imp(pm, buffer, cb);
+      png_process_data(pp, pi, buffer, cb);
+   }
+
+   /* Check the invariants at the end (if this fails it's a problem in this
+    * file!)
+    */
+   if (pm->buffer_count > pm->buffer_position ||
+       pm->this.next != &pm->this.current->data ||
+       pm->this.readpos < pm->this.current->datacount)
+      png_error(pp, "progressive read implementation error");
+}
+
+/* Set up a modifier. */
+static png_structp
+set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
+    const char *name)
+{
+   /* Do this first so that the modifier fields are cleared even if an error
+    * happens allocating the png_struct.  No allocation is done here so no
+    * cleanup is required.
+    */
+   pm->state = modifier_start;
+   pm->bit_depth = 0;
+   pm->colour_type = 255;
+
+   pm->pending_len = 0;
+   pm->pending_chunk = 0;
+   pm->flush = 0;
+   pm->buffer_count = 0;
+   pm->buffer_position = 0;
+
+   return set_store_for_read(&pm->this, ppi, id, name);
+}
+
+
+/******************************** MODIFICATIONS *******************************/
+/* Standard modifications to add chunks.  These do not require the _SUPPORTED
+ * macros because the chunks can be there regardless of whether this specific
+ * libpng supports them.
+ */
+typedef struct gama_modification
+{
+   png_modification this;
+   png_fixed_point  gamma;
+} gama_modification;
+
+static int
+gama_modify(png_modifier *pm, png_modification *me, int add)
+{
+   UNUSED(add)
+   /* This simply dumps the given gamma value into the buffer. */
+   png_save_uint_32(pm->buffer, 4);
+   png_save_uint_32(pm->buffer+4, CHUNK_gAMA);
+   png_save_uint_32(pm->buffer+8, ((gama_modification*)me)->gamma);
+   return 1;
+}
+
+static void
+gama_modification_init(gama_modification *me, png_modifier *pm, double gammad)
+{
+   double g;
+
+   modification_init(&me->this);
+   me->this.chunk = CHUNK_gAMA;
+   me->this.modify_fn = gama_modify;
+   me->this.add = CHUNK_PLTE;
+   g = fix(gammad);
+   me->gamma = (png_fixed_point)g;
+   me->this.next = pm->modifications;
+   pm->modifications = &me->this;
+}
+
+typedef struct chrm_modification
+{
+   png_modification          this;
+   const color_encoding *encoding;
+   png_fixed_point           wx, wy, rx, ry, gx, gy, bx, by;
+} chrm_modification;
+
+static int
+chrm_modify(png_modifier *pm, png_modification *me, int add)
+{
+   UNUSED(add)
+   /* As with gAMA this just adds the required cHRM chunk to the buffer. */
+   png_save_uint_32(pm->buffer   , 32);
+   png_save_uint_32(pm->buffer+ 4, CHUNK_cHRM);
+   png_save_uint_32(pm->buffer+ 8, ((chrm_modification*)me)->wx);
+   png_save_uint_32(pm->buffer+12, ((chrm_modification*)me)->wy);
+   png_save_uint_32(pm->buffer+16, ((chrm_modification*)me)->rx);
+   png_save_uint_32(pm->buffer+20, ((chrm_modification*)me)->ry);
+   png_save_uint_32(pm->buffer+24, ((chrm_modification*)me)->gx);
+   png_save_uint_32(pm->buffer+28, ((chrm_modification*)me)->gy);
+   png_save_uint_32(pm->buffer+32, ((chrm_modification*)me)->bx);
+   png_save_uint_32(pm->buffer+36, ((chrm_modification*)me)->by);
+   return 1;
+}
+
+static void
+chrm_modification_init(chrm_modification *me, png_modifier *pm,
+   const color_encoding *encoding)
+{
+   CIE_color white = white_point(encoding);
+
+   /* Original end points: */
+   me->encoding = encoding;
+
+   /* Chromaticities (in fixed point): */
+   me->wx = fix(chromaticity_x(white));
+   me->wy = fix(chromaticity_y(white));
+
+   me->rx = fix(chromaticity_x(encoding->red));
+   me->ry = fix(chromaticity_y(encoding->red));
+   me->gx = fix(chromaticity_x(encoding->green));
+   me->gy = fix(chromaticity_y(encoding->green));
+   me->bx = fix(chromaticity_x(encoding->blue));
+   me->by = fix(chromaticity_y(encoding->blue));
+
+   modification_init(&me->this);
+   me->this.chunk = CHUNK_cHRM;
+   me->this.modify_fn = chrm_modify;
+   me->this.add = CHUNK_PLTE;
+   me->this.next = pm->modifications;
+   pm->modifications = &me->this;
+}
+
+typedef struct srgb_modification
+{
+   png_modification this;
+   png_byte         intent;
+} srgb_modification;
+
+static int
+srgb_modify(png_modifier *pm, png_modification *me, int add)
+{
+   UNUSED(add)
+   /* As above, ignore add and just make a new chunk */
+   png_save_uint_32(pm->buffer, 1);
+   png_save_uint_32(pm->buffer+4, CHUNK_sRGB);
+   pm->buffer[8] = ((srgb_modification*)me)->intent;
+   return 1;
+}
+
+static void
+srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent)
+{
+   modification_init(&me->this);
+   me->this.chunk = CHUNK_sBIT;
+
+   if (intent <= 3) /* if valid, else *delete* sRGB chunks */
+   {
+      me->this.modify_fn = srgb_modify;
+      me->this.add = CHUNK_PLTE;
+      me->intent = intent;
+   }
+
+   else
+   {
+      me->this.modify_fn = 0;
+      me->this.add = 0;
+      me->intent = 0;
+   }
+
+   me->this.next = pm->modifications;
+   pm->modifications = &me->this;
+}
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+typedef struct sbit_modification
+{
+   png_modification this;
+   png_byte         sbit;
+} sbit_modification;
+
+static int
+sbit_modify(png_modifier *pm, png_modification *me, int add)
+{
+   png_byte sbit = ((sbit_modification*)me)->sbit;
+   if (pm->bit_depth > sbit)
+   {
+      int cb = 0;
+      switch (pm->colour_type)
+      {
+         case 0:
+            cb = 1;
+            break;
+
+         case 2:
+         case 3:
+            cb = 3;
+            break;
+
+         case 4:
+            cb = 2;
+            break;
+
+         case 6:
+            cb = 4;
+            break;
+
+         default:
+            png_error(pm->this.pread,
+               "unexpected colour type in sBIT modification");
+      }
+
+      png_save_uint_32(pm->buffer, cb);
+      png_save_uint_32(pm->buffer+4, CHUNK_sBIT);
+
+      while (cb > 0)
+         (pm->buffer+8)[--cb] = sbit;
+
+      return 1;
+   }
+   else if (!add)
+   {
+      /* Remove the sBIT chunk */
+      pm->buffer_count = pm->buffer_position = 0;
+      return 1;
+   }
+   else
+      return 0; /* do nothing */
+}
+
+static void
+sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit)
+{
+   modification_init(&me->this);
+   me->this.chunk = CHUNK_sBIT;
+   me->this.modify_fn = sbit_modify;
+   me->this.add = CHUNK_PLTE;
+   me->sbit = sbit;
+   me->this.next = pm->modifications;
+   pm->modifications = &me->this;
+}
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/***************************** STANDARD PNG FILES *****************************/
+/* Standard files - write and save standard files. */
+/* There are two basic forms of standard images.  Those which attempt to have
+ * all the possible pixel values (not possible for 16bpp images, but a range of
+ * values are produced) and those which have a range of image sizes.  The former
+ * are used for testing transforms, in particular gamma correction and bit
+ * reduction and increase.  The latter are reserved for testing the behavior of
+ * libpng with respect to 'odd' image sizes - particularly small images where
+ * rows become 1 byte and interlace passes disappear.
+ *
+ * The first, most useful, set are the 'transform' images, the second set of
+ * small images are the 'size' images.
+ *
+ * The transform files are constructed with rows which fit into a 1024 byte row
+ * buffer.  This makes allocation easier below.  Further regardless of the file
+ * format every row has 128 pixels (giving 1024 bytes for 64bpp formats).
+ *
+ * Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed
+ * and with an ID derived from the colour type, bit depth and interlace type
+ * as above (FILEID).  The width (128) and height (variable) are not stored in
+ * the FILEID - instead the fields are set to 0, indicating a transform file.
+ *
+ * The size files ar constructed with rows a maximum of 128 bytes wide, allowing
+ * a maximum width of 16 pixels (for the 64bpp case.)  They also have a maximum
+ * height of 16 rows.  The width and height are stored in the FILEID and, being
+ * non-zero, indicate a size file.
+ *
+ * Because the PNG filter code is typically the largest CPU consumer within
+ * libpng itself there is a tendency to attempt to optimize it.  This results in
+ * special case code which needs to be validated.  To cause this to happen the
+ * 'size' images are made to use each possible filter, in so far as this is
+ * possible for smaller images.
+ *
+ * For palette image (colour type 3) multiple transform images are stored with
+ * the same bit depth to allow testing of more colour combinations -
+ * particularly important for testing the gamma code because libpng uses a
+ * different code path for palette images.  For size images a single palette is
+ * used.
+ */
+
+/* Make a 'standard' palette.  Because there are only 256 entries in a palette
+ * (maximum) this actually makes a random palette in the hope that enough tests
+ * will catch enough errors.  (Note that the same palette isn't produced every
+ * time for the same test - it depends on what previous tests have been run -
+ * but a given set of arguments to pngvalid will always produce the same palette
+ * at the same test!  This is why pseudo-random number generators are useful for
+ * testing.)
+ *
+ * The store must be open for write when this is called, otherwise an internal
+ * error will occur.  This routine contains its own magic number seed, so the
+ * palettes generated don't change if there are intervening errors (changing the
+ * calls to the store_mark seed.)
+ */
+static store_palette_entry *
+make_standard_palette(png_store* ps, int npalette, int do_tRNS)
+{
+   static png_uint_32 palette_seed[2] = { 0x87654321, 9 };
+
+   int i = 0;
+   png_byte values[256][4];
+
+   /* Always put in black and white plus the six primary and secondary colors.
+    */
+   for (; i<8; ++i)
+   {
+      values[i][1] = (png_byte)((i&1) ? 255U : 0U);
+      values[i][2] = (png_byte)((i&2) ? 255U : 0U);
+      values[i][3] = (png_byte)((i&4) ? 255U : 0U);
+   }
+
+   /* Then add 62 grays (one quarter of the remaining 256 slots). */
+   {
+      int j = 0;
+      png_byte random_bytes[4];
+      png_byte need[256];
+
+      need[0] = 0; /*got black*/
+      memset(need+1, 1, (sizeof need)-2); /*need these*/
+      need[255] = 0; /*but not white*/
+
+      while (i<70)
+      {
+         png_byte b;
+
+         if (j==0)
+         {
+            make_four_random_bytes(palette_seed, random_bytes);
+            j = 4;
+         }
+
+         b = random_bytes[--j];
+         if (need[b])
+         {
+            values[i][1] = b;
+            values[i][2] = b;
+            values[i++][3] = b;
+         }
+      }
+   }
+
+   /* Finally add 192 colors at random - don't worry about matches to things we
+    * already have, chance is less than 1/65536.  Don't worry about grays,
+    * chance is the same, so we get a duplicate or extra gray less than 1 time
+    * in 170.
+    */
+   for (; i<256; ++i)
+      make_four_random_bytes(palette_seed, values[i]);
+
+   /* Fill in the alpha values in the first byte.  Just use all possible values
+    * (0..255) in an apparently random order:
+    */
+   {
+      store_palette_entry *palette;
+      png_byte selector[4];
+
+      make_four_random_bytes(palette_seed, selector);
+
+      if (do_tRNS)
+         for (i=0; i<256; ++i)
+            values[i][0] = (png_byte)(i ^ selector[0]);
+
+      else
+         for (i=0; i<256; ++i)
+            values[i][0] = 255; /* no transparency/tRNS chunk */
+
+      /* 'values' contains 256 ARGB values, but we only need 'npalette'.
+       * 'npalette' will always be a power of 2: 2, 4, 16 or 256.  In the low
+       * bit depth cases select colors at random, else it is difficult to have
+       * a set of low bit depth palette test with any chance of a reasonable
+       * range of colors.  Do this by randomly permuting values into the low
+       * 'npalette' entries using an XOR mask generated here.  This also
+       * permutes the npalette == 256 case in a potentially useful way (there is
+       * no relationship between palette index and the color value therein!)
+       */
+      palette = store_write_palette(ps, npalette);
+
+      for (i=0; i<npalette; ++i)
+      {
+         palette[i].alpha = values[i ^ selector[1]][0];
+         palette[i].red   = values[i ^ selector[1]][1];
+         palette[i].green = values[i ^ selector[1]][2];
+         palette[i].blue  = values[i ^ selector[1]][3];
+      }
+
+      return palette;
+   }
+}
+
+/* Initialize a standard palette on a write stream.  The 'do_tRNS' argument
+ * indicates whether or not to also set the tRNS chunk.
+ */
+/* TODO: the png_structp here can probably be 'const' in the future */
+static void
+init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette,
+   int do_tRNS)
+{
+   store_palette_entry *ppal = make_standard_palette(ps, npalette, do_tRNS);
+
+   {
+      int i;
+      png_color palette[256];
+
+      /* Set all entries to detect overread errors. */
+      for (i=0; i<npalette; ++i)
+      {
+         palette[i].red = ppal[i].red;
+         palette[i].green = ppal[i].green;
+         palette[i].blue = ppal[i].blue;
+      }
+
+      /* Just in case fill in the rest with detectable values: */
+      for (; i<256; ++i)
+         palette[i].red = palette[i].green = palette[i].blue = 42;
+
+      png_set_PLTE(pp, pi, palette, npalette);
+   }
+
+   if (do_tRNS)
+   {
+      int i, j;
+      png_byte tRNS[256];
+
+      /* Set all the entries, but skip trailing opaque entries */
+      for (i=j=0; i<npalette; ++i)
+         if ((tRNS[i] = ppal[i].alpha) < 255)
+            j = i+1;
+
+      /* Fill in the remainder with a detectable value: */
+      for (; i<256; ++i)
+         tRNS[i] = 24;
+
+#     ifdef PNG_WRITE_tRNS_SUPPORTED
+         if (j > 0)
+            png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+#     endif
+   }
+}
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+static void
+set_random_tRNS(png_structp pp, png_infop pi, const png_byte colour_type,
+   const int bit_depth)
+{
+   /* To make this useful the tRNS color needs to match at least one pixel.
+    * Random values are fine for gray, including the 16-bit case where we know
+    * that the test image contains all the gray values.  For RGB we need more
+    * method as only 65536 different RGB values are generated.
+    */
+   png_color_16 tRNS;
+   const png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1);
+
+   RANDOMIZE(tRNS);
+
+   if (colour_type & 2/*RGB*/)
+   {
+      if (bit_depth == 8)
+      {
+         tRNS.blue = tRNS.red ^ tRNS.green;
+         tRNS.red &= mask;
+         tRNS.green &= mask;
+         tRNS.blue &= mask;
+      }
+
+      else /* bit_depth == 16 */
+      {
+         tRNS.green = (png_uint_16)(tRNS.red * 257);
+         tRNS.blue = (png_uint_16)(tRNS.green * 17);
+      }
+   }
+
+   else
+      tRNS.gray &= mask;
+
+   png_set_tRNS(pp, pi, NULL, 0, &tRNS);
+}
+#endif
+
+/* The number of passes is related to the interlace type. There was no libpng
+ * API to determine this prior to 1.5, so we need an inquiry function:
+ */
+static int
+npasses_from_interlace_type(png_const_structp pp, int interlace_type)
+{
+   switch (interlace_type)
+   {
+   default:
+      png_error(pp, "invalid interlace type");
+
+   case PNG_INTERLACE_NONE:
+      return 1;
+
+   case PNG_INTERLACE_ADAM7:
+      return PNG_INTERLACE_ADAM7_PASSES;
+   }
+}
+
+static unsigned int
+bit_size(png_const_structp pp, png_byte colour_type, png_byte bit_depth)
+{
+   switch (colour_type)
+   {
+      default: png_error(pp, "invalid color type");
+
+      case 0:  return bit_depth;
+
+      case 2:  return 3*bit_depth;
+
+      case 3:  return bit_depth;
+
+      case 4:  return 2*bit_depth;
+
+      case 6:  return 4*bit_depth;
+   }
+}
+
+#define TRANSFORM_WIDTH  128U
+#define TRANSFORM_ROWMAX (TRANSFORM_WIDTH*8U)
+#define SIZE_ROWMAX (16*8U) /* 16 pixels, max 8 bytes each - 128 bytes */
+#define STANDARD_ROWMAX TRANSFORM_ROWMAX /* The larger of the two */
+#define SIZE_HEIGHTMAX 16 /* Maximum range of size images */
+
+static size_t
+transform_rowsize(png_const_structp pp, png_byte colour_type,
+   png_byte bit_depth)
+{
+   return (TRANSFORM_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
+}
+
+/* transform_width(pp, colour_type, bit_depth) current returns the same number
+ * every time, so just use a macro:
+ */
+#define transform_width(pp, colour_type, bit_depth) TRANSFORM_WIDTH
+
+static png_uint_32
+transform_height(png_const_structp pp, png_byte colour_type, png_byte bit_depth)
+{
+   switch (bit_size(pp, colour_type, bit_depth))
+   {
+      case 1:
+      case 2:
+      case 4:
+         return 1;   /* Total of 128 pixels */
+
+      case 8:
+         return 2;   /* Total of 256 pixels/bytes */
+
+      case 16:
+         return 512; /* Total of 65536 pixels */
+
+      case 24:
+      case 32:
+         return 512; /* 65536 pixels */
+
+      case 48:
+      case 64:
+         return 2048;/* 4 x 65536 pixels. */
+#        define TRANSFORM_HEIGHTMAX 2048
+
+      default:
+         return 0;   /* Error, will be caught later */
+   }
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* The following can only be defined here, now we have the definitions
+ * of the transform image sizes.
+ */
+static png_uint_32
+standard_width(png_const_structp pp, png_uint_32 id)
+{
+   png_uint_32 width = WIDTH_FROM_ID(id);
+   UNUSED(pp)
+
+   if (width == 0)
+      width = transform_width(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+
+   return width;
+}
+
+static png_uint_32
+standard_height(png_const_structp pp, png_uint_32 id)
+{
+   png_uint_32 height = HEIGHT_FROM_ID(id);
+
+   if (height == 0)
+      height = transform_height(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+
+   return height;
+}
+
+static png_uint_32
+standard_rowsize(png_const_structp pp, png_uint_32 id)
+{
+   png_uint_32 width = standard_width(pp, id);
+
+   /* This won't overflow: */
+   width *= bit_size(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+   return (width + 7) / 8;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+static void
+transform_row(png_const_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
+   png_byte colour_type, png_byte bit_depth, png_uint_32 y)
+{
+   png_uint_32 v = y << 7;
+   png_uint_32 i = 0;
+
+   switch (bit_size(pp, colour_type, bit_depth))
+   {
+      case 1:
+         while (i<128/8) buffer[i] = (png_byte)(v & 0xff), v += 17, ++i;
+         return;
+
+      case 2:
+         while (i<128/4) buffer[i] = (png_byte)(v & 0xff), v += 33, ++i;
+         return;
+
+      case 4:
+         while (i<128/2) buffer[i] = (png_byte)(v & 0xff), v += 65, ++i;
+         return;
+
+      case 8:
+         /* 256 bytes total, 128 bytes in each row set as follows: */
+         while (i<128) buffer[i] = (png_byte)(v & 0xff), ++v, ++i;
+         return;
+
+      case 16:
+         /* Generate all 65536 pixel values in order, which includes the 8 bit
+          * GA case as well as the 16 bit G case.
+          */
+         while (i<128)
+         {
+            buffer[2*i] = (png_byte)((v>>8) & 0xff);
+            buffer[2*i+1] = (png_byte)(v & 0xff);
+            ++v;
+            ++i;
+         }
+
+         return;
+
+      case 24:
+         /* 65535 pixels, but rotate the values. */
+         while (i<128)
+         {
+            /* Three bytes per pixel, r, g, b, make b by r^g */
+            buffer[3*i+0] = (png_byte)((v >> 8) & 0xff);
+            buffer[3*i+1] = (png_byte)(v & 0xff);
+            buffer[3*i+2] = (png_byte)(((v >> 8) ^ v) & 0xff);
+            ++v;
+            ++i;
+         }
+
+         return;
+
+      case 32:
+         /* 65535 pixels, r, g, b, a; just replicate */
+         while (i<128)
+         {
+            buffer[4*i+0] = (png_byte)((v >> 8) & 0xff);
+            buffer[4*i+1] = (png_byte)(v & 0xff);
+            buffer[4*i+2] = (png_byte)((v >> 8) & 0xff);
+            buffer[4*i+3] = (png_byte)(v & 0xff);
+            ++v;
+            ++i;
+         }
+
+         return;
+
+      case 48:
+         /* y is maximum 2047, giving 4x65536 pixels, make 'r' increase by 1 at
+          * each pixel, g increase by 257 (0x101) and 'b' by 0x1111:
+          */
+         while (i<128)
+         {
+            png_uint_32 t = v++;
+            buffer[6*i+0] = (png_byte)((t >> 8) & 0xff);
+            buffer[6*i+1] = (png_byte)(t & 0xff);
+            t *= 257;
+            buffer[6*i+2] = (png_byte)((t >> 8) & 0xff);
+            buffer[6*i+3] = (png_byte)(t & 0xff);
+            t *= 17;
+            buffer[6*i+4] = (png_byte)((t >> 8) & 0xff);
+            buffer[6*i+5] = (png_byte)(t & 0xff);
+            ++i;
+         }
+
+         return;
+
+      case 64:
+         /* As above in the 32 bit case. */
+         while (i<128)
+         {
+            png_uint_32 t = v++;
+            buffer[8*i+0] = (png_byte)((t >> 8) & 0xff);
+            buffer[8*i+1] = (png_byte)(t & 0xff);
+            buffer[8*i+4] = (png_byte)((t >> 8) & 0xff);
+            buffer[8*i+5] = (png_byte)(t & 0xff);
+            t *= 257;
+            buffer[8*i+2] = (png_byte)((t >> 8) & 0xff);
+            buffer[8*i+3] = (png_byte)(t & 0xff);
+            buffer[8*i+6] = (png_byte)((t >> 8) & 0xff);
+            buffer[8*i+7] = (png_byte)(t & 0xff);
+            ++i;
+         }
+         return;
+
+      default:
+         break;
+   }
+
+   png_error(pp, "internal error");
+}
+
+/* This is just to do the right cast - could be changed to a function to check
+ * 'bd' but there isn't much point.
+ */
+#define DEPTH(bd) ((png_byte)(1U << (bd)))
+
+/* This is just a helper for compiling on minimal systems with no write
+ * interlacing support.  If there is no write interlacing we can't generate test
+ * cases with interlace:
+ */
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+#  define INTERLACE_LAST PNG_INTERLACE_LAST
+#  define check_interlace_type(type) ((void)(type))
+#  define set_write_interlace_handling(pp,type) png_set_interlace_handling(pp)
+#  define do_own_interlace 0
+#elif PNG_LIBPNG_VER < 10700
+#  define set_write_interlace_handling(pp,type) (1)
+static void
+check_interlace_type(int const interlace_type)
+{
+   /* Prior to 1.7.0 libpng does not support the write of an interlaced image
+    * unless PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the
+    * code here does the pixel interlace itself, so:
+    */
+   if (interlace_type != PNG_INTERLACE_NONE)
+   {
+      /* This is an internal error - --interlace tests should be skipped, not
+       * attempted.
+       */
+      fprintf(stderr, "pngvalid: no interlace support\n");
+      exit(99);
+   }
+}
+#  define INTERLACE_LAST (PNG_INTERLACE_NONE+1)
+#  define do_own_interlace 0
+#else /* libpng 1.7+ */
+#  define set_write_interlace_handling(pp,type)\
+      npasses_from_interlace_type(pp,type)
+#  define check_interlace_type(type) ((void)(type))
+#  define INTERLACE_LAST PNG_INTERLACE_LAST
+#  define do_own_interlace 1
+#endif /* WRITE_INTERLACING tests */
+
+#define CAN_WRITE_INTERLACE\
+   PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED
+
+/* Do the same thing for read interlacing; this controls whether read tests do
+ * their own de-interlace or use libpng.
+ */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+#  define do_read_interlace 0
+#else /* no libpng read interlace support */
+#  define do_read_interlace 1
+#endif
+/* The following two routines use the PNG interlace support macros from
+ * png.h to interlace or deinterlace rows.
+ */
+static void
+interlace_row(png_bytep buffer, png_const_bytep imageRow,
+   unsigned int pixel_size, png_uint_32 w, int pass, int littleendian)
+{
+   png_uint_32 xin, xout, xstep;
+
+   /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
+    * code is presented this way to make it easier to understand.  In practice
+    * consult the code in the libpng source to see other ways of doing this.
+    *
+    * It is OK for buffer and imageRow to be identical, because 'xin' moves
+    * faster than 'xout' and we copy up.
+    */
+   xin = PNG_PASS_START_COL(pass);
+   xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+   for (xout=0; xin<w; xin+=xstep)
+   {
+      pixel_copy(buffer, xout, imageRow, xin, pixel_size, littleendian);
+      ++xout;
+   }
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+deinterlace_row(png_bytep buffer, png_const_bytep row,
+   unsigned int pixel_size, png_uint_32 w, int pass, int littleendian)
+{
+   /* The inverse of the above, 'row' is part of row 'y' of the output image,
+    * in 'buffer'.  The image is 'w' wide and this is pass 'pass', distribute
+    * the pixels of row into buffer and return the number written (to allow
+    * this to be checked).
+    */
+   png_uint_32 xin, xout, xstep;
+
+   xout = PNG_PASS_START_COL(pass);
+   xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+   for (xin=0; xout<w; xout+=xstep)
+   {
+      pixel_copy(buffer, xout, row, xin, pixel_size, littleendian);
+      ++xin;
+   }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Make a standardized image given an image colour type, bit depth and
+ * interlace type.  The standard images have a very restricted range of
+ * rows and heights and are used for testing transforms rather than image
+ * layout details.  See make_size_images below for a way to make images
+ * that test odd sizes along with the libpng interlace handling.
+ */
+static void
+make_transform_image(png_store* const ps, png_byte const colour_type,
+    png_byte const bit_depth, unsigned int palette_number,
+    int interlace_type, png_const_charp name)
+{
+   context(ps, fault);
+
+   check_interlace_type(interlace_type);
+
+   Try
+   {
+      png_infop pi;
+      png_structp pp = set_store_for_write(ps, &pi, name);
+      png_uint_32 h, w;
+
+      /* In the event of a problem return control to the Catch statement below
+       * to do the clean up - it is not possible to 'return' directly from a Try
+       * block.
+       */
+      if (pp == NULL)
+         Throw ps;
+
+      w = transform_width(pp, colour_type, bit_depth);
+      h = transform_height(pp, colour_type, bit_depth);
+
+      png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#ifdef PNG_TEXT_SUPPORTED
+#  if defined(PNG_READ_zTXt_SUPPORTED) && defined(PNG_WRITE_zTXt_SUPPORTED)
+#     define TEXT_COMPRESSION PNG_TEXT_COMPRESSION_zTXt
+#  else
+#     define TEXT_COMPRESSION PNG_TEXT_COMPRESSION_NONE
+#  endif
+      {
+         static char key[] = "image name"; /* must be writeable */
+         size_t pos;
+         png_text text;
+         char copy[FILE_NAME_SIZE];
+
+         /* Use a compressed text string to test the correct interaction of text
+          * compression and IDAT compression.
+          */
+         text.compression = TEXT_COMPRESSION;
+         text.key = key;
+         /* Yuck: the text must be writable! */
+         pos = safecat(copy, sizeof copy, 0, ps->wname);
+         text.text = copy;
+         text.text_length = pos;
+         text.itxt_length = 0;
+         text.lang = 0;
+         text.lang_key = 0;
+
+         png_set_text(pp, pi, &text, 1);
+      }
+#endif
+
+      if (colour_type == 3) /* palette */
+         init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/);
+
+#     ifdef PNG_WRITE_tRNS_SUPPORTED
+         else if (palette_number)
+            set_random_tRNS(pp, pi, colour_type, bit_depth);
+#     endif
+
+      png_write_info(pp, pi);
+
+      if (png_get_rowbytes(pp, pi) !=
+          transform_rowsize(pp, colour_type, bit_depth))
+         png_error(pp, "transform row size incorrect");
+
+      else
+      {
+         /* Somewhat confusingly this must be called *after* png_write_info
+          * because if it is called before, the information in *pp has not been
+          * updated to reflect the interlaced image.
+          */
+         int npasses = set_write_interlace_handling(pp, interlace_type);
+         int pass;
+
+         if (npasses != npasses_from_interlace_type(pp, interlace_type))
+            png_error(pp, "write: png_set_interlace_handling failed");
+
+         for (pass=0; pass<npasses; ++pass)
+         {
+            png_uint_32 y;
+
+            /* do_own_interlace is a pre-defined boolean (a #define) which is
+             * set if we have to work out the interlaced rows here.
+             */
+            for (y=0; y<h; ++y)
+            {
+               png_byte buffer[TRANSFORM_ROWMAX];
+
+               transform_row(pp, buffer, colour_type, bit_depth, y);
+
+#              if do_own_interlace
+                  /* If do_own_interlace *and* the image is interlaced we need a
+                   * reduced interlace row; this may be reduced to empty.
+                   */
+                  if (interlace_type == PNG_INTERLACE_ADAM7)
+                  {
+                     /* The row must not be written if it doesn't exist, notice
+                      * that there are two conditions here, either the row isn't
+                      * ever in the pass or the row would be but isn't wide
+                      * enough to contribute any pixels.  In fact the wPass test
+                      * can be used to skip the whole y loop in this case.
+                      */
+                     if (PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+                         PNG_PASS_COLS(w, pass) > 0)
+                        interlace_row(buffer, buffer,
+                              bit_size(pp, colour_type, bit_depth), w, pass,
+                              0/*data always bigendian*/);
+                     else
+                        continue;
+                  }
+#              endif /* do_own_interlace */
+
+               png_write_row(pp, buffer);
+            }
+         }
+      }
+
+#ifdef PNG_TEXT_SUPPORTED
+      {
+         static char key[] = "end marker";
+         static char comment[] = "end";
+         png_text text;
+
+         /* Use a compressed text string to test the correct interaction of text
+          * compression and IDAT compression.
+          */
+         text.compression = TEXT_COMPRESSION;
+         text.key = key;
+         text.text = comment;
+         text.text_length = (sizeof comment)-1;
+         text.itxt_length = 0;
+         text.lang = 0;
+         text.lang_key = 0;
+
+         png_set_text(pp, pi, &text, 1);
+      }
+#endif
+
+      png_write_end(pp, pi);
+
+      /* And store this under the appropriate id, then clean up. */
+      store_storefile(ps, FILEID(colour_type, bit_depth, palette_number,
+         interlace_type, 0, 0, 0));
+
+      store_write_reset(ps);
+   }
+
+   Catch(fault)
+   {
+      /* Use the png_store returned by the exception. This may help the compiler
+       * because 'ps' is not used in this branch of the setjmp.  Note that fault
+       * and ps will always be the same value.
+       */
+      store_write_reset(fault);
+   }
+}
+
+static void
+make_transform_images(png_modifier *pm)
+{
+   png_byte colour_type = 0;
+   png_byte bit_depth = 0;
+   unsigned int palette_number = 0;
+
+   /* This is in case of errors. */
+   safecat(pm->this.test, sizeof pm->this.test, 0, "make standard images");
+
+   /* Use next_format to enumerate all the combinations we test, including
+    * generating multiple low bit depth palette images. Non-A images (palette
+    * and direct) are created with and without tRNS chunks.
+    */
+   while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1))
+   {
+      int interlace_type;
+
+      for (interlace_type = PNG_INTERLACE_NONE;
+           interlace_type < INTERLACE_LAST; ++interlace_type)
+      {
+         char name[FILE_NAME_SIZE];
+
+         standard_name(name, sizeof name, 0, colour_type, bit_depth,
+            palette_number, interlace_type, 0, 0, do_own_interlace);
+         make_transform_image(&pm->this, colour_type, bit_depth, palette_number,
+            interlace_type, name);
+      }
+   }
+}
+
+/* Build a single row for the 'size' test images; this fills in only the
+ * first bit_width bits of the sample row.
+ */
+static void
+size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
+{
+   /* height is in the range 1 to 16, so: */
+   y = ((y & 1) << 7) + ((y & 2) << 6) + ((y & 4) << 5) + ((y & 8) << 4);
+   /* the following ensures bits are set in small images: */
+   y ^= 0xA5;
+
+   while (bit_width >= 8)
+      *buffer++ = (png_byte)y++, bit_width -= 8;
+
+   /* There may be up to 7 remaining bits, these go in the most significant
+    * bits of the byte.
+    */
+   if (bit_width > 0)
+   {
+      png_uint_32 mask = (1U<<(8-bit_width))-1;
+      *buffer = (png_byte)((*buffer & mask) | (y & ~mask));
+   }
+}
+
+static void
+make_size_image(png_store* const ps, png_byte const colour_type,
+    png_byte const bit_depth, int const interlace_type,
+    png_uint_32 const w, png_uint_32 const h,
+    int const do_interlace)
+{
+   context(ps, fault);
+
+   check_interlace_type(interlace_type);
+
+   Try
+   {
+      png_infop pi;
+      png_structp pp;
+      unsigned int pixel_size;
+
+      /* Make a name and get an appropriate id for the store: */
+      char name[FILE_NAME_SIZE];
+      const png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
+         interlace_type, w, h, do_interlace);
+
+      standard_name_from_id(name, sizeof name, 0, id);
+      pp = set_store_for_write(ps, &pi, name);
+
+      /* In the event of a problem return control to the Catch statement below
+       * to do the clean up - it is not possible to 'return' directly from a Try
+       * block.
+       */
+      if (pp == NULL)
+         Throw ps;
+
+      png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#ifdef PNG_TEXT_SUPPORTED
+      {
+         static char key[] = "image name"; /* must be writeable */
+         size_t pos;
+         png_text text;
+         char copy[FILE_NAME_SIZE];
+
+         /* Use a compressed text string to test the correct interaction of text
+          * compression and IDAT compression.
+          */
+         text.compression = TEXT_COMPRESSION;
+         text.key = key;
+         /* Yuck: the text must be writable! */
+         pos = safecat(copy, sizeof copy, 0, ps->wname);
+         text.text = copy;
+         text.text_length = pos;
+         text.itxt_length = 0;
+         text.lang = 0;
+         text.lang_key = 0;
+
+         png_set_text(pp, pi, &text, 1);
+      }
+#endif
+
+      if (colour_type == 3) /* palette */
+         init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
+
+      png_write_info(pp, pi);
+
+      /* Calculate the bit size, divide by 8 to get the byte size - this won't
+       * overflow because we know the w values are all small enough even for
+       * a system where 'unsigned int' is only 16 bits.
+       */
+      pixel_size = bit_size(pp, colour_type, bit_depth);
+      if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
+         png_error(pp, "size row size incorrect");
+
+      else
+      {
+         int npasses = npasses_from_interlace_type(pp, interlace_type);
+         png_uint_32 y;
+         int pass;
+#        ifdef PNG_WRITE_FILTER_SUPPORTED
+            int nfilter = PNG_FILTER_VALUE_LAST;
+#        endif
+         png_byte image[16][SIZE_ROWMAX];
+
+         /* To help consistent error detection make the parts of this buffer
+          * that aren't set below all '1':
+          */
+         memset(image, 0xff, sizeof image);
+
+         if (!do_interlace &&
+             npasses != set_write_interlace_handling(pp, interlace_type))
+            png_error(pp, "write: png_set_interlace_handling failed");
+
+         /* Prepare the whole image first to avoid making it 7 times: */
+         for (y=0; y<h; ++y)
+            size_row(image[y], w * pixel_size, y);
+
+         for (pass=0; pass<npasses; ++pass)
+         {
+            /* The following two are for checking the macros: */
+            const png_uint_32 wPass = PNG_PASS_COLS(w, pass);
+
+            /* If do_interlace is set we don't call png_write_row for every
+             * row because some of them are empty.  In fact, for a 1x1 image,
+             * most of them are empty!
+             */
+            for (y=0; y<h; ++y)
+            {
+               png_const_bytep row = image[y];
+               png_byte tempRow[SIZE_ROWMAX];
+
+               /* If do_interlace *and* the image is interlaced we
+                * need a reduced interlace row; this may be reduced
+                * to empty.
+                */
+               if (do_interlace && interlace_type == PNG_INTERLACE_ADAM7)
+               {
+                  /* The row must not be written if it doesn't exist, notice
+                   * that there are two conditions here, either the row isn't
+                   * ever in the pass or the row would be but isn't wide
+                   * enough to contribute any pixels.  In fact the wPass test
+                   * can be used to skip the whole y loop in this case.
+                   */
+                  if (PNG_ROW_IN_INTERLACE_PASS(y, pass) && wPass > 0)
+                  {
+                     /* Set to all 1's for error detection (libpng tends to
+                      * set unset things to 0).
+                      */
+                     memset(tempRow, 0xff, sizeof tempRow);
+                     interlace_row(tempRow, row, pixel_size, w, pass,
+                           0/*data always bigendian*/);
+                     row = tempRow;
+                  }
+                  else
+                     continue;
+               }
+
+#           ifdef PNG_WRITE_FILTER_SUPPORTED
+               /* Only get to here if the row has some pixels in it, set the
+                * filters to 'all' for the very first row and thereafter to a
+                * single filter.  It isn't well documented, but png_set_filter
+                * does accept a filter number (per the spec) as well as a bit
+                * mask.
+                *
+                * The apparent wackiness of decrementing nfilter rather than
+                * incrementing is so that Paeth gets used in all images bigger
+                * than 1 row - it's the tricky one.
+                */
+               png_set_filter(pp, 0/*method*/,
+                  nfilter >= PNG_FILTER_VALUE_LAST ? PNG_ALL_FILTERS : nfilter);
+
+               if (nfilter-- == 0)
+                  nfilter = PNG_FILTER_VALUE_LAST-1;
+#           endif
+
+               png_write_row(pp, row);
+            }
+         }
+      }
+
+#ifdef PNG_TEXT_SUPPORTED
+      {
+         static char key[] = "end marker";
+         static char comment[] = "end";
+         png_text text;
+
+         /* Use a compressed text string to test the correct interaction of text
+          * compression and IDAT compression.
+          */
+         text.compression = TEXT_COMPRESSION;
+         text.key = key;
+         text.text = comment;
+         text.text_length = (sizeof comment)-1;
+         text.itxt_length = 0;
+         text.lang = 0;
+         text.lang_key = 0;
+
+         png_set_text(pp, pi, &text, 1);
+      }
+#endif
+
+      png_write_end(pp, pi);
+
+      /* And store this under the appropriate id, then clean up. */
+      store_storefile(ps, id);
+
+      store_write_reset(ps);
+   }
+
+   Catch(fault)
+   {
+      /* Use the png_store returned by the exception. This may help the compiler
+       * because 'ps' is not used in this branch of the setjmp.  Note that fault
+       * and ps will always be the same value.
+       */
+      store_write_reset(fault);
+   }
+}
+
+static void
+make_size(png_store* const ps, png_byte const colour_type, int bdlo,
+    int const bdhi)
+{
+   for (; bdlo <= bdhi; ++bdlo)
+   {
+      png_uint_32 width;
+
+      for (width = 1; width <= 16; ++width)
+      {
+         png_uint_32 height;
+
+         for (height = 1; height <= 16; ++height)
+         {
+            /* The four combinations of DIY interlace and interlace or not -
+             * no interlace + DIY should be identical to no interlace with
+             * libpng doing it.
+             */
+            make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
+               width, height, 0);
+            make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
+               width, height, 1);
+#        ifdef PNG_WRITE_INTERLACING_SUPPORTED
+            make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+               width, height, 0);
+#        endif
+#        if CAN_WRITE_INTERLACE
+            /* 1.7.0 removes the hack that prevented app write of an interlaced
+             * image if WRITE_INTERLACE was not supported
+             */
+            make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+               width, height, 1);
+#        endif
+         }
+      }
+   }
+}
+
+static void
+make_size_images(png_store *ps)
+{
+   /* This is in case of errors. */
+   safecat(ps->test, sizeof ps->test, 0, "make size images");
+
+   /* Arguments are colour_type, low bit depth, high bit depth
+    */
+   make_size(ps, 0, 0, WRITE_BDHI);
+   make_size(ps, 2, 3, WRITE_BDHI);
+   make_size(ps, 3, 0, 3 /*palette: max 8 bits*/);
+   make_size(ps, 4, 3, WRITE_BDHI);
+   make_size(ps, 6, 3, WRITE_BDHI);
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Return a row based on image id and 'y' for checking: */
+static void
+standard_row(png_const_structp pp, png_byte std[STANDARD_ROWMAX],
+   png_uint_32 id, png_uint_32 y)
+{
+   if (WIDTH_FROM_ID(id) == 0)
+      transform_row(pp, std, COL_FROM_ID(id), DEPTH_FROM_ID(id), y);
+   else
+      size_row(std, WIDTH_FROM_ID(id) * bit_size(pp, COL_FROM_ID(id),
+         DEPTH_FROM_ID(id)), y);
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Tests - individual test cases */
+/* Like 'make_standard' but errors are deliberately introduced into the calls
+ * to ensure that they get detected - it should not be possible to write an
+ * invalid image with libpng!
+ */
+/* TODO: the 'set' functions can probably all be made to take a
+ * png_const_structp rather than a modifiable one.
+ */
+#ifdef PNG_WARNINGS_SUPPORTED
+static void
+sBIT0_error_fn(png_structp pp, png_infop pi)
+{
+   /* 0 is invalid... */
+   png_color_8 bad;
+   bad.red = bad.green = bad.blue = bad.gray = bad.alpha = 0;
+   png_set_sBIT(pp, pi, &bad);
+}
+
+static void
+sBIT_error_fn(png_structp pp, png_infop pi)
+{
+   png_byte bit_depth;
+   png_color_8 bad;
+
+   if (png_get_color_type(pp, pi) == PNG_COLOR_TYPE_PALETTE)
+      bit_depth = 8;
+
+   else
+      bit_depth = png_get_bit_depth(pp, pi);
+
+   /* Now we know the bit depth we can easily generate an invalid sBIT entry */
+   bad.red = bad.green = bad.blue = bad.gray = bad.alpha =
+      (png_byte)(bit_depth+1);
+   png_set_sBIT(pp, pi, &bad);
+}
+
+static const struct
+{
+   void          (*fn)(png_structp, png_infop);
+   const char *msg;
+   unsigned int    warning :1; /* the error is a warning... */
+} error_test[] =
+    {
+       /* no warnings makes these errors undetectable prior to 1.7.0 */
+       { sBIT0_error_fn, "sBIT(0): failed to detect error",
+         PNG_LIBPNG_VER < 10700 },
+
+       { sBIT_error_fn, "sBIT(too big): failed to detect error",
+         PNG_LIBPNG_VER < 10700 },
+    };
+
+static void
+make_error(png_store* const ps, png_byte const colour_type,
+    png_byte bit_depth, int interlace_type, int test, png_const_charp name)
+{
+   context(ps, fault);
+
+   check_interlace_type(interlace_type);
+
+   Try
+   {
+      png_infop pi;
+      const png_structp pp = set_store_for_write(ps, &pi, name);
+      png_uint_32 w, h;
+      gnu_volatile(pp)
+
+      if (pp == NULL)
+         Throw ps;
+
+      w = transform_width(pp, colour_type, bit_depth);
+      gnu_volatile(w)
+      h = transform_height(pp, colour_type, bit_depth);
+      gnu_volatile(h)
+      png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+      if (colour_type == 3) /* palette */
+         init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
+
+      /* Time for a few errors; these are in various optional chunks, the
+       * standard tests test the standard chunks pretty well.
+       */
+#     define exception__prev exception_prev_1
+#     define exception__env exception_env_1
+      Try
+      {
+         gnu_volatile(exception__prev)
+
+         /* Expect this to throw: */
+         ps->expect_error = !error_test[test].warning;
+         ps->expect_warning = error_test[test].warning;
+         ps->saw_warning = 0;
+         error_test[test].fn(pp, pi);
+
+         /* Normally the error is only detected here: */
+         png_write_info(pp, pi);
+
+         /* And handle the case where it was only a warning: */
+         if (ps->expect_warning && ps->saw_warning)
+            Throw ps;
+
+         /* If we get here there is a problem, we have success - no error or
+          * no warning - when we shouldn't have success.  Log an error.
+          */
+         store_log(ps, pp, error_test[test].msg, 1 /*error*/);
+      }
+
+      Catch (fault)
+      { /* expected exit */
+      }
+#undef exception__prev
+#undef exception__env
+
+      /* And clear these flags */
+      ps->expect_error = 0;
+      ps->expect_warning = 0;
+
+      /* Now write the whole image, just to make sure that the detected, or
+       * undetected, errro has not created problems inside libpng.
+       */
+      if (png_get_rowbytes(pp, pi) !=
+          transform_rowsize(pp, colour_type, bit_depth))
+         png_error(pp, "row size incorrect");
+
+      else
+      {
+         int npasses = set_write_interlace_handling(pp, interlace_type);
+         int pass;
+
+         if (npasses != npasses_from_interlace_type(pp, interlace_type))
+            png_error(pp, "write: png_set_interlace_handling failed");
+
+         for (pass=0; pass<npasses; ++pass)
+         {
+            png_uint_32 y;
+
+            for (y=0; y<h; ++y)
+            {
+               png_byte buffer[TRANSFORM_ROWMAX];
+
+               transform_row(pp, buffer, colour_type, bit_depth, y);
+
+#              if do_own_interlace
+                  /* If do_own_interlace *and* the image is interlaced we need a
+                   * reduced interlace row; this may be reduced to empty.
+                   */
+                  if (interlace_type == PNG_INTERLACE_ADAM7)
+                  {
+                     /* The row must not be written if it doesn't exist, notice
+                      * that there are two conditions here, either the row isn't
+                      * ever in the pass or the row would be but isn't wide
+                      * enough to contribute any pixels.  In fact the wPass test
+                      * can be used to skip the whole y loop in this case.
+                      */
+                     if (PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+                         PNG_PASS_COLS(w, pass) > 0)
+                        interlace_row(buffer, buffer,
+                              bit_size(pp, colour_type, bit_depth), w, pass,
+                              0/*data always bigendian*/);
+                     else
+                        continue;
+                  }
+#              endif /* do_own_interlace */
+
+               png_write_row(pp, buffer);
+            }
+         }
+      }
+
+      png_write_end(pp, pi);
+
+      /* The following deletes the file that was just written. */
+      store_write_reset(ps);
+   }
+
+   Catch(fault)
+   {
+      store_write_reset(fault);
+   }
+}
+
+static int
+make_errors(png_modifier* const pm, png_byte const colour_type,
+    int bdlo, int const bdhi)
+{
+   for (; bdlo <= bdhi; ++bdlo)
+   {
+      int interlace_type;
+
+      for (interlace_type = PNG_INTERLACE_NONE;
+           interlace_type < INTERLACE_LAST; ++interlace_type)
+      {
+         unsigned int test;
+         char name[FILE_NAME_SIZE];
+
+         standard_name(name, sizeof name, 0, colour_type, 1<<bdlo, 0,
+            interlace_type, 0, 0, do_own_interlace);
+
+         for (test=0; test<ARRAY_SIZE(error_test); ++test)
+         {
+            make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
+               test, name);
+
+            if (fail(pm))
+               return 0;
+         }
+      }
+   }
+
+   return 1; /* keep going */
+}
+#endif /* PNG_WARNINGS_SUPPORTED */
+
+static void
+perform_error_test(png_modifier *pm)
+{
+#ifdef PNG_WARNINGS_SUPPORTED /* else there are no cases that work! */
+   /* Need to do this here because we just write in this test. */
+   safecat(pm->this.test, sizeof pm->this.test, 0, "error test");
+
+   if (!make_errors(pm, 0, 0, WRITE_BDHI))
+      return;
+
+   if (!make_errors(pm, 2, 3, WRITE_BDHI))
+      return;
+
+   if (!make_errors(pm, 3, 0, 3))
+      return;
+
+   if (!make_errors(pm, 4, 3, WRITE_BDHI))
+      return;
+
+   if (!make_errors(pm, 6, 3, WRITE_BDHI))
+      return;
+#else
+   UNUSED(pm)
+#endif
+}
+
+/* This is just to validate the internal PNG formatting code - if this fails
+ * then the warning messages the library outputs will probably be garbage.
+ */
+static void
+perform_formatting_test(png_store *ps)
+{
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+   /* The handle into the formatting code is the RFC1123 support; this test does
+    * nothing if that is compiled out.
+    */
+   context(ps, fault);
+
+   Try
+   {
+      png_const_charp correct = "29 Aug 2079 13:53:60 +0000";
+      png_const_charp result;
+#     if PNG_LIBPNG_VER >= 10600
+         char timestring[29];
+#     endif
+      png_structp pp;
+      png_time pt;
+
+      pp = set_store_for_write(ps, NULL, "libpng formatting test");
+
+      if (pp == NULL)
+         Throw ps;
+
+
+      /* Arbitrary settings: */
+      pt.year = 2079;
+      pt.month = 8;
+      pt.day = 29;
+      pt.hour = 13;
+      pt.minute = 53;
+      pt.second = 60; /* a leap second */
+
+#     if PNG_LIBPNG_VER < 10600
+         result = png_convert_to_rfc1123(pp, &pt);
+#     else
+         if (png_convert_to_rfc1123_buffer(timestring, &pt))
+            result = timestring;
+
+         else
+            result = NULL;
+#     endif
+
+      if (result == NULL)
+         png_error(pp, "png_convert_to_rfc1123 failed");
+
+      if (strcmp(result, correct) != 0)
+      {
+         size_t pos = 0;
+         char msg[128];
+
+         pos = safecat(msg, sizeof msg, pos, "png_convert_to_rfc1123(");
+         pos = safecat(msg, sizeof msg, pos, correct);
+         pos = safecat(msg, sizeof msg, pos, ") returned: '");
+         pos = safecat(msg, sizeof msg, pos, result);
+         pos = safecat(msg, sizeof msg, pos, "'");
+
+         png_error(pp, msg);
+      }
+
+      store_write_reset(ps);
+   }
+
+   Catch(fault)
+   {
+      store_write_reset(fault);
+   }
+#else
+   UNUSED(ps)
+#endif
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Because we want to use the same code in both the progressive reader and the
+ * sequential reader it is necessary to deal with the fact that the progressive
+ * reader callbacks only have one parameter (png_get_progressive_ptr()), so this
+ * must contain all the test parameters and all the local variables directly
+ * accessible to the sequential reader implementation.
+ *
+ * The technique adopted is to reinvent part of what Dijkstra termed a
+ * 'display'; an array of pointers to the stack frames of enclosing functions so
+ * that a nested function definition can access the local (C auto) variables of
+ * the functions that contain its definition.  In fact C provides the first
+ * pointer (the local variables - the stack frame pointer) and the last (the
+ * global variables - the BCPL global vector typically implemented as global
+ * addresses), this code requires one more pointer to make the display - the
+ * local variables (and function call parameters) of the function that actually
+ * invokes either the progressive or sequential reader.
+ *
+ * Perhaps confusingly this technique is confounded with classes - the
+ * 'standard_display' defined here is sub-classed as the 'gamma_display' below.
+ * A gamma_display is a standard_display, taking advantage of the ANSI-C
+ * requirement that the pointer to the first member of a structure must be the
+ * same as the pointer to the structure.  This allows us to reuse standard_
+ * functions in the gamma test code; something that could not be done with
+ * nested functions!
+ */
+typedef struct standard_display
+{
+   png_store*  ps;             /* Test parameters (passed to the function) */
+   png_byte    colour_type;
+   png_byte    bit_depth;
+   png_byte    red_sBIT;       /* Input data sBIT values. */
+   png_byte    green_sBIT;
+   png_byte    blue_sBIT;
+   png_byte    alpha_sBIT;
+   png_byte    interlace_type;
+   png_byte    filler;         /* Output has a filler */
+   png_uint_32 id;             /* Calculated file ID */
+   png_uint_32 w;              /* Width of image */
+   png_uint_32 h;              /* Height of image */
+   int         npasses;        /* Number of interlaced passes */
+   png_uint_32 pixel_size;     /* Width of one pixel in bits */
+   png_uint_32 bit_width;      /* Width of output row in bits */
+   size_t      cbRow;          /* Bytes in a row of the output image */
+   int         do_interlace;   /* Do interlacing internally */
+   int         littleendian;   /* App (row) data is little endian */
+   int         is_transparent; /* Transparency information was present. */
+   int         has_tRNS;       /* color type GRAY or RGB with a tRNS chunk. */
+   int         speed;          /* Doing a speed test */
+   int         use_update_info;/* Call update_info, not start_image */
+   struct
+   {
+      png_uint_16 red;
+      png_uint_16 green;
+      png_uint_16 blue;
+   }           transparent;    /* The transparent color, if set. */
+   int         npalette;       /* Number of entries in the palette. */
+   store_palette
+               palette;
+} standard_display;
+
+static void
+standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
+   int do_interlace, int use_update_info)
+{
+   memset(dp, 0, sizeof *dp);
+
+   dp->ps = ps;
+   dp->colour_type = COL_FROM_ID(id);
+   dp->bit_depth = DEPTH_FROM_ID(id);
+   if (dp->bit_depth < 1 || dp->bit_depth > 16)
+      internal_error(ps, "internal: bad bit depth");
+   if (dp->colour_type == 3)
+      dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT = 8;
+   else
+      dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT =
+         dp->bit_depth;
+   dp->interlace_type = INTERLACE_FROM_ID(id);
+   check_interlace_type(dp->interlace_type);
+   dp->id = id;
+   /* All the rest are filled in after the read_info: */
+   dp->w = 0;
+   dp->h = 0;
+   dp->npasses = 0;
+   dp->pixel_size = 0;
+   dp->bit_width = 0;
+   dp->cbRow = 0;
+   dp->do_interlace = do_interlace;
+   dp->littleendian = 0;
+   dp->is_transparent = 0;
+   dp->speed = ps->speed;
+   dp->use_update_info = use_update_info;
+   dp->npalette = 0;
+   /* Preset the transparent color to black: */
+   memset(&dp->transparent, 0, sizeof dp->transparent);
+   /* Preset the palette to full intensity/opaque througout: */
+   memset(dp->palette, 0xff, sizeof dp->palette);
+}
+
+/* Initialize the palette fields - this must be done later because the palette
+ * comes from the particular png_store_file that is selected.
+ */
+static void
+standard_palette_init(standard_display *dp)
+{
+   store_palette_entry *palette = store_current_palette(dp->ps, &dp->npalette);
+
+   /* The remaining entries remain white/opaque. */
+   if (dp->npalette > 0)
+   {
+      int i = dp->npalette;
+      memcpy(dp->palette, palette, i * sizeof *palette);
+
+      /* Check for a non-opaque palette entry: */
+      while (--i >= 0)
+         if (palette[i].alpha < 255)
+            break;
+
+#     ifdef __GNUC__
+         /* GCC can't handle the more obviously optimizable version. */
+         if (i >= 0)
+            dp->is_transparent = 1;
+         else
+            dp->is_transparent = 0;
+#     else
+         dp->is_transparent = (i >= 0);
+#     endif
+   }
+}
+
+/* Utility to read the palette from the PNG file and convert it into
+ * store_palette format.  This returns 1 if there is any transparency in the
+ * palette (it does not check for a transparent colour in the non-palette case.)
+ */
+static int
+read_palette(store_palette palette, int *npalette, png_const_structp pp,
+   png_infop pi)
+{
+   png_colorp pal;
+   png_bytep trans_alpha;
+   int num;
+
+   pal = 0;
+   *npalette = -1;
+
+   if (png_get_PLTE(pp, pi, &pal, npalette) & PNG_INFO_PLTE)
+   {
+      int i = *npalette;
+
+      if (i <= 0 || i > 256)
+         png_error(pp, "validate: invalid PLTE count");
+
+      while (--i >= 0)
+      {
+         palette[i].red = pal[i].red;
+         palette[i].green = pal[i].green;
+         palette[i].blue = pal[i].blue;
+      }
+
+      /* Mark the remainder of the entries with a flag value (other than
+       * white/opaque which is the flag value stored above.)
+       */
+      memset(palette + *npalette, 126, (256-*npalette) * sizeof *palette);
+   }
+
+   else /* !png_get_PLTE */
+   {
+      if (*npalette != (-1))
+         png_error(pp, "validate: invalid PLTE result");
+      /* But there is no palette, so record this: */
+      *npalette = 0;
+      memset(palette, 113, sizeof (store_palette));
+   }
+
+   trans_alpha = 0;
+   num = 2; /* force error below */
+   if ((png_get_tRNS(pp, pi, &trans_alpha, &num, 0) & PNG_INFO_tRNS) != 0 &&
+      (trans_alpha != NULL || num != 1/*returns 1 for a transparent color*/) &&
+      /* Oops, if a palette tRNS gets expanded png_read_update_info (at least so
+       * far as 1.5.4) does not remove the trans_alpha pointer, only num_trans,
+       * so in the above call we get a success, we get a pointer (who knows what
+       * to) and we get num_trans == 0:
+       */
+      !(trans_alpha != NULL && num == 0)) /* TODO: fix this in libpng. */
+   {
+      int i;
+
+      /* Any of these are crash-worthy - given the implementation of
+       * png_get_tRNS up to 1.5 an app won't crash if it just checks the
+       * result above and fails to check that the variables it passed have
+       * actually been filled in!  Note that if the app were to pass the
+       * last, png_color_16p, variable too it couldn't rely on this.
+       */
+      if (trans_alpha == NULL || num <= 0 || num > 256 || num > *npalette)
+         png_error(pp, "validate: unexpected png_get_tRNS (palette) result");
+
+      for (i=0; i<num; ++i)
+         palette[i].alpha = trans_alpha[i];
+
+      for (num=*npalette; i<num; ++i)
+         palette[i].alpha = 255;
+
+      for (; i<256; ++i)
+         palette[i].alpha = 33; /* flag value */
+
+      return 1; /* transparency */
+   }
+
+   else
+   {
+      /* No palette transparency - just set the alpha channel to opaque. */
+      int i;
+
+      for (i=0, num=*npalette; i<num; ++i)
+         palette[i].alpha = 255;
+
+      for (; i<256; ++i)
+         palette[i].alpha = 55; /* flag value */
+
+      return 0; /* no transparency */
+   }
+}
+
+/* Utility to validate the palette if it should not have changed (the
+ * non-transform case).
+ */
+static void
+standard_palette_validate(standard_display *dp, png_const_structp pp,
+   png_infop pi)
+{
+   int npalette;
+   store_palette palette;
+
+   if (read_palette(palette, &npalette, pp, pi) != dp->is_transparent)
+      png_error(pp, "validate: palette transparency changed");
+
+   if (npalette != dp->npalette)
+   {
+      size_t pos = 0;
+      char msg[64];
+
+      pos = safecat(msg, sizeof msg, pos, "validate: palette size changed: ");
+      pos = safecatn(msg, sizeof msg, pos, dp->npalette);
+      pos = safecat(msg, sizeof msg, pos, " -> ");
+      pos = safecatn(msg, sizeof msg, pos, npalette);
+      png_error(pp, msg);
+   }
+
+   {
+      int i = npalette; /* npalette is aliased */
+
+      while (--i >= 0)
+         if (palette[i].red != dp->palette[i].red ||
+            palette[i].green != dp->palette[i].green ||
+            palette[i].blue != dp->palette[i].blue ||
+            palette[i].alpha != dp->palette[i].alpha)
+            png_error(pp, "validate: PLTE or tRNS chunk changed");
+   }
+}
+
+/* By passing a 'standard_display' the progressive callbacks can be used
+ * directly by the sequential code, the functions suffixed "_imp" are the
+ * implementations, the functions without the suffix are the callbacks.
+ *
+ * The code for the info callback is split into two because this callback calls
+ * png_read_update_info or png_start_read_image and what gets called depends on
+ * whether the info needs updating (we want to test both calls in pngvalid.)
+ */
+static void
+standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
+{
+   if (png_get_bit_depth(pp, pi) != dp->bit_depth)
+      png_error(pp, "validate: bit depth changed");
+
+   if (png_get_color_type(pp, pi) != dp->colour_type)
+      png_error(pp, "validate: color type changed");
+
+   if (png_get_filter_type(pp, pi) != PNG_FILTER_TYPE_BASE)
+      png_error(pp, "validate: filter type changed");
+
+   if (png_get_interlace_type(pp, pi) != dp->interlace_type)
+      png_error(pp, "validate: interlacing changed");
+
+   if (png_get_compression_type(pp, pi) != PNG_COMPRESSION_TYPE_BASE)
+      png_error(pp, "validate: compression type changed");
+
+   dp->w = png_get_image_width(pp, pi);
+
+   if (dp->w != standard_width(pp, dp->id))
+      png_error(pp, "validate: image width changed");
+
+   dp->h = png_get_image_height(pp, pi);
+
+   if (dp->h != standard_height(pp, dp->id))
+      png_error(pp, "validate: image height changed");
+
+   /* Record (but don't check at present) the input sBIT according to the colour
+    * type information.
+    */
+   {
+      png_color_8p sBIT = 0;
+
+      if (png_get_sBIT(pp, pi, &sBIT) & PNG_INFO_sBIT)
+      {
+         int sBIT_invalid = 0;
+
+         if (sBIT == 0)
+            png_error(pp, "validate: unexpected png_get_sBIT result");
+
+         if (dp->colour_type & PNG_COLOR_MASK_COLOR)
+         {
+            if (sBIT->red == 0 || sBIT->red > dp->bit_depth)
+               sBIT_invalid = 1;
+            else
+               dp->red_sBIT = sBIT->red;
+
+            if (sBIT->green == 0 || sBIT->green > dp->bit_depth)
+               sBIT_invalid = 1;
+            else
+               dp->green_sBIT = sBIT->green;
+
+            if (sBIT->blue == 0 || sBIT->blue > dp->bit_depth)
+               sBIT_invalid = 1;
+            else
+               dp->blue_sBIT = sBIT->blue;
+         }
+
+         else /* !COLOR */
+         {
+            if (sBIT->gray == 0 || sBIT->gray > dp->bit_depth)
+               sBIT_invalid = 1;
+            else
+               dp->blue_sBIT = dp->green_sBIT = dp->red_sBIT = sBIT->gray;
+         }
+
+         /* All 8 bits in tRNS for a palette image are significant - see the
+          * spec.
+          */
+         if (dp->colour_type & PNG_COLOR_MASK_ALPHA)
+         {
+            if (sBIT->alpha == 0 || sBIT->alpha > dp->bit_depth)
+               sBIT_invalid = 1;
+            else
+               dp->alpha_sBIT = sBIT->alpha;
+         }
+
+         if (sBIT_invalid)
+            png_error(pp, "validate: sBIT value out of range");
+      }
+   }
+
+   /* Important: this is validating the value *before* any transforms have been
+    * put in place.  It doesn't matter for the standard tests, where there are
+    * no transforms, but it does for other tests where rowbytes may change after
+    * png_read_update_info.
+    */
+   if (png_get_rowbytes(pp, pi) != standard_rowsize(pp, dp->id))
+      png_error(pp, "validate: row size changed");
+
+   /* Validate the colour type 3 palette (this can be present on other color
+    * types.)
+    */
+   standard_palette_validate(dp, pp, pi);
+
+   /* In any case always check for a tranparent color (notice that the
+    * colour type 3 case must not give a successful return on the get_tRNS call
+    * with these arguments!)
+    */
+   {
+      png_color_16p trans_color = 0;
+
+      if (png_get_tRNS(pp, pi, 0, 0, &trans_color) & PNG_INFO_tRNS)
+      {
+         if (trans_color == 0)
+            png_error(pp, "validate: unexpected png_get_tRNS (color) result");
+
+         switch (dp->colour_type)
+         {
+         case 0:
+            dp->transparent.red = dp->transparent.green = dp->transparent.blue =
+               trans_color->gray;
+            dp->has_tRNS = 1;
+            break;
+
+         case 2:
+            dp->transparent.red = trans_color->red;
+            dp->transparent.green = trans_color->green;
+            dp->transparent.blue = trans_color->blue;
+            dp->has_tRNS = 1;
+            break;
+
+         case 3:
+            /* Not expected because it should result in the array case
+             * above.
+             */
+            png_error(pp, "validate: unexpected png_get_tRNS result");
+            break;
+
+         default:
+            png_error(pp, "validate: invalid tRNS chunk with alpha image");
+         }
+      }
+   }
+
+   /* Read the number of passes - expected to match the value used when
+    * creating the image (interlaced or not).  This has the side effect of
+    * turning on interlace handling (if do_interlace is not set.)
+    */
+   dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
+   if (!dp->do_interlace)
+   {
+#     ifdef PNG_READ_INTERLACING_SUPPORTED
+         if (dp->npasses != png_set_interlace_handling(pp))
+            png_error(pp, "validate: file changed interlace type");
+#     else /* !READ_INTERLACING */
+         /* This should never happen: the relevant tests (!do_interlace) should
+          * not be run.
+          */
+         if (dp->npasses > 1)
+            png_error(pp, "validate: no libpng interlace support");
+#     endif /* !READ_INTERLACING */
+   }
+
+   /* Caller calls png_read_update_info or png_start_read_image now, then calls
+    * part2.
+    */
+}
+
+/* This must be called *after* the png_read_update_info call to get the correct
+ * 'rowbytes' value, otherwise png_get_rowbytes will refer to the untransformed
+ * image.
+ */
+static void
+standard_info_part2(standard_display *dp, png_const_structp pp,
+    png_const_infop pi, int nImages)
+{
+   /* Record cbRow now that it can be found. */
+   {
+      png_byte ct = png_get_color_type(pp, pi);
+      png_byte bd = png_get_bit_depth(pp, pi);
+
+      if (bd >= 8 && (ct == PNG_COLOR_TYPE_RGB || ct == PNG_COLOR_TYPE_GRAY) &&
+          dp->filler)
+          ct |= 4; /* handle filler as faked alpha channel */
+
+      dp->pixel_size = bit_size(pp, ct, bd);
+   }
+   dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
+   dp->cbRow = png_get_rowbytes(pp, pi);
+
+   /* Validate the rowbytes here again. */
+   if (dp->cbRow != (dp->bit_width+7)/8)
+      png_error(pp, "bad png_get_rowbytes calculation");
+
+   /* Then ensure there is enough space for the output image(s). */
+   store_ensure_image(dp->ps, pp, nImages, dp->cbRow, dp->h);
+}
+
+static void
+standard_info_imp(standard_display *dp, png_structp pp, png_infop pi,
+    int nImages)
+{
+   /* Note that the validation routine has the side effect of turning on
+    * interlace handling in the subsequent code.
+    */
+   standard_info_part1(dp, pp, pi);
+
+   /* And the info callback has to call this (or png_read_update_info - see
+    * below in the png_modifier code for that variant.
+    */
+   if (dp->use_update_info)
+   {
+      /* For debugging the effect of multiple calls: */
+      int i = dp->use_update_info;
+      while (i-- > 0)
+         png_read_update_info(pp, pi);
+   }
+
+   else
+      png_start_read_image(pp);
+
+   /* Validate the height, width and rowbytes plus ensure that sufficient buffer
+    * exists for decoding the image.
+    */
+   standard_info_part2(dp, pp, pi, nImages);
+}
+
+static void PNGCBAPI
+standard_info(png_structp pp, png_infop pi)
+{
+   standard_display *dp = voidcast(standard_display*,
+      png_get_progressive_ptr(pp));
+
+   /* Call with nImages==1 because the progressive reader can only produce one
+    * image.
+    */
+   standard_info_imp(dp, pp, pi, 1 /*only one image*/);
+}
+
+static void PNGCBAPI
+progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
+{
+   png_const_structp pp = ppIn;
+   const standard_display *dp = voidcast(standard_display*,
+      png_get_progressive_ptr(pp));
+
+   /* When handling interlacing some rows will be absent in each pass, the
+    * callback still gets called, but with a NULL pointer.  This is checked
+    * in the 'else' clause below.  We need our own 'cbRow', but we can't call
+    * png_get_rowbytes because we got no info structure.
+    */
+   if (new_row != NULL)
+   {
+      png_bytep row;
+
+      /* In the case where the reader doesn't do the interlace it gives
+       * us the y in the sub-image:
+       */
+      if (dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7)
+      {
+#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
+         /* Use this opportunity to validate the png 'current' APIs: */
+         if (y != png_get_current_row_number(pp))
+            png_error(pp, "png_get_current_row_number is broken");
+
+         if (pass != png_get_current_pass_number(pp))
+            png_error(pp, "png_get_current_pass_number is broken");
+#endif /* USER_TRANSFORM_INFO */
+
+         y = PNG_ROW_FROM_PASS_ROW(y, pass);
+      }
+
+      /* Validate this just in case. */
+      if (y >= dp->h)
+         png_error(pp, "invalid y to progressive row callback");
+
+      row = store_image_row(dp->ps, pp, 0, y);
+
+      /* Combine the new row into the old: */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+      if (dp->do_interlace)
+#endif /* READ_INTERLACING */
+      {
+         if (dp->interlace_type == PNG_INTERLACE_ADAM7)
+            deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass,
+                  dp->littleendian);
+         else
+            row_copy(row, new_row, dp->pixel_size * dp->w, dp->littleendian);
+      }
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+      else
+         png_progressive_combine_row(pp, row, new_row);
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+   }
+
+   else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
+       PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+       PNG_PASS_COLS(dp->w, pass) > 0)
+      png_error(pp, "missing row in progressive de-interlacing");
+}
+
+static void
+sequential_row(standard_display *dp, png_structp pp, png_infop pi,
+    const int iImage, const int iDisplay)
+{
+   const int         npasses = dp->npasses;
+   const int         do_interlace = dp->do_interlace &&
+      dp->interlace_type == PNG_INTERLACE_ADAM7;
+   const png_uint_32 height = standard_height(pp, dp->id);
+   const png_uint_32 width = standard_width(pp, dp->id);
+   const png_store*  ps = dp->ps;
+   int pass;
+
+   for (pass=0; pass<npasses; ++pass)
+   {
+      png_uint_32 y;
+      png_uint_32 wPass = PNG_PASS_COLS(width, pass);
+
+      for (y=0; y<height; ++y)
+      {
+         if (do_interlace)
+         {
+            /* wPass may be zero or this row may not be in this pass.
+             * png_read_row must not be called in either case.
+             */
+            if (wPass > 0 && PNG_ROW_IN_INTERLACE_PASS(y, pass))
+            {
+               /* Read the row into a pair of temporary buffers, then do the
+                * merge here into the output rows.
+                */
+               png_byte row[STANDARD_ROWMAX], display[STANDARD_ROWMAX];
+
+               /* The following aids (to some extent) error detection - we can
+                * see where png_read_row wrote.  Use opposite values in row and
+                * display to make this easier.  Don't use 0xff (which is used in
+                * the image write code to fill unused bits) or 0 (which is a
+                * likely value to overwrite unused bits with).
+                */
+               memset(row, 0xc5, sizeof row);
+               memset(display, 0x5c, sizeof display);
+
+               png_read_row(pp, row, display);
+
+               if (iImage >= 0)
+                  deinterlace_row(store_image_row(ps, pp, iImage, y), row,
+                     dp->pixel_size, dp->w, pass, dp->littleendian);
+
+               if (iDisplay >= 0)
+                  deinterlace_row(store_image_row(ps, pp, iDisplay, y), display,
+                     dp->pixel_size, dp->w, pass, dp->littleendian);
+            }
+         }
+         else
+            png_read_row(pp,
+               iImage >= 0 ? store_image_row(ps, pp, iImage, y) : NULL,
+               iDisplay >= 0 ? store_image_row(ps, pp, iDisplay, y) : NULL);
+      }
+   }
+
+   /* And finish the read operation (only really necessary if the caller wants
+    * to find additional data in png_info from chunks after the last IDAT.)
+    */
+   png_read_end(pp, pi);
+}
+
+#ifdef PNG_TEXT_SUPPORTED
+static void
+standard_check_text(png_const_structp pp, png_const_textp tp,
+   png_const_charp keyword, png_const_charp text)
+{
+   char msg[1024];
+   size_t pos = safecat(msg, sizeof msg, 0, "text: ");
+   size_t ok;
+
+   pos = safecat(msg, sizeof msg, pos, keyword);
+   pos = safecat(msg, sizeof msg, pos, ": ");
+   ok = pos;
+
+   if (tp->compression != TEXT_COMPRESSION)
+   {
+      char buf[64];
+
+      sprintf(buf, "compression [%d->%d], ", TEXT_COMPRESSION,
+         tp->compression);
+      pos = safecat(msg, sizeof msg, pos, buf);
+   }
+
+   if (tp->key == NULL || strcmp(tp->key, keyword) != 0)
+   {
+      pos = safecat(msg, sizeof msg, pos, "keyword \"");
+      if (tp->key != NULL)
+      {
+         pos = safecat(msg, sizeof msg, pos, tp->key);
+         pos = safecat(msg, sizeof msg, pos, "\", ");
+      }
+
+      else
+         pos = safecat(msg, sizeof msg, pos, "null, ");
+   }
+
+   if (tp->text == NULL)
+      pos = safecat(msg, sizeof msg, pos, "text lost, ");
+
+   else
+   {
+      if (tp->text_length != strlen(text))
+      {
+         char buf[64];
+         sprintf(buf, "text length changed[%lu->%lu], ",
+            (unsigned long)strlen(text), (unsigned long)tp->text_length);
+         pos = safecat(msg, sizeof msg, pos, buf);
+      }
+
+      if (strcmp(tp->text, text) != 0)
+      {
+         pos = safecat(msg, sizeof msg, pos, "text becomes \"");
+         pos = safecat(msg, sizeof msg, pos, tp->text);
+         pos = safecat(msg, sizeof msg, pos, "\" (was \"");
+         pos = safecat(msg, sizeof msg, pos, text);
+         pos = safecat(msg, sizeof msg, pos, "\"), ");
+      }
+   }
+
+   if (tp->itxt_length != 0)
+      pos = safecat(msg, sizeof msg, pos, "iTXt length set, ");
+
+   if (tp->lang != NULL)
+   {
+      pos = safecat(msg, sizeof msg, pos, "iTXt language \"");
+      pos = safecat(msg, sizeof msg, pos, tp->lang);
+      pos = safecat(msg, sizeof msg, pos, "\", ");
+   }
+
+   if (tp->lang_key != NULL)
+   {
+      pos = safecat(msg, sizeof msg, pos, "iTXt keyword \"");
+      pos = safecat(msg, sizeof msg, pos, tp->lang_key);
+      pos = safecat(msg, sizeof msg, pos, "\", ");
+   }
+
+   if (pos > ok)
+   {
+      msg[pos-2] = '\0'; /* Remove the ", " at the end */
+      png_error(pp, msg);
+   }
+}
+
+static void
+standard_text_validate(standard_display *dp, png_const_structp pp,
+   png_infop pi, int check_end)
+{
+   png_textp tp = NULL;
+   png_uint_32 num_text = png_get_text(pp, pi, &tp, NULL);
+
+   if (num_text == 2 && tp != NULL)
+   {
+      standard_check_text(pp, tp, "image name", dp->ps->current->name);
+
+      /* This exists because prior to 1.5.18 the progressive reader left the
+       * png_struct z_stream unreset at the end of the image, so subsequent
+       * attempts to use it simply returns Z_STREAM_END.
+       */
+      if (check_end)
+         standard_check_text(pp, tp+1, "end marker", "end");
+   }
+
+   else
+   {
+      char msg[64];
+
+      sprintf(msg, "expected two text items, got %lu",
+         (unsigned long)num_text);
+      png_error(pp, msg);
+   }
+}
+#else
+#  define standard_text_validate(dp,pp,pi,check_end) ((void)0)
+#endif
+
+static void
+standard_row_validate(standard_display *dp, png_const_structp pp,
+   int iImage, int iDisplay, png_uint_32 y)
+{
+   int where;
+   png_byte std[STANDARD_ROWMAX];
+
+   /* The row must be pre-initialized to the magic number here for the size
+    * tests to pass:
+    */
+   memset(std, 178, sizeof std);
+   standard_row(pp, std, dp->id, y);
+
+   /* At the end both the 'row' and 'display' arrays should end up identical.
+    * In earlier passes 'row' will be partially filled in, with only the pixels
+    * that have been read so far, but 'display' will have those pixels
+    * replicated to fill the unread pixels while reading an interlaced image.
+    */
+   if (iImage >= 0 &&
+      (where = pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
+            dp->bit_width)) != 0)
+   {
+      char msg[64];
+      sprintf(msg, "PNG image row[%lu][%d] changed from %.2x to %.2x",
+         (unsigned long)y, where-1, std[where-1],
+         store_image_row(dp->ps, pp, iImage, y)[where-1]);
+      png_error(pp, msg);
+   }
+
+   if (iDisplay >= 0 &&
+      (where = pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y),
+         dp->bit_width)) != 0)
+   {
+      char msg[64];
+      sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x",
+         (unsigned long)y, where-1, std[where-1],
+         store_image_row(dp->ps, pp, iDisplay, y)[where-1]);
+      png_error(pp, msg);
+   }
+}
+
+static void
+standard_image_validate(standard_display *dp, png_const_structp pp, int iImage,
+    int iDisplay)
+{
+   png_uint_32 y;
+
+   if (iImage >= 0)
+      store_image_check(dp->ps, pp, iImage);
+
+   if (iDisplay >= 0)
+      store_image_check(dp->ps, pp, iDisplay);
+
+   for (y=0; y<dp->h; ++y)
+      standard_row_validate(dp, pp, iImage, iDisplay, y);
+
+   /* This avoids false positives if the validation code is never called! */
+   dp->ps->validated = 1;
+}
+
+static void PNGCBAPI
+standard_end(png_structp ppIn, png_infop pi)
+{
+   png_const_structp pp = ppIn;
+   standard_display *dp = voidcast(standard_display*,
+      png_get_progressive_ptr(pp));
+
+   UNUSED(pi)
+
+   /* Validate the image - progressive reading only produces one variant for
+    * interlaced images.
+    */
+   standard_text_validate(dp, pp, pi,
+      PNG_LIBPNG_VER >= 10518/*check_end: see comments above*/);
+   standard_image_validate(dp, pp, 0, -1);
+}
+
+/* A single test run checking the standard image to ensure it is not damaged. */
+static void
+standard_test(png_store* const psIn, png_uint_32 const id,
+   int do_interlace, int use_update_info)
+{
+   standard_display d;
+   context(psIn, fault);
+
+   /* Set up the display (stack frame) variables from the arguments to the
+    * function and initialize the locals that are filled in later.
+    */
+   standard_display_init(&d, psIn, id, do_interlace, use_update_info);
+
+   /* Everything is protected by a Try/Catch.  The functions called also
+    * typically have local Try/Catch blocks.
+    */
+   Try
+   {
+      png_structp pp;
+      png_infop pi;
+
+      /* Get a png_struct for reading the image. This will throw an error if it
+       * fails, so we don't need to check the result.
+       */
+      pp = set_store_for_read(d.ps, &pi, d.id,
+         d.do_interlace ?  (d.ps->progressive ?
+            "pngvalid progressive deinterlacer" :
+            "pngvalid sequential deinterlacer") : (d.ps->progressive ?
+               "progressive reader" : "sequential reader"));
+
+      /* Initialize the palette correctly from the png_store_file. */
+      standard_palette_init(&d);
+
+      /* Introduce the correct read function. */
+      if (d.ps->progressive)
+      {
+         png_set_progressive_read_fn(pp, &d, standard_info, progressive_row,
+            standard_end);
+
+         /* Now feed data into the reader until we reach the end: */
+         store_progressive_read(d.ps, pp, pi);
+      }
+      else
+      {
+         /* Note that this takes the store, not the display. */
+         png_set_read_fn(pp, d.ps, store_read);
+
+         /* Check the header values: */
+         png_read_info(pp, pi);
+
+         /* The code tests both versions of the images that the sequential
+          * reader can produce.
+          */
+         standard_info_imp(&d, pp, pi, 2 /*images*/);
+
+         /* Need the total bytes in the image below; we can't get to this point
+          * unless the PNG file values have been checked against the expected
+          * values.
+          */
+         {
+            sequential_row(&d, pp, pi, 0, 1);
+
+            /* After the last pass loop over the rows again to check that the
+             * image is correct.
+             */
+            if (!d.speed)
+            {
+               standard_text_validate(&d, pp, pi, 1/*check_end*/);
+               standard_image_validate(&d, pp, 0, 1);
+            }
+            else
+               d.ps->validated = 1;
+         }
+      }
+
+      /* Check for validation. */
+      if (!d.ps->validated)
+         png_error(pp, "image read failed silently");
+
+      /* Successful completion. */
+   }
+
+   Catch(fault)
+      d.ps = fault; /* make sure this hasn't been clobbered. */
+
+   /* In either case clean up the store. */
+   store_read_reset(d.ps);
+}
+
+static int
+test_standard(png_modifier* const pm, png_byte const colour_type,
+    int bdlo, int const bdhi)
+{
+   for (; bdlo <= bdhi; ++bdlo)
+   {
+      int interlace_type;
+
+      for (interlace_type = PNG_INTERLACE_NONE;
+           interlace_type < INTERLACE_LAST; ++interlace_type)
+      {
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            interlace_type, 0, 0, 0), do_read_interlace, pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+      }
+   }
+
+   return 1; /* keep going */
+}
+
+static void
+perform_standard_test(png_modifier *pm)
+{
+   /* Test each colour type over the valid range of bit depths (expressed as
+    * log2(bit_depth) in turn, stop as soon as any error is detected.
+    */
+   if (!test_standard(pm, 0, 0, READ_BDHI))
+      return;
+
+   if (!test_standard(pm, 2, 3, READ_BDHI))
+      return;
+
+   if (!test_standard(pm, 3, 0, 3))
+      return;
+
+   if (!test_standard(pm, 4, 3, READ_BDHI))
+      return;
+
+   if (!test_standard(pm, 6, 3, READ_BDHI))
+      return;
+}
+
+
+/********************************** SIZE TESTS ********************************/
+static int
+test_size(png_modifier* const pm, png_byte const colour_type,
+    int bdlo, int const bdhi)
+{
+   /* Run the tests on each combination.
+    *
+    * NOTE: on my 32 bit x86 each of the following blocks takes
+    * a total of 3.5 seconds if done across every combo of bit depth
+    * width and height.  This is a waste of time in practice, hence the
+    * hinc and winc stuff:
+    */
+   static const png_byte hinc[] = {1, 3, 11, 1, 5};
+   static const png_byte winc[] = {1, 9, 5, 7, 1};
+   const int save_bdlo = bdlo;
+
+   for (; bdlo <= bdhi; ++bdlo)
+   {
+      png_uint_32 h, w;
+
+      for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
+      {
+         /* First test all the 'size' images against the sequential
+          * reader using libpng to deinterlace (where required.)  This
+          * validates the write side of libpng.  There are four possibilities
+          * to validate.
+          */
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+
+         /* Now validate the interlaced read side - do_interlace true,
+          * in the progressive case this does actually make a difference
+          * to the code used in the non-interlaced case too.
+          */
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+
+#     if CAN_WRITE_INTERLACE
+         /* Validate the pngvalid code itself: */
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_ADAM7, w, h, 1), 1/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+#     endif
+      }
+   }
+
+   /* Now do the tests of libpng interlace handling, after we have made sure
+    * that the pngvalid version works:
+    */
+   for (bdlo = save_bdlo; bdlo <= bdhi; ++bdlo)
+   {
+      png_uint_32 h, w;
+
+      for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
+      {
+#     ifdef PNG_READ_INTERLACING_SUPPORTED
+         /* Test with pngvalid generated interlaced images first; we have
+          * already verify these are ok (unless pngvalid has self-consistent
+          * read/write errors, which is unlikely), so this detects errors in the
+          * read side first:
+          */
+#     if CAN_WRITE_INTERLACE
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+#     endif
+#     endif /* READ_INTERLACING */
+
+#     ifdef PNG_WRITE_INTERLACING_SUPPORTED
+         /* Test the libpng write side against the pngvalid read side: */
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+#     endif
+
+#     ifdef PNG_READ_INTERLACING_SUPPORTED
+#     ifdef PNG_WRITE_INTERLACING_SUPPORTED
+         /* Test both together: */
+         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+            PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/,
+            pm->use_update_info);
+
+         if (fail(pm))
+            return 0;
+#     endif
+#     endif /* READ_INTERLACING */
+      }
+   }
+
+   return 1; /* keep going */
+}
+
+static void
+perform_size_test(png_modifier *pm)
+{
+   /* Test each colour type over the valid range of bit depths (expressed as
+    * log2(bit_depth) in turn, stop as soon as any error is detected.
+    */
+   if (!test_size(pm, 0, 0, READ_BDHI))
+      return;
+
+   if (!test_size(pm, 2, 3, READ_BDHI))
+      return;
+
+   /* For the moment don't do the palette test - it's a waste of time when
+    * compared to the grayscale test.
+    */
+#if 0
+   if (!test_size(pm, 3, 0, 3))
+      return;
+#endif
+
+   if (!test_size(pm, 4, 3, READ_BDHI))
+      return;
+
+   if (!test_size(pm, 6, 3, READ_BDHI))
+      return;
+}
+
+
+/******************************* TRANSFORM TESTS ******************************/
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* A set of tests to validate libpng image transforms.  The possibilities here
+ * are legion because the transforms can be combined in a combinatorial
+ * fashion.  To deal with this some measure of restraint is required, otherwise
+ * the tests would take forever.
+ */
+typedef struct image_pixel
+{
+   /* A local (pngvalid) representation of a PNG pixel, in all its
+    * various forms.
+    */
+   unsigned int red, green, blue, alpha; /* For non-palette images. */
+   unsigned int palette_index;           /* For a palette image. */
+   png_byte     colour_type;             /* As in the spec. */
+   png_byte     bit_depth;               /* Defines bit size in row */
+   png_byte     sample_depth;            /* Scale of samples */
+   unsigned int have_tRNS :1;            /* tRNS chunk may need processing */
+   unsigned int swap_rgb :1;             /* RGB swapped to BGR */
+   unsigned int alpha_first :1;          /* Alpha at start, not end */
+   unsigned int alpha_inverted :1;       /* Alpha channel inverted */
+   unsigned int mono_inverted :1;        /* Gray channel inverted */
+   unsigned int swap16 :1;               /* Byte swap 16-bit components */
+   unsigned int littleendian :1;         /* High bits on right */
+   unsigned int sig_bits :1;             /* Pixel shifted (sig bits only) */
+
+   /* For checking the code calculates double precision floating point values
+    * along with an error value, accumulated from the transforms.  Because an
+    * sBIT setting allows larger error bounds (indeed, by the spec, apparently
+    * up to just less than +/-1 in the scaled value) the *lowest* sBIT for each
+    * channel is stored.  This sBIT value is folded in to the stored error value
+    * at the end of the application of the transforms to the pixel.
+    *
+    * If sig_bits is set above the red, green, blue and alpha values have been
+    * scaled so they only contain the significant bits of the component values.
+    */
+   double   redf, greenf, bluef, alphaf;
+   double   rede, greene, bluee, alphae;
+   png_byte red_sBIT, green_sBIT, blue_sBIT, alpha_sBIT;
+} image_pixel;
+
+/* Shared utility function, see below. */
+static void
+image_pixel_setf(image_pixel *this, unsigned int rMax, unsigned int gMax,
+        unsigned int bMax, unsigned int aMax)
+{
+   this->redf = this->red / (double)rMax;
+   this->greenf = this->green / (double)gMax;
+   this->bluef = this->blue / (double)bMax;
+   this->alphaf = this->alpha / (double)aMax;
+
+   if (this->red < rMax)
+      this->rede = this->redf * DBL_EPSILON;
+   else
+      this->rede = 0;
+   if (this->green < gMax)
+      this->greene = this->greenf * DBL_EPSILON;
+   else
+      this->greene = 0;
+   if (this->blue < bMax)
+      this->bluee = this->bluef * DBL_EPSILON;
+   else
+      this->bluee = 0;
+   if (this->alpha < aMax)
+      this->alphae = this->alphaf * DBL_EPSILON;
+   else
+      this->alphae = 0;
+}
+
+/* Initialize the structure for the next pixel - call this before doing any
+ * transforms and call it for each pixel since all the fields may need to be
+ * reset.
+ */
+static void
+image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
+    png_byte bit_depth, png_uint_32 x, store_palette palette,
+    const image_pixel *format /*from pngvalid transform of input*/)
+{
+   const png_byte sample_depth = (png_byte)(colour_type ==
+      PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
+   const unsigned int max = (1U<<sample_depth)-1;
+   const int swap16 = (format != 0 && format->swap16);
+   const int littleendian = (format != 0 && format->littleendian);
+   const int sig_bits = (format != 0 && format->sig_bits);
+
+   /* Initially just set everything to the same number and the alpha to opaque.
+    * Note that this currently assumes a simple palette where entry x has colour
+    * rgb(x,x,x)!
+    */
+   this->palette_index = this->red = this->green = this->blue =
+      sample(row, colour_type, bit_depth, x, 0, swap16, littleendian);
+   this->alpha = max;
+   this->red_sBIT = this->green_sBIT = this->blue_sBIT = this->alpha_sBIT =
+      sample_depth;
+
+   /* Then override as appropriate: */
+   if (colour_type == 3) /* palette */
+   {
+      /* This permits the caller to default to the sample value. */
+      if (palette != 0)
+      {
+         const unsigned int i = this->palette_index;
+
+         this->red = palette[i].red;
+         this->green = palette[i].green;
+         this->blue = palette[i].blue;
+         this->alpha = palette[i].alpha;
+      }
+   }
+
+   else /* not palette */
+   {
+      unsigned int i = 0;
+
+      if ((colour_type & 4) != 0 && format != 0 && format->alpha_first)
+      {
+         this->alpha = this->red;
+         /* This handles the gray case for 'AG' pixels */
+         this->palette_index = this->red = this->green = this->blue =
+            sample(row, colour_type, bit_depth, x, 1, swap16, littleendian);
+         i = 1;
+      }
+
+      if (colour_type & 2)
+      {
+         /* Green is second for both BGR and RGB: */
+         this->green = sample(row, colour_type, bit_depth, x, ++i, swap16,
+                 littleendian);
+
+         if (format != 0 && format->swap_rgb) /* BGR */
+             this->red = sample(row, colour_type, bit_depth, x, ++i, swap16,
+                     littleendian);
+         else
+             this->blue = sample(row, colour_type, bit_depth, x, ++i, swap16,
+                     littleendian);
+      }
+
+      else /* grayscale */ if (format != 0 && format->mono_inverted)
+         this->red = this->green = this->blue = this->red ^ max;
+
+      if ((colour_type & 4) != 0) /* alpha */
+      {
+         if (format == 0 || !format->alpha_first)
+             this->alpha = sample(row, colour_type, bit_depth, x, ++i, swap16,
+                     littleendian);
+
+         if (format != 0 && format->alpha_inverted)
+            this->alpha ^= max;
+      }
+   }
+
+   /* Calculate the scaled values, these are simply the values divided by
+    * 'max' and the error is initialized to the double precision epsilon value
+    * from the header file.
+    */
+   image_pixel_setf(this,
+      sig_bits ? (1U << format->red_sBIT)-1 : max,
+      sig_bits ? (1U << format->green_sBIT)-1 : max,
+      sig_bits ? (1U << format->blue_sBIT)-1 : max,
+      sig_bits ? (1U << format->alpha_sBIT)-1 : max);
+
+   /* Store the input information for use in the transforms - these will
+    * modify the information.
+    */
+   this->colour_type = colour_type;
+   this->bit_depth = bit_depth;
+   this->sample_depth = sample_depth;
+   this->have_tRNS = 0;
+   this->swap_rgb = 0;
+   this->alpha_first = 0;
+   this->alpha_inverted = 0;
+   this->mono_inverted = 0;
+   this->swap16 = 0;
+   this->littleendian = 0;
+   this->sig_bits = 0;
+}
+
+#if defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_GRAY_TO_RGB_SUPPORTED\
+   || defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_EXPAND_16_SUPPORTED\
+   || defined PNG_READ_BACKGROUND_SUPPORTED
+/* Convert a palette image to an rgb image.  This necessarily converts the tRNS
+ * chunk at the same time, because the tRNS will be in palette form.  The way
+ * palette validation works means that the original palette is never updated,
+ * instead the image_pixel value from the row contains the RGB of the
+ * corresponding palette entry and *this* is updated.  Consequently this routine
+ * only needs to change the colour type information.
+ */
+static void
+image_pixel_convert_PLTE(image_pixel *this)
+{
+   if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (this->have_tRNS)
+      {
+         this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         this->have_tRNS = 0;
+      }
+      else
+         this->colour_type = PNG_COLOR_TYPE_RGB;
+
+      /* The bit depth of the row changes at this point too (notice that this is
+       * the row format, not the sample depth, which is separate.)
+       */
+      this->bit_depth = 8;
+   }
+}
+
+/* Add an alpha channel; this will import the tRNS information because tRNS is
+ * not valid in an alpha image.  The bit depth will invariably be set to at
+ * least 8 prior to 1.7.0.  Palette images will be converted to alpha (using
+ * the above API).  With png_set_background the alpha channel is never expanded
+ * but this routine is used by pngvalid to simplify code; 'for_background'
+ * records this.
+ */
+static void
+image_pixel_add_alpha(image_pixel *this, const standard_display *display,
+   int for_background)
+{
+   if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
+      image_pixel_convert_PLTE(this);
+
+   if ((this->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
+   {
+      if (this->colour_type == PNG_COLOR_TYPE_GRAY)
+      {
+#        if PNG_LIBPNG_VER < 10700
+            if (!for_background && this->bit_depth < 8)
+               this->bit_depth = this->sample_depth = 8;
+#        endif
+
+         if (this->have_tRNS)
+         {
+            /* After 1.7 the expansion of bit depth only happens if there is a
+             * tRNS chunk to expand at this point.
+             */
+#           if PNG_LIBPNG_VER >= 10700
+               if (!for_background && this->bit_depth < 8)
+                  this->bit_depth = this->sample_depth = 8;
+#           endif
+
+            this->have_tRNS = 0;
+
+            /* Check the input, original, channel value here against the
+             * original tRNS gray chunk valie.
+             */
+            if (this->red == display->transparent.red)
+               this->alphaf = 0;
+            else
+               this->alphaf = 1;
+         }
+         else
+            this->alphaf = 1;
+
+         this->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+      }
+
+      else if (this->colour_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (this->have_tRNS)
+         {
+            this->have_tRNS = 0;
+
+            /* Again, check the exact input values, not the current transformed
+             * value!
+             */
+            if (this->red == display->transparent.red &&
+               this->green == display->transparent.green &&
+               this->blue == display->transparent.blue)
+               this->alphaf = 0;
+            else
+               this->alphaf = 1;
+         }
+         else
+            this->alphaf = 1;
+
+         this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
+      }
+
+      /* The error in the alpha is zero and the sBIT value comes from the
+       * original sBIT data (actually it will always be the original bit depth).
+       */
+      this->alphae = 0;
+      this->alpha_sBIT = display->alpha_sBIT;
+   }
+}
+#endif /* transforms that need image_pixel_add_alpha */
+
+struct transform_display;
+typedef struct image_transform
+{
+   /* The name of this transform: a string. */
+   const char *name;
+
+   /* Each transform can be disabled from the command line: */
+   int enable;
+
+   /* The global list of transforms; read only. */
+   struct image_transform *const list;
+
+   /* The global count of the number of times this transform has been set on an
+    * image.
+    */
+   unsigned int global_use;
+
+   /* The local count of the number of times this transform has been set. */
+   unsigned int local_use;
+
+   /* The next transform in the list, each transform must call its own next
+    * transform after it has processed the pixel successfully.
+    */
+   const struct image_transform *next;
+
+   /* A single transform for the image, expressed as a series of function
+    * callbacks and some space for values.
+    *
+    * First a callback to add any required modifications to the png_modifier;
+    * this gets called just before the modifier is set up for read.
+    */
+   void (*ini)(const struct image_transform *this,
+      struct transform_display *that);
+
+   /* And a callback to set the transform on the current png_read_struct:
+    */
+   void (*set)(const struct image_transform *this,
+      struct transform_display *that, png_structp pp, png_infop pi);
+
+   /* Then a transform that takes an input pixel in one PNG format or another
+    * and modifies it by a pngvalid implementation of the transform (thus
+    * duplicating the libpng intent without, we hope, duplicating the bugs
+    * in the libpng implementation!)  The png_structp is solely to allow error
+    * reporting via png_error and png_warning.
+    */
+   void (*mod)(const struct image_transform *this, image_pixel *that,
+      png_const_structp pp, const struct transform_display *display);
+
+   /* Add this transform to the list and return true if the transform is
+    * meaningful for this colour type and bit depth - if false then the
+    * transform should have no effect on the image so there's not a lot of
+    * point running it.
+    */
+   int (*add)(struct image_transform *this,
+      const struct image_transform **that, png_byte colour_type,
+      png_byte bit_depth);
+} image_transform;
+
+typedef struct transform_display
+{
+   standard_display this;
+
+   /* Parameters */
+   png_modifier*              pm;
+   const image_transform* transform_list;
+   unsigned int max_gamma_8;
+
+   /* Local variables */
+   png_byte output_colour_type;
+   png_byte output_bit_depth;
+   png_byte unpacked;
+
+   /* Modifications (not necessarily used.) */
+   gama_modification gama_mod;
+   chrm_modification chrm_mod;
+   srgb_modification srgb_mod;
+} transform_display;
+
+/* Set sRGB, cHRM and gAMA transforms as required by the current encoding. */
+static void
+transform_set_encoding(transform_display *this)
+{
+   /* Set up the png_modifier '_current' fields then use these to determine how
+    * to add appropriate chunks.
+    */
+   png_modifier *pm = this->pm;
+
+   modifier_set_encoding(pm);
+
+   if (modifier_color_encoding_is_set(pm))
+   {
+      if (modifier_color_encoding_is_sRGB(pm))
+         srgb_modification_init(&this->srgb_mod, pm, PNG_sRGB_INTENT_ABSOLUTE);
+
+      else
+      {
+         /* Set gAMA and cHRM separately. */
+         gama_modification_init(&this->gama_mod, pm, pm->current_gamma);
+
+         if (pm->current_encoding != 0)
+            chrm_modification_init(&this->chrm_mod, pm, pm->current_encoding);
+      }
+   }
+}
+
+/* Three functions to end the list: */
+static void
+image_transform_ini_end(const image_transform *this,
+   transform_display *that)
+{
+   UNUSED(this)
+   UNUSED(that)
+}
+
+static void
+image_transform_set_end(const image_transform *this,
+   transform_display *that, png_structp pp, png_infop pi)
+{
+   UNUSED(this)
+   UNUSED(that)
+   UNUSED(pp)
+   UNUSED(pi)
+}
+
+/* At the end of the list recalculate the output image pixel value from the
+ * double precision values set up by the preceding 'mod' calls:
+ */
+static unsigned int
+sample_scale(double sample_value, unsigned int scale)
+{
+   sample_value = floor(sample_value * scale + .5);
+
+   /* Return NaN as 0: */
+   if (!(sample_value > 0))
+      sample_value = 0;
+   else if (sample_value > scale)
+      sample_value = scale;
+
+   return (unsigned int)sample_value;
+}
+
+static void
+image_transform_mod_end(const image_transform *this, image_pixel *that,
+    png_const_structp pp, const transform_display *display)
+{
+   const unsigned int scale = (1U<<that->sample_depth)-1;
+   const int sig_bits = that->sig_bits;
+
+   UNUSED(this)
+   UNUSED(pp)
+   UNUSED(display)
+
+   /* At the end recalculate the digitized red green and blue values according
+    * to the current sample_depth of the pixel.
+    *
+    * The sample value is simply scaled to the maximum, checking for over
+    * and underflow (which can both happen for some image transforms,
+    * including simple size scaling, though libpng doesn't do that at present.
+    */
+   that->red = sample_scale(that->redf, scale);
+
+   /* This is a bit bogus; really the above calculation should use the red_sBIT
+    * value, not sample_depth, but because libpng does png_set_shift by just
+    * shifting the bits we get errors if we don't do it the same way.
+    */
+   if (sig_bits && that->red_sBIT < that->sample_depth)
+      that->red >>= that->sample_depth - that->red_sBIT;
+
+   /* The error value is increased, at the end, according to the lowest sBIT
+    * value seen.  Common sense tells us that the intermediate integer
+    * representations are no more accurate than +/- 0.5 in the integral values,
+    * the sBIT allows the implementation to be worse than this.  In addition the
+    * PNG specification actually permits any error within the range (-1..+1),
+    * but that is ignored here.  Instead the final digitized value is compared,
+    * below to the digitized value of the error limits - this has the net effect
+    * of allowing (almost) +/-1 in the output value.  It's difficult to see how
+    * any algorithm that digitizes intermediate results can be more accurate.
+    */
+   that->rede += 1./(2*((1U<<that->red_sBIT)-1));
+
+   if (that->colour_type & PNG_COLOR_MASK_COLOR)
+   {
+      that->green = sample_scale(that->greenf, scale);
+      if (sig_bits && that->green_sBIT < that->sample_depth)
+         that->green >>= that->sample_depth - that->green_sBIT;
+
+      that->blue = sample_scale(that->bluef, scale);
+      if (sig_bits && that->blue_sBIT < that->sample_depth)
+         that->blue >>= that->sample_depth - that->blue_sBIT;
+
+      that->greene += 1./(2*((1U<<that->green_sBIT)-1));
+      that->bluee += 1./(2*((1U<<that->blue_sBIT)-1));
+   }
+   else
+   {
+      that->blue = that->green = that->red;
+      that->bluef = that->greenf = that->redf;
+      that->bluee = that->greene = that->rede;
+   }
+
+   if ((that->colour_type & PNG_COLOR_MASK_ALPHA) ||
+      that->colour_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      that->alpha = sample_scale(that->alphaf, scale);
+      that->alphae += 1./(2*((1U<<that->alpha_sBIT)-1));
+   }
+   else
+   {
+      that->alpha = scale; /* opaque */
+      that->alphaf = 1;    /* Override this. */
+      that->alphae = 0;    /* It's exact ;-) */
+   }
+
+   if (sig_bits && that->alpha_sBIT < that->sample_depth)
+      that->alpha >>= that->sample_depth - that->alpha_sBIT;
+}
+
+/* Static 'end' structure: */
+static image_transform image_transform_end =
+{
+   "(end)", /* name */
+   1, /* enable */
+   0, /* list */
+   0, /* global_use */
+   0, /* local_use */
+   0, /* next */
+   image_transform_ini_end,
+   image_transform_set_end,
+   image_transform_mod_end,
+   0 /* never called, I want it to crash if it is! */
+};
+
+/* Reader callbacks and implementations, where they differ from the standard
+ * ones.
+ */
+static void
+transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id,
+    const image_transform *transform_list)
+{
+   memset(dp, 0, sizeof *dp);
+
+   /* Standard fields */
+   standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
+      pm->use_update_info);
+
+   /* Parameter fields */
+   dp->pm = pm;
+   dp->transform_list = transform_list;
+   dp->max_gamma_8 = 16;
+
+   /* Local variable fields */
+   dp->output_colour_type = 255; /* invalid */
+   dp->output_bit_depth = 255;  /* invalid */
+   dp->unpacked = 0; /* not unpacked */
+}
+
+static void
+transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
+{
+   /* Reuse the standard stuff as appropriate. */
+   standard_info_part1(&dp->this, pp, pi);
+
+   /* Now set the list of transforms. */
+   dp->transform_list->set(dp->transform_list, dp, pp, pi);
+
+   /* Update the info structure for these transforms: */
+   {
+      int i = dp->this.use_update_info;
+      /* Always do one call, even if use_update_info is 0. */
+      do
+         png_read_update_info(pp, pi);
+      while (--i > 0);
+   }
+
+   /* And get the output information into the standard_display */
+   standard_info_part2(&dp->this, pp, pi, 1/*images*/);
+
+   /* Plus the extra stuff we need for the transform tests: */
+   dp->output_colour_type = png_get_color_type(pp, pi);
+   dp->output_bit_depth = png_get_bit_depth(pp, pi);
+
+   /* If png_set_filler is in action then fake the output color type to include
+    * an alpha channel where appropriate.
+    */
+   if (dp->output_bit_depth >= 8 &&
+       (dp->output_colour_type == PNG_COLOR_TYPE_RGB ||
+        dp->output_colour_type == PNG_COLOR_TYPE_GRAY) && dp->this.filler)
+       dp->output_colour_type |= 4;
+
+   /* Validate the combination of colour type and bit depth that we are getting
+    * out of libpng; the semantics of something not in the PNG spec are, at
+    * best, unclear.
+    */
+   switch (dp->output_colour_type)
+   {
+   case PNG_COLOR_TYPE_PALETTE:
+      if (dp->output_bit_depth > 8) goto error;
+      /*FALL THROUGH*/
+   case PNG_COLOR_TYPE_GRAY:
+      if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
+         dp->output_bit_depth == 4)
+         break;
+      /*FALL THROUGH*/
+   default:
+      if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
+         break;
+      /*FALL THROUGH*/
+   error:
+      {
+         char message[128];
+         size_t pos;
+
+         pos = safecat(message, sizeof message, 0,
+            "invalid final bit depth: colour type(");
+         pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
+         pos = safecat(message, sizeof message, pos, ") with bit depth: ");
+         pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+
+         png_error(pp, message);
+      }
+   }
+
+   /* Use a test pixel to check that the output agrees with what we expect -
+    * this avoids running the whole test if the output is unexpected.  This also
+    * checks for internal errors.
+    */
+   {
+      image_pixel test_pixel;
+
+      memset(&test_pixel, 0, sizeof test_pixel);
+      test_pixel.colour_type = dp->this.colour_type; /* input */
+      test_pixel.bit_depth = dp->this.bit_depth;
+      if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
+         test_pixel.sample_depth = 8;
+      else
+         test_pixel.sample_depth = test_pixel.bit_depth;
+      /* Don't need sBIT here, but it must be set to non-zero to avoid
+       * arithmetic overflows.
+       */
+      test_pixel.have_tRNS = dp->this.is_transparent != 0;
+      test_pixel.red_sBIT = test_pixel.green_sBIT = test_pixel.blue_sBIT =
+         test_pixel.alpha_sBIT = test_pixel.sample_depth;
+
+      dp->transform_list->mod(dp->transform_list, &test_pixel, pp, dp);
+
+      if (test_pixel.colour_type != dp->output_colour_type)
+      {
+         char message[128];
+         size_t pos = safecat(message, sizeof message, 0, "colour type ");
+
+         pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
+         pos = safecat(message, sizeof message, pos, " expected ");
+         pos = safecatn(message, sizeof message, pos, test_pixel.colour_type);
+
+         png_error(pp, message);
+      }
+
+      if (test_pixel.bit_depth != dp->output_bit_depth)
+      {
+         char message[128];
+         size_t pos = safecat(message, sizeof message, 0, "bit depth ");
+
+         pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+         pos = safecat(message, sizeof message, pos, " expected ");
+         pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+         png_error(pp, message);
+      }
+
+      /* If both bit depth and colour type are correct check the sample depth.
+       */
+      if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE &&
+          test_pixel.sample_depth != 8) /* oops - internal error! */
+         png_error(pp, "pngvalid: internal: palette sample depth not 8");
+      else if (dp->unpacked && test_pixel.bit_depth != 8)
+         png_error(pp, "pngvalid: internal: bad unpacked pixel depth");
+      else if (!dp->unpacked && test_pixel.colour_type != PNG_COLOR_TYPE_PALETTE
+              && test_pixel.bit_depth != test_pixel.sample_depth)
+      {
+         char message[128];
+         size_t pos = safecat(message, sizeof message, 0,
+            "internal: sample depth ");
+
+         /* Because unless something has set 'unpacked' or the image is palette
+          * mapped we expect the transform to keep sample depth and bit depth
+          * the same.
+          */
+         pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
+         pos = safecat(message, sizeof message, pos, " expected ");
+         pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+         png_error(pp, message);
+      }
+      else if (test_pixel.bit_depth != dp->output_bit_depth)
+      {
+         /* This could be a libpng error too; libpng has not produced what we
+          * expect for the output bit depth.
+          */
+         char message[128];
+         size_t pos = safecat(message, sizeof message, 0,
+            "internal: bit depth ");
+
+         pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+         pos = safecat(message, sizeof message, pos, " expected ");
+         pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+         png_error(pp, message);
+      }
+   }
+}
+
+static void PNGCBAPI
+transform_info(png_structp pp, png_infop pi)
+{
+   transform_info_imp(voidcast(transform_display*, png_get_progressive_ptr(pp)),
+      pp, pi);
+}
+
+static void
+transform_range_check(png_const_structp pp, unsigned int r, unsigned int g,
+   unsigned int b, unsigned int a, unsigned int in_digitized, double in,
+   unsigned int out, png_byte sample_depth, double err, double limit,
+   const char *name, double digitization_error)
+{
+   /* Compare the scaled, digitzed, values of our local calculation (in+-err)
+    * with the digitized values libpng produced;  'sample_depth' is the actual
+    * digitization depth of the libpng output colors (the bit depth except for
+    * palette images where it is always 8.)  The check on 'err' is to detect
+    * internal errors in pngvalid itself.
+    */
+   unsigned int max = (1U<<sample_depth)-1;
+   double in_min = ceil((in-err)*max - digitization_error);
+   double in_max = floor((in+err)*max + digitization_error);
+   if (err > limit || !(out >= in_min && out <= in_max))
+   {
+      char message[256];
+      size_t pos;
+
+      pos = safecat(message, sizeof message, 0, name);
+      pos = safecat(message, sizeof message, pos, " output value error: rgba(");
+      pos = safecatn(message, sizeof message, pos, r);
+      pos = safecat(message, sizeof message, pos, ",");
+      pos = safecatn(message, sizeof message, pos, g);
+      pos = safecat(message, sizeof message, pos, ",");
+      pos = safecatn(message, sizeof message, pos, b);
+      pos = safecat(message, sizeof message, pos, ",");
+      pos = safecatn(message, sizeof message, pos, a);
+      pos = safecat(message, sizeof message, pos, "): ");
+      pos = safecatn(message, sizeof message, pos, out);
+      pos = safecat(message, sizeof message, pos, " expected: ");
+      pos = safecatn(message, sizeof message, pos, in_digitized);
+      pos = safecat(message, sizeof message, pos, " (");
+      pos = safecatd(message, sizeof message, pos, (in-err)*max, 3);
+      pos = safecat(message, sizeof message, pos, "..");
+      pos = safecatd(message, sizeof message, pos, (in+err)*max, 3);
+      pos = safecat(message, sizeof message, pos, ")");
+
+      png_error(pp, message);
+   }
+}
+
+static void
+transform_image_validate(transform_display *dp, png_const_structp pp,
+   png_infop pi)
+{
+   /* Constants for the loop below: */
+   const png_store* const ps = dp->this.ps;
+   const png_byte in_ct = dp->this.colour_type;
+   const png_byte in_bd = dp->this.bit_depth;
+   const png_uint_32 w = dp->this.w;
+   const png_uint_32 h = dp->this.h;
+   const png_byte out_ct = dp->output_colour_type;
+   const png_byte out_bd = dp->output_bit_depth;
+   const png_byte sample_depth = (png_byte)(out_ct ==
+      PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
+   const png_byte red_sBIT = dp->this.red_sBIT;
+   const png_byte green_sBIT = dp->this.green_sBIT;
+   const png_byte blue_sBIT = dp->this.blue_sBIT;
+   const png_byte alpha_sBIT = dp->this.alpha_sBIT;
+   const int have_tRNS = dp->this.is_transparent;
+   double digitization_error;
+
+   store_palette out_palette;
+   png_uint_32 y;
+
+   UNUSED(pi)
+
+   /* Check for row overwrite errors */
+   store_image_check(dp->this.ps, pp, 0);
+
+   /* Read the palette corresponding to the output if the output colour type
+    * indicates a palette, othewise set out_palette to garbage.
+    */
+   if (out_ct == PNG_COLOR_TYPE_PALETTE)
+   {
+      /* Validate that the palette count itself has not changed - this is not
+       * expected.
+       */
+      int npalette = (-1);
+
+      (void)read_palette(out_palette, &npalette, pp, pi);
+      if (npalette != dp->this.npalette)
+         png_error(pp, "unexpected change in palette size");
+
+      digitization_error = .5;
+   }
+   else
+   {
+      png_byte in_sample_depth;
+
+      memset(out_palette, 0x5e, sizeof out_palette);
+
+      /* use-input-precision means assume that if the input has 8 bit (or less)
+       * samples and the output has 16 bit samples the calculations will be done
+       * with 8 bit precision, not 16.
+       */
+      if (in_ct == PNG_COLOR_TYPE_PALETTE || in_bd < 16)
+         in_sample_depth = 8;
+      else
+         in_sample_depth = in_bd;
+
+      if (sample_depth != 16 || in_sample_depth > 8 ||
+         !dp->pm->calculations_use_input_precision)
+         digitization_error = .5;
+
+      /* Else calculations are at 8 bit precision, and the output actually
+       * consists of scaled 8-bit values, so scale .5 in 8 bits to the 16 bits:
+       */
+      else
+         digitization_error = .5 * 257;
+   }
+
+   for (y=0; y<h; ++y)
+   {
+      png_const_bytep const pRow = store_image_row(ps, pp, 0, y);
+      png_uint_32 x;
+
+      /* The original, standard, row pre-transforms. */
+      png_byte std[STANDARD_ROWMAX];
+
+      transform_row(pp, std, in_ct, in_bd, y);
+
+      /* Go through each original pixel transforming it and comparing with what
+       * libpng did to the same pixel.
+       */
+      for (x=0; x<w; ++x)
+      {
+         image_pixel in_pixel, out_pixel;
+         unsigned int r, g, b, a;
+
+         /* Find out what we think the pixel should be: */
+         image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette,
+                 NULL);
+
+         in_pixel.red_sBIT = red_sBIT;
+         in_pixel.green_sBIT = green_sBIT;
+         in_pixel.blue_sBIT = blue_sBIT;
+         in_pixel.alpha_sBIT = alpha_sBIT;
+         in_pixel.have_tRNS = have_tRNS != 0;
+
+         /* For error detection, below. */
+         r = in_pixel.red;
+         g = in_pixel.green;
+         b = in_pixel.blue;
+         a = in_pixel.alpha;
+
+         /* This applies the transforms to the input data, including output
+          * format operations which must be used when reading the output
+          * pixel that libpng produces.
+          */
+         dp->transform_list->mod(dp->transform_list, &in_pixel, pp, dp);
+
+         /* Read the output pixel and compare it to what we got, we don't
+          * use the error field here, so no need to update sBIT.  in_pixel
+          * says whether we expect libpng to change the output format.
+          */
+         image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette,
+                 &in_pixel);
+
+         /* We don't expect changes to the index here even if the bit depth is
+          * changed.
+          */
+         if (in_ct == PNG_COLOR_TYPE_PALETTE &&
+            out_ct == PNG_COLOR_TYPE_PALETTE)
+         {
+            if (in_pixel.palette_index != out_pixel.palette_index)
+               png_error(pp, "unexpected transformed palette index");
+         }
+
+         /* Check the colours for palette images too - in fact the palette could
+          * be separately verified itself in most cases.
+          */
+         if (in_pixel.red != out_pixel.red)
+            transform_range_check(pp, r, g, b, a, in_pixel.red, in_pixel.redf,
+               out_pixel.red, sample_depth, in_pixel.rede,
+               dp->pm->limit + 1./(2*((1U<<in_pixel.red_sBIT)-1)), "red/gray",
+               digitization_error);
+
+         if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
+            in_pixel.green != out_pixel.green)
+            transform_range_check(pp, r, g, b, a, in_pixel.green,
+               in_pixel.greenf, out_pixel.green, sample_depth, in_pixel.greene,
+               dp->pm->limit + 1./(2*((1U<<in_pixel.green_sBIT)-1)), "green",
+               digitization_error);
+
+         if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
+            in_pixel.blue != out_pixel.blue)
+            transform_range_check(pp, r, g, b, a, in_pixel.blue, in_pixel.bluef,
+               out_pixel.blue, sample_depth, in_pixel.bluee,
+               dp->pm->limit + 1./(2*((1U<<in_pixel.blue_sBIT)-1)), "blue",
+               digitization_error);
+
+         if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0 &&
+            in_pixel.alpha != out_pixel.alpha)
+            transform_range_check(pp, r, g, b, a, in_pixel.alpha,
+               in_pixel.alphaf, out_pixel.alpha, sample_depth, in_pixel.alphae,
+               dp->pm->limit + 1./(2*((1U<<in_pixel.alpha_sBIT)-1)), "alpha",
+               digitization_error);
+      } /* pixel (x) loop */
+   } /* row (y) loop */
+
+   /* Record that something was actually checked to avoid a false positive. */
+   dp->this.ps->validated = 1;
+}
+
+static void PNGCBAPI
+transform_end(png_structp ppIn, png_infop pi)
+{
+   png_const_structp pp = ppIn;
+   transform_display *dp = voidcast(transform_display*,
+      png_get_progressive_ptr(pp));
+
+   if (!dp->this.speed)
+      transform_image_validate(dp, pp, pi);
+   else
+      dp->this.ps->validated = 1;
+}
+
+/* A single test run. */
+static void
+transform_test(png_modifier *pmIn, const png_uint_32 idIn,
+    const image_transform* transform_listIn, const char * const name)
+{
+   transform_display d;
+   context(&pmIn->this, fault);
+
+   transform_display_init(&d, pmIn, idIn, transform_listIn);
+
+   Try
+   {
+      size_t pos = 0;
+      png_structp pp;
+      png_infop pi;
+      char full_name[256];
+
+      /* Make sure the encoding fields are correct and enter the required
+       * modifications.
+       */
+      transform_set_encoding(&d);
+
+      /* Add any modifications required by the transform list. */
+      d.transform_list->ini(d.transform_list, &d);
+
+      /* Add the color space information, if any, to the name. */
+      pos = safecat(full_name, sizeof full_name, pos, name);
+      pos = safecat_current_encoding(full_name, sizeof full_name, pos, d.pm);
+
+      /* Get a png_struct for reading the image. */
+      pp = set_modifier_for_read(d.pm, &pi, d.this.id, full_name);
+      standard_palette_init(&d.this);
+
+#     if 0
+         /* Logging (debugging only) */
+         {
+            char buffer[256];
+
+            (void)store_message(&d.pm->this, pp, buffer, sizeof buffer, 0,
+               "running test");
+
+            fprintf(stderr, "%s\n", buffer);
+         }
+#     endif
+
+      /* Introduce the correct read function. */
+      if (d.pm->this.progressive)
+      {
+         /* Share the row function with the standard implementation. */
+         png_set_progressive_read_fn(pp, &d, transform_info, progressive_row,
+            transform_end);
+
+         /* Now feed data into the reader until we reach the end: */
+         modifier_progressive_read(d.pm, pp, pi);
+      }
+      else
+      {
+         /* modifier_read expects a png_modifier* */
+         png_set_read_fn(pp, d.pm, modifier_read);
+
+         /* Check the header values: */
+         png_read_info(pp, pi);
+
+         /* Process the 'info' requirements. Only one image is generated */
+         transform_info_imp(&d, pp, pi);
+
+         sequential_row(&d.this, pp, pi, -1, 0);
+
+         if (!d.this.speed)
+            transform_image_validate(&d, pp, pi);
+         else
+            d.this.ps->validated = 1;
+      }
+
+      modifier_reset(d.pm);
+   }
+
+   Catch(fault)
+   {
+      modifier_reset(voidcast(png_modifier*,(void*)fault));
+   }
+}
+
+/* The transforms: */
+#define ITSTRUCT(name) image_transform_##name
+#define ITDATA(name) image_transform_data_##name
+#define image_transform_ini image_transform_default_ini
+#define IT(name)\
+static image_transform ITSTRUCT(name) =\
+{\
+   #name,\
+   1, /*enable*/\
+   &PT, /*list*/\
+   0, /*global_use*/\
+   0, /*local_use*/\
+   0, /*next*/\
+   image_transform_ini,\
+   image_transform_png_set_##name##_set,\
+   image_transform_png_set_##name##_mod,\
+   image_transform_png_set_##name##_add\
+}
+#define PT ITSTRUCT(end) /* stores the previous transform */
+
+/* To save code: */
+extern void image_transform_default_ini(const image_transform *this,
+   transform_display *that); /* silence GCC warnings */
+
+void /* private, but almost always needed */
+image_transform_default_ini(const image_transform *this,
+    transform_display *that)
+{
+   this->next->ini(this->next, that);
+}
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+static int
+image_transform_default_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return 1;
+}
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_palette_to_rgb */
+static void
+image_transform_png_set_palette_to_rgb_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_palette_to_rgb(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_palette_to_rgb_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+      image_pixel_convert_PLTE(that);
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_palette_to_rgb_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return colour_type == PNG_COLOR_TYPE_PALETTE;
+}
+
+IT(palette_to_rgb);
+#undef PT
+#define PT ITSTRUCT(palette_to_rgb)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_tRNS_to_alpha */
+static void
+image_transform_png_set_tRNS_to_alpha_set(const image_transform *this,
+   transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_tRNS_to_alpha(pp);
+
+   /* If there was a tRNS chunk that would get expanded and add an alpha
+    * channel is_transparent must be updated:
+    */
+   if (that->this.has_tRNS)
+      that->this.is_transparent = 1;
+
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_tRNS_to_alpha_mod(const image_transform *this,
+   image_pixel *that, png_const_structp pp,
+   const transform_display *display)
+{
+#if PNG_LIBPNG_VER < 10700
+   /* LIBPNG BUG: this always forces palette images to RGB. */
+   if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+      image_pixel_convert_PLTE(that);
+#endif
+
+   /* This effectively does an 'expand' only if there is some transparency to
+    * convert to an alpha channel.
+    */
+   if (that->have_tRNS)
+#     if PNG_LIBPNG_VER >= 10700
+         if (that->colour_type != PNG_COLOR_TYPE_PALETTE &&
+             (that->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
+#     endif
+      image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+#if PNG_LIBPNG_VER < 10700
+   /* LIBPNG BUG: otherwise libpng still expands to 8 bits! */
+   else
+   {
+      if (that->bit_depth < 8)
+         that->bit_depth =8;
+      if (that->sample_depth < 8)
+         that->sample_depth = 8;
+   }
+#endif
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_tRNS_to_alpha_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   /* We don't know yet whether there will be a tRNS chunk, but we know that
+    * this transformation should do nothing if there already is an alpha
+    * channel.  In addition, after the bug fix in 1.7.0, there is no longer
+    * any action on a palette image.
+    */
+   return
+#  if PNG_LIBPNG_VER >= 10700
+      colour_type != PNG_COLOR_TYPE_PALETTE &&
+#  endif
+   (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
+}
+
+IT(tRNS_to_alpha);
+#undef PT
+#define PT ITSTRUCT(tRNS_to_alpha)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+/* png_set_gray_to_rgb */
+static void
+image_transform_png_set_gray_to_rgb_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_gray_to_rgb(pp);
+   /* NOTE: this doesn't result in tRNS expansion. */
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_gray_to_rgb_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   /* NOTE: we can actually pend the tRNS processing at this point because we
+    * can correctly recognize the original pixel value even though we have
+    * mapped the one gray channel to the three RGB ones, but in fact libpng
+    * doesn't do this, so we don't either.
+    */
+   if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS)
+      image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+   /* Simply expand the bit depth and alter the colour type as required. */
+   if (that->colour_type == PNG_COLOR_TYPE_GRAY)
+   {
+      /* RGB images have a bit depth at least equal to '8' */
+      if (that->bit_depth < 8)
+         that->sample_depth = that->bit_depth = 8;
+
+      /* And just changing the colour type works here because the green and blue
+       * channels are being maintained in lock-step with the red/gray:
+       */
+      that->colour_type = PNG_COLOR_TYPE_RGB;
+   }
+
+   else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      that->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_gray_to_rgb_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return (colour_type & PNG_COLOR_MASK_COLOR) == 0;
+}
+
+IT(gray_to_rgb);
+#undef PT
+#define PT ITSTRUCT(gray_to_rgb)
+#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_expand */
+static void
+image_transform_png_set_expand_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_expand(pp);
+
+   if (that->this.has_tRNS)
+      that->this.is_transparent = 1;
+
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   /* The general expand case depends on what the colour type is: */
+   if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+      image_pixel_convert_PLTE(that);
+   else if (that->bit_depth < 8) /* grayscale */
+      that->sample_depth = that->bit_depth = 8;
+
+   if (that->have_tRNS)
+      image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   /* 'expand' should do nothing for RGBA or GA input - no tRNS and the bit
+    * depth is at least 8 already.
+    */
+   return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
+}
+
+IT(expand);
+#undef PT
+#define PT ITSTRUCT(expand)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_expand_gray_1_2_4_to_8
+ * Pre 1.7.0 LIBPNG BUG: this just does an 'expand'
+ */
+static void
+image_transform_png_set_expand_gray_1_2_4_to_8_set(
+    const image_transform *this, transform_display *that, png_structp pp,
+    png_infop pi)
+{
+   png_set_expand_gray_1_2_4_to_8(pp);
+   /* NOTE: don't expect this to expand tRNS */
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_gray_1_2_4_to_8_mod(
+    const image_transform *this, image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+#if PNG_LIBPNG_VER < 10700
+   image_transform_png_set_expand_mod(this, that, pp, display);
+#else
+   /* Only expand grayscale of bit depth less than 8: */
+   if (that->colour_type == PNG_COLOR_TYPE_GRAY &&
+       that->bit_depth < 8)
+      that->sample_depth = that->bit_depth = 8;
+
+   this->next->mod(this->next, that, pp, display);
+#endif /* 1.7 or later */
+}
+
+static int
+image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+#if PNG_LIBPNG_VER < 10700
+   return image_transform_png_set_expand_add(this, that, colour_type,
+      bit_depth);
+#else
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   /* This should do nothing unless the color type is gray and the bit depth is
+    * less than 8:
+    */
+   return colour_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8;
+#endif /* 1.7 or later */
+}
+
+IT(expand_gray_1_2_4_to_8);
+#undef PT
+#define PT ITSTRUCT(expand_gray_1_2_4_to_8)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* png_set_expand_16 */
+static void
+image_transform_png_set_expand_16_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_expand_16(pp);
+
+   /* NOTE: prior to 1.7 libpng does SET_EXPAND as well, so tRNS is expanded. */
+#  if PNG_LIBPNG_VER < 10700
+      if (that->this.has_tRNS)
+         that->this.is_transparent = 1;
+#  endif
+
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_16_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   /* Expect expand_16 to expand everything to 16 bits as a result of also
+    * causing 'expand' to happen.
+    */
+   if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+      image_pixel_convert_PLTE(that);
+
+   if (that->have_tRNS)
+      image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+   if (that->bit_depth < 16)
+      that->sample_depth = that->bit_depth = 16;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_16_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   /* expand_16 does something unless the bit depth is already 16. */
+   return bit_depth < 16;
+}
+
+IT(expand_16);
+#undef PT
+#define PT ITSTRUCT(expand_16)
+#endif /* PNG_READ_EXPAND_16_SUPPORTED */
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED  /* API added in 1.5.4 */
+/* png_set_scale_16 */
+static void
+image_transform_png_set_scale_16_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_scale_16(pp);
+#  if PNG_LIBPNG_VER < 10700
+      /* libpng will limit the gamma table size: */
+      that->max_gamma_8 = PNG_MAX_GAMMA_8;
+#  endif
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_scale_16_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->bit_depth == 16)
+   {
+      that->sample_depth = that->bit_depth = 8;
+      if (that->red_sBIT > 8) that->red_sBIT = 8;
+      if (that->green_sBIT > 8) that->green_sBIT = 8;
+      if (that->blue_sBIT > 8) that->blue_sBIT = 8;
+      if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
+   }
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_scale_16_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   return bit_depth > 8;
+}
+
+IT(scale_16);
+#undef PT
+#define PT ITSTRUCT(scale_16)
+#endif /* PNG_READ_SCALE_16_TO_8_SUPPORTED (1.5.4 on) */
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED /* the default before 1.5.4 */
+/* png_set_strip_16 */
+static void
+image_transform_png_set_strip_16_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_strip_16(pp);
+#  if PNG_LIBPNG_VER < 10700
+      /* libpng will limit the gamma table size: */
+      that->max_gamma_8 = PNG_MAX_GAMMA_8;
+#  endif
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_strip_16_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->bit_depth == 16)
+   {
+      that->sample_depth = that->bit_depth = 8;
+      if (that->red_sBIT > 8) that->red_sBIT = 8;
+      if (that->green_sBIT > 8) that->green_sBIT = 8;
+      if (that->blue_sBIT > 8) that->blue_sBIT = 8;
+      if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
+
+      /* Prior to 1.5.4 png_set_strip_16 would use an 'accurate' method if this
+       * configuration option is set.  From 1.5.4 the flag is never set and the
+       * 'scale' API (above) must be used.
+       */
+#     ifdef PNG_READ_ACCURATE_SCALE_SUPPORTED
+#        if PNG_LIBPNG_VER >= 10504
+#           error PNG_READ_ACCURATE_SCALE should not be set
+#        endif
+
+         /* The strip 16 algorithm drops the low 8 bits rather than calculating
+          * 1/257, so we need to adjust the permitted errors appropriately:
+          * Notice that this is only relevant prior to the addition of the
+          * png_set_scale_16 API in 1.5.4 (but 1.5.4+ always defines the above!)
+          */
+         {
+            const double d = (255-128.5)/65535;
+            that->rede += d;
+            that->greene += d;
+            that->bluee += d;
+            that->alphae += d;
+         }
+#     endif
+   }
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_strip_16_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   return bit_depth > 8;
+}
+
+IT(strip_16);
+#undef PT
+#define PT ITSTRUCT(strip_16)
+#endif /* PNG_READ_16_TO_8_SUPPORTED */
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+/* png_set_strip_alpha */
+static void
+image_transform_png_set_strip_alpha_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_strip_alpha(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_strip_alpha_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      that->colour_type = PNG_COLOR_TYPE_GRAY;
+   else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      that->colour_type = PNG_COLOR_TYPE_RGB;
+
+   that->have_tRNS = 0;
+   that->alphaf = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_strip_alpha_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return (colour_type & PNG_COLOR_MASK_ALPHA) != 0;
+}
+
+IT(strip_alpha);
+#undef PT
+#define PT ITSTRUCT(strip_alpha)
+#endif /* PNG_READ_STRIP_ALPHA_SUPPORTED */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* png_set_rgb_to_gray(png_structp, int err_action, double red, double green)
+ * png_set_rgb_to_gray_fixed(png_structp, int err_action, png_fixed_point red,
+ *    png_fixed_point green)
+ * png_get_rgb_to_gray_status
+ *
+ * The 'default' test here uses values known to be used inside libpng prior to
+ * 1.7.0:
+ *
+ *   red:    6968
+ *   green: 23434
+ *   blue:   2366
+ *
+ * These values are being retained for compatibility, along with the somewhat
+ * broken truncation calculation in the fast-and-inaccurate code path.  Older
+ * versions of libpng will fail the accuracy tests below because they use the
+ * truncation algorithm everywhere.
+ */
+#define data ITDATA(rgb_to_gray)
+static struct
+{
+   double gamma;      /* File gamma to use in processing */
+
+   /* The following are the parameters for png_set_rgb_to_gray: */
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      double red_to_set;
+      double green_to_set;
+#  else
+      png_fixed_point red_to_set;
+      png_fixed_point green_to_set;
+#  endif
+
+   /* The actual coefficients: */
+   double red_coefficient;
+   double green_coefficient;
+   double blue_coefficient;
+
+   /* Set if the coeefficients have been overridden. */
+   int coefficients_overridden;
+} data;
+
+#undef image_transform_ini
+#define image_transform_ini image_transform_png_set_rgb_to_gray_ini
+static void
+image_transform_png_set_rgb_to_gray_ini(const image_transform *this,
+    transform_display *that)
+{
+   png_modifier *pm = that->pm;
+   const color_encoding *e = pm->current_encoding;
+
+   UNUSED(this)
+
+   /* Since we check the encoding this flag must be set: */
+   pm->test_uses_encoding = 1;
+
+   /* If 'e' is not NULL chromaticity information is present and either a cHRM
+    * or an sRGB chunk will be inserted.
+    */
+   if (e != 0)
+   {
+      /* Coefficients come from the encoding, but may need to be normalized to a
+       * white point Y of 1.0
+       */
+      const double whiteY = e->red.Y + e->green.Y + e->blue.Y;
+
+      data.red_coefficient = e->red.Y;
+      data.green_coefficient = e->green.Y;
+      data.blue_coefficient = e->blue.Y;
+
+      if (whiteY != 1)
+      {
+         data.red_coefficient /= whiteY;
+         data.green_coefficient /= whiteY;
+         data.blue_coefficient /= whiteY;
+      }
+   }
+
+   else
+   {
+      /* The default (built in) coeffcients, as above: */
+#     if PNG_LIBPNG_VER < 10700
+         data.red_coefficient = 6968 / 32768.;
+         data.green_coefficient = 23434 / 32768.;
+         data.blue_coefficient = 2366 / 32768.;
+#     else
+         data.red_coefficient = .2126;
+         data.green_coefficient = .7152;
+         data.blue_coefficient = .0722;
+#     endif
+   }
+
+   data.gamma = pm->current_gamma;
+
+   /* If not set then the calculations assume linear encoding (implicitly): */
+   if (data.gamma == 0)
+      data.gamma = 1;
+
+   /* The arguments to png_set_rgb_to_gray can override the coefficients implied
+    * by the color space encoding.  If doing exhaustive checks do the override
+    * in each case, otherwise do it randomly.
+    */
+   if (pm->test_exhaustive)
+   {
+      /* First time in coefficients_overridden is 0, the following sets it to 1,
+       * so repeat if it is set.  If a test fails this may mean we subsequently
+       * skip a non-override test, ignore that.
+       */
+      data.coefficients_overridden = !data.coefficients_overridden;
+      pm->repeat = data.coefficients_overridden != 0;
+   }
+
+   else
+      data.coefficients_overridden = random_choice();
+
+   if (data.coefficients_overridden)
+   {
+      /* These values override the color encoding defaults, simply use random
+       * numbers.
+       */
+      png_uint_32 ru;
+      double total;
+
+      RANDOMIZE(ru);
+      data.green_coefficient = total = (ru & 0xffff) / 65535.;
+      ru >>= 16;
+      data.red_coefficient = (1 - total) * (ru & 0xffff) / 65535.;
+      total += data.red_coefficient;
+      data.blue_coefficient = 1 - total;
+
+#     ifdef PNG_FLOATING_POINT_SUPPORTED
+         data.red_to_set = data.red_coefficient;
+         data.green_to_set = data.green_coefficient;
+#     else
+         data.red_to_set = fix(data.red_coefficient);
+         data.green_to_set = fix(data.green_coefficient);
+#     endif
+
+      /* The following just changes the error messages: */
+      pm->encoding_ignored = 1;
+   }
+
+   else
+   {
+      data.red_to_set = -1;
+      data.green_to_set = -1;
+   }
+
+   /* Adjust the error limit in the png_modifier because of the larger errors
+    * produced in the digitization during the gamma handling.
+    */
+   if (data.gamma != 1) /* Use gamma tables */
+   {
+      if (that->this.bit_depth == 16 || pm->assume_16_bit_calculations)
+      {
+         /* The computations have the form:
+          *
+          *    r * rc + g * gc + b * bc
+          *
+          *  Each component of which is +/-1/65535 from the gamma_to_1 table
+          *  lookup, resulting in a base error of +/-6.  The gamma_from_1
+          *  conversion adds another +/-2 in the 16-bit case and
+          *  +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case.
+          */
+#        if PNG_LIBPNG_VER < 10700
+            if (that->this.bit_depth < 16)
+               that->max_gamma_8 = PNG_MAX_GAMMA_8;
+#        endif
+         that->pm->limit += pow(
+            (that->this.bit_depth == 16 || that->max_gamma_8 > 14 ?
+               8. :
+               6. + (1<<(15-that->max_gamma_8))
+            )/65535, data.gamma);
+      }
+
+      else
+      {
+         /* Rounding to 8 bits in the linear space causes massive errors which
+          * will trigger the error check in transform_range_check.  Fix that
+          * here by taking the gamma encoding into account.
+          *
+          * When DIGITIZE is set because a pre-1.7 version of libpng is being
+          * tested allow a bigger slack.
+          *
+          * NOTE: this magic number was determined by experiment to be about
+          * 1.263.  There's no great merit to the value below, however it only
+          * affects the limit used for checking for internal calculation errors,
+          * not the actual limit imposed by pngvalid on the output errors.
+          */
+         that->pm->limit += pow(
+#        if DIGITIZE
+            1.3
+#        else
+            1.0
+#        endif
+            /255, data.gamma);
+      }
+   }
+
+   else
+   {
+      /* With no gamma correction a large error comes from the truncation of the
+       * calculation in the 8 bit case, allow for that here.
+       */
+      if (that->this.bit_depth != 16 && !pm->assume_16_bit_calculations)
+         that->pm->limit += 4E-3;
+   }
+}
+
+static void
+image_transform_png_set_rgb_to_gray_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   const int error_action = 1; /* no error, no defines in png.h */
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      png_set_rgb_to_gray(pp, error_action, data.red_to_set, data.green_to_set);
+#  else
+      png_set_rgb_to_gray_fixed(pp, error_action, data.red_to_set,
+         data.green_to_set);
+#  endif
+
+#  ifdef PNG_READ_cHRM_SUPPORTED
+      if (that->pm->current_encoding != 0)
+      {
+         /* We have an encoding so a cHRM chunk may have been set; if so then
+          * check that the libpng APIs give the correct (X,Y,Z) values within
+          * some margin of error for the round trip through the chromaticity
+          * form.
+          */
+#        ifdef PNG_FLOATING_POINT_SUPPORTED
+#           define API_function png_get_cHRM_XYZ
+#           define API_form "FP"
+#           define API_type double
+#           define API_cvt(x) (x)
+#        else
+#           define API_function png_get_cHRM_XYZ_fixed
+#           define API_form "fixed"
+#           define API_type png_fixed_point
+#           define API_cvt(x) ((double)(x)/PNG_FP_1)
+#        endif
+
+         API_type rX, gX, bX;
+         API_type rY, gY, bY;
+         API_type rZ, gZ, bZ;
+
+         if ((API_function(pp, pi, &rX, &rY, &rZ, &gX, &gY, &gZ, &bX, &bY, &bZ)
+               & PNG_INFO_cHRM) != 0)
+         {
+            double maxe;
+            const char *el;
+            color_encoding e, o;
+
+            /* Expect libpng to return a normalized result, but the original
+             * color space encoding may not be normalized.
+             */
+            modifier_current_encoding(that->pm, &o);
+            normalize_color_encoding(&o);
+
+            /* Sanity check the pngvalid code - the coefficients should match
+             * the normalized Y values of the encoding unless they were
+             * overridden.
+             */
+            if (data.red_to_set == -1 && data.green_to_set == -1 &&
+               (fabs(o.red.Y - data.red_coefficient) > DBL_EPSILON ||
+               fabs(o.green.Y - data.green_coefficient) > DBL_EPSILON ||
+               fabs(o.blue.Y - data.blue_coefficient) > DBL_EPSILON))
+               png_error(pp, "internal pngvalid cHRM coefficient error");
+
+            /* Generate a colour space encoding. */
+            e.gamma = o.gamma; /* not used */
+            e.red.X = API_cvt(rX);
+            e.red.Y = API_cvt(rY);
+            e.red.Z = API_cvt(rZ);
+            e.green.X = API_cvt(gX);
+            e.green.Y = API_cvt(gY);
+            e.green.Z = API_cvt(gZ);
+            e.blue.X = API_cvt(bX);
+            e.blue.Y = API_cvt(bY);
+            e.blue.Z = API_cvt(bZ);
+
+            /* This should match the original one from the png_modifier, within
+             * the range permitted by the libpng fixed point representation.
+             */
+            maxe = 0;
+            el = "-"; /* Set to element name with error */
+
+#           define CHECK(col,x)\
+            {\
+               double err = fabs(o.col.x - e.col.x);\
+               if (err > maxe)\
+               {\
+                  maxe = err;\
+                  el = #col "(" #x ")";\
+               }\
+            }
+
+            CHECK(red,X)
+            CHECK(red,Y)
+            CHECK(red,Z)
+            CHECK(green,X)
+            CHECK(green,Y)
+            CHECK(green,Z)
+            CHECK(blue,X)
+            CHECK(blue,Y)
+            CHECK(blue,Z)
+
+            /* Here in both fixed and floating cases to check the values read
+             * from the cHRm chunk.  PNG uses fixed point in the cHRM chunk, so
+             * we can't expect better than +/-.5E-5 on the result, allow 1E-5.
+             */
+            if (maxe >= 1E-5)
+            {
+               size_t pos = 0;
+               char buffer[256];
+
+               pos = safecat(buffer, sizeof buffer, pos, API_form);
+               pos = safecat(buffer, sizeof buffer, pos, " cHRM ");
+               pos = safecat(buffer, sizeof buffer, pos, el);
+               pos = safecat(buffer, sizeof buffer, pos, " error: ");
+               pos = safecatd(buffer, sizeof buffer, pos, maxe, 7);
+               pos = safecat(buffer, sizeof buffer, pos, " ");
+               /* Print the color space without the gamma value: */
+               pos = safecat_color_encoding(buffer, sizeof buffer, pos, &o, 0);
+               pos = safecat(buffer, sizeof buffer, pos, " -> ");
+               pos = safecat_color_encoding(buffer, sizeof buffer, pos, &e, 0);
+
+               png_error(pp, buffer);
+            }
+         }
+      }
+#  endif /* READ_cHRM */
+
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_rgb_to_gray_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
+   {
+      double gray, err;
+
+#     if PNG_LIBPNG_VER < 10700
+         if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+            image_pixel_convert_PLTE(that);
+#     endif
+
+      /* Image now has RGB channels... */
+#  if DIGITIZE
+      {
+         const png_modifier *pm = display->pm;
+         const unsigned int sample_depth = that->sample_depth;
+         const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
+            sample_depth);
+         const unsigned int gamma_depth =
+            (sample_depth == 16 ?
+               display->max_gamma_8 :
+               (pm->assume_16_bit_calculations ?
+                  display->max_gamma_8 :
+                  sample_depth));
+         int isgray;
+         double r, g, b;
+         double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi;
+
+         /* Do this using interval arithmetic, otherwise it is too difficult to
+          * handle the errors correctly.
+          *
+          * To handle the gamma correction work out the upper and lower bounds
+          * of the digitized value.  Assume rounding here - normally the values
+          * will be identical after this operation if there is only one
+          * transform, feel free to delete the png_error checks on this below in
+          * the future (this is just me trying to ensure it works!)
+          *
+          * Interval arithmetic is exact, but to implement it it must be
+          * possible to control the floating point implementation rounding mode.
+          * This cannot be done in ANSI-C, so instead I reduce the 'lo' values
+          * by DBL_EPSILON and increase the 'hi' values by the same.
+          */
+#        define DD(v,d,r) (digitize(v*(1-DBL_EPSILON), d, r) * (1-DBL_EPSILON))
+#        define DU(v,d,r) (digitize(v*(1+DBL_EPSILON), d, r) * (1+DBL_EPSILON))
+
+         r = rlo = rhi = that->redf;
+         rlo -= that->rede;
+         rlo = DD(rlo, calc_depth, 1/*round*/);
+         rhi += that->rede;
+         rhi = DU(rhi, calc_depth, 1/*round*/);
+
+         g = glo = ghi = that->greenf;
+         glo -= that->greene;
+         glo = DD(glo, calc_depth, 1/*round*/);
+         ghi += that->greene;
+         ghi = DU(ghi, calc_depth, 1/*round*/);
+
+         b = blo = bhi = that->bluef;
+         blo -= that->bluee;
+         blo = DD(blo, calc_depth, 1/*round*/);
+         bhi += that->bluee;
+         bhi = DU(bhi, calc_depth, 1/*round*/);
+
+         isgray = r==g && g==b;
+
+         if (data.gamma != 1)
+         {
+            const double power = 1/data.gamma;
+            const double abse = .5/(sample_depth == 16 ? 65535 : 255);
+
+            /* If a gamma calculation is done it is done using lookup tables of
+             * precision gamma_depth, so the already digitized value above may
+             * need to be further digitized here.
+             */
+            if (gamma_depth != calc_depth)
+            {
+               rlo = DD(rlo, gamma_depth, 0/*truncate*/);
+               rhi = DU(rhi, gamma_depth, 0/*truncate*/);
+               glo = DD(glo, gamma_depth, 0/*truncate*/);
+               ghi = DU(ghi, gamma_depth, 0/*truncate*/);
+               blo = DD(blo, gamma_depth, 0/*truncate*/);
+               bhi = DU(bhi, gamma_depth, 0/*truncate*/);
+            }
+
+            /* 'abse' is the error in the gamma table calculation itself. */
+            r = pow(r, power);
+            rlo = DD(pow(rlo, power)-abse, calc_depth, 1);
+            rhi = DU(pow(rhi, power)+abse, calc_depth, 1);
+
+            g = pow(g, power);
+            glo = DD(pow(glo, power)-abse, calc_depth, 1);
+            ghi = DU(pow(ghi, power)+abse, calc_depth, 1);
+
+            b = pow(b, power);
+            blo = DD(pow(blo, power)-abse, calc_depth, 1);
+            bhi = DU(pow(bhi, power)+abse, calc_depth, 1);
+         }
+
+         /* Now calculate the actual gray values.  Although the error in the
+          * coefficients depends on whether they were specified on the command
+          * line (in which case truncation to 15 bits happened) or not (rounding
+          * was used) the maxium error in an individual coefficient is always
+          * 2/32768, because even in the rounding case the requirement that
+          * coefficients add up to 32768 can cause a larger rounding error.
+          *
+          * The only time when rounding doesn't occur in 1.5.5 and later is when
+          * the non-gamma code path is used for less than 16 bit data.
+          */
+         gray = r * data.red_coefficient + g * data.green_coefficient +
+            b * data.blue_coefficient;
+
+         {
+            const int do_round = data.gamma != 1 || calc_depth == 16;
+            const double ce = 2. / 32768;
+
+            graylo = DD(rlo * (data.red_coefficient-ce) +
+               glo * (data.green_coefficient-ce) +
+               blo * (data.blue_coefficient-ce), calc_depth, do_round);
+            if (graylo > gray) /* always accept the right answer */
+               graylo = gray;
+
+            grayhi = DU(rhi * (data.red_coefficient+ce) +
+               ghi * (data.green_coefficient+ce) +
+               bhi * (data.blue_coefficient+ce), calc_depth, do_round);
+            if (grayhi < gray)
+               grayhi = gray;
+         }
+
+         /* And invert the gamma. */
+         if (data.gamma != 1)
+         {
+            const double power = data.gamma;
+
+            /* And this happens yet again, shifting the values once more. */
+            if (gamma_depth != sample_depth)
+            {
+               rlo = DD(rlo, gamma_depth, 0/*truncate*/);
+               rhi = DU(rhi, gamma_depth, 0/*truncate*/);
+               glo = DD(glo, gamma_depth, 0/*truncate*/);
+               ghi = DU(ghi, gamma_depth, 0/*truncate*/);
+               blo = DD(blo, gamma_depth, 0/*truncate*/);
+               bhi = DU(bhi, gamma_depth, 0/*truncate*/);
+            }
+
+            gray = pow(gray, power);
+            graylo = DD(pow(graylo, power), sample_depth, 1);
+            grayhi = DU(pow(grayhi, power), sample_depth, 1);
+         }
+
+#        undef DD
+#        undef DU
+
+         /* Now the error can be calculated.
+          *
+          * If r==g==b because there is no overall gamma correction libpng
+          * currently preserves the original value.
+          */
+         if (isgray)
+            err = (that->rede + that->greene + that->bluee)/3;
+
+         else
+         {
+            err = fabs(grayhi-gray);
+            if (fabs(gray - graylo) > err)
+               err = fabs(graylo-gray);
+
+            /* Check that this worked: */
+            if (err > pm->limit)
+            {
+               size_t pos = 0;
+               char buffer[128];
+
+               pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error ");
+               pos = safecatd(buffer, sizeof buffer, pos, err, 6);
+               pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
+               pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
+               png_error(pp, buffer);
+            }
+         }
+      }
+#  else  /* DIGITIZE */
+      {
+         double r = that->redf;
+         double re = that->rede;
+         double g = that->greenf;
+         double ge = that->greene;
+         double b = that->bluef;
+         double be = that->bluee;
+
+#        if PNG_LIBPNG_VER < 10700
+            /* The true gray case involves no math in earlier versions (not
+             * true, there was some if gamma correction was happening too.)
+             */
+            if (r == g && r == b)
+            {
+               gray = r;
+               err = re;
+               if (err < ge) err = ge;
+               if (err < be) err = be;
+            }
+
+            else
+#        endif /* before 1.7 */
+         if (data.gamma == 1)
+         {
+            /* There is no need to do the conversions to and from linear space,
+             * so the calculation should be a lot more accurate.  There is a
+             * built in error in the coefficients because they only have 15 bits
+             * and are adjusted to make sure they add up to 32768.  This
+             * involves a integer calculation with truncation of the form:
+             *
+             *     ((int)(coefficient * 100000) * 32768)/100000
+             *
+             * This is done to the red and green coefficients (the ones
+             * provided to the API) then blue is calculated from them so the
+             * result adds up to 32768.  In the worst case this can result in
+             * a -1 error in red and green and a +2 error in blue.  Consequently
+             * the worst case in the calculation below is 2/32768 error.
+             *
+             * TODO: consider fixing this in libpng by rounding the calculation
+             * limiting the error to 1/32768.
+             *
+             * Handling this by adding 2/32768 here avoids needing to increase
+             * the global error limits to take this into account.)
+             */
+            gray = r * data.red_coefficient + g * data.green_coefficient +
+               b * data.blue_coefficient;
+            err = re * data.red_coefficient + ge * data.green_coefficient +
+               be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON;
+         }
+
+         else
+         {
+            /* The calculation happens in linear space, and this produces much
+             * wider errors in the encoded space.  These are handled here by
+             * factoring the errors in to the calculation.  There are two table
+             * lookups in the calculation and each introduces a quantization
+             * error defined by the table size.
+             */
+            const png_modifier *pm = display->pm;
+            double in_qe = (that->sample_depth > 8 ? .5/65535 : .5/255);
+            double out_qe = (that->sample_depth > 8 ? .5/65535 :
+               (pm->assume_16_bit_calculations ? .5/(1<<display->max_gamma_8) :
+               .5/255));
+            double rhi, ghi, bhi, grayhi;
+            double g1 = 1/data.gamma;
+
+            rhi = r + re + in_qe; if (rhi > 1) rhi = 1;
+            r -= re + in_qe; if (r < 0) r = 0;
+            ghi = g + ge + in_qe; if (ghi > 1) ghi = 1;
+            g -= ge + in_qe; if (g < 0) g = 0;
+            bhi = b + be + in_qe; if (bhi > 1) bhi = 1;
+            b -= be + in_qe; if (b < 0) b = 0;
+
+            r = pow(r, g1)*(1-DBL_EPSILON); rhi = pow(rhi, g1)*(1+DBL_EPSILON);
+            g = pow(g, g1)*(1-DBL_EPSILON); ghi = pow(ghi, g1)*(1+DBL_EPSILON);
+            b = pow(b, g1)*(1-DBL_EPSILON); bhi = pow(bhi, g1)*(1+DBL_EPSILON);
+
+            /* Work out the lower and upper bounds for the gray value in the
+             * encoded space, then work out an average and error.  Remove the
+             * previously added input quantization error at this point.
+             */
+            gray = r * data.red_coefficient + g * data.green_coefficient +
+               b * data.blue_coefficient - 2./32768 - out_qe;
+            if (gray <= 0)
+               gray = 0;
+            else
+            {
+               gray *= (1 - 6 * DBL_EPSILON);
+               gray = pow(gray, data.gamma) * (1-DBL_EPSILON);
+            }
+
+            grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient +
+               bhi * data.blue_coefficient + 2./32768 + out_qe;
+            grayhi *= (1 + 6 * DBL_EPSILON);
+            if (grayhi >= 1)
+               grayhi = 1;
+            else
+               grayhi = pow(grayhi, data.gamma) * (1+DBL_EPSILON);
+
+            err = (grayhi - gray) / 2;
+            gray = (grayhi + gray) / 2;
+
+            if (err <= in_qe)
+               err = gray * DBL_EPSILON;
+
+            else
+               err -= in_qe;
+
+            /* Validate that the error is within limits (this has caused
+             * problems before, it's much easier to detect them here.)
+             */
+            if (err > pm->limit)
+            {
+               size_t pos = 0;
+               char buffer[128];
+
+               pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error ");
+               pos = safecatd(buffer, sizeof buffer, pos, err, 6);
+               pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
+               pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
+               png_error(pp, buffer);
+            }
+         }
+      }
+#  endif /* !DIGITIZE */
+
+      that->bluef = that->greenf = that->redf = gray;
+      that->bluee = that->greene = that->rede = err;
+
+      /* The sBIT is the minium of the three colour channel sBITs. */
+      if (that->red_sBIT > that->green_sBIT)
+         that->red_sBIT = that->green_sBIT;
+      if (that->red_sBIT > that->blue_sBIT)
+         that->red_sBIT = that->blue_sBIT;
+      that->blue_sBIT = that->green_sBIT = that->red_sBIT;
+
+      /* And remove the colour bit in the type: */
+      if (that->colour_type == PNG_COLOR_TYPE_RGB)
+         that->colour_type = PNG_COLOR_TYPE_GRAY;
+      else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         that->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+   }
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_rgb_to_gray_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return (colour_type & PNG_COLOR_MASK_COLOR) != 0;
+}
+
+#undef data
+IT(rgb_to_gray);
+#undef PT
+#define PT ITSTRUCT(rgb_to_gray)
+#undef image_transform_ini
+#define image_transform_ini image_transform_default_ini
+#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* png_set_background(png_structp, png_const_color_16p background_color,
+ *    int background_gamma_code, int need_expand, double background_gamma)
+ * png_set_background_fixed(png_structp, png_const_color_16p background_color,
+ *    int background_gamma_code, int need_expand,
+ *    png_fixed_point background_gamma)
+ *
+ * This ignores the gamma (at present.)
+*/
+#define data ITDATA(background)
+static image_pixel data;
+
+static void
+image_transform_png_set_background_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_byte colour_type, bit_depth;
+   png_byte random_bytes[8]; /* 8 bytes - 64 bits - the biggest pixel */
+   int expand;
+   png_color_16 back;
+
+   /* We need a background colour, because we don't know exactly what transforms
+    * have been set we have to supply the colour in the original file format and
+    * so we need to know what that is!  The background colour is stored in the
+    * transform_display.
+    */
+   RANDOMIZE(random_bytes);
+
+   /* Read the random value, for colour type 3 the background colour is actually
+    * expressed as a 24bit rgb, not an index.
+    */
+   colour_type = that->this.colour_type;
+   if (colour_type == 3)
+   {
+      colour_type = PNG_COLOR_TYPE_RGB;
+      bit_depth = 8;
+      expand = 0; /* passing in an RGB not a pixel index */
+   }
+
+   else
+   {
+      if (that->this.has_tRNS)
+         that->this.is_transparent = 1;
+
+      bit_depth = that->this.bit_depth;
+      expand = 1;
+   }
+
+   image_pixel_init(&data, random_bytes, colour_type,
+      bit_depth, 0/*x*/, 0/*unused: palette*/, NULL/*format*/);
+
+   /* Extract the background colour from this image_pixel, but make sure the
+    * unused fields of 'back' are garbage.
+    */
+   RANDOMIZE(back);
+
+   if (colour_type & PNG_COLOR_MASK_COLOR)
+   {
+      back.red = (png_uint_16)data.red;
+      back.green = (png_uint_16)data.green;
+      back.blue = (png_uint_16)data.blue;
+   }
+
+   else
+      back.gray = (png_uint_16)data.red;
+
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
+#  else
+      png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
+#  endif
+
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_background_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   /* Check for tRNS first: */
+   if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
+      image_pixel_add_alpha(that, &display->this, 1/*for background*/);
+
+   /* This is only necessary if the alpha value is less than 1. */
+   if (that->alphaf < 1)
+   {
+      /* Now we do the background calculation without any gamma correction. */
+      if (that->alphaf <= 0)
+      {
+         that->redf = data.redf;
+         that->greenf = data.greenf;
+         that->bluef = data.bluef;
+
+         that->rede = data.rede;
+         that->greene = data.greene;
+         that->bluee = data.bluee;
+
+         that->red_sBIT= data.red_sBIT;
+         that->green_sBIT= data.green_sBIT;
+         that->blue_sBIT= data.blue_sBIT;
+      }
+
+      else /* 0 < alpha < 1 */
+      {
+         double alf = 1 - that->alphaf;
+
+         that->redf = that->redf * that->alphaf + data.redf * alf;
+         that->rede = that->rede * that->alphaf + data.rede * alf +
+            DBL_EPSILON;
+         that->greenf = that->greenf * that->alphaf + data.greenf * alf;
+         that->greene = that->greene * that->alphaf + data.greene * alf +
+            DBL_EPSILON;
+         that->bluef = that->bluef * that->alphaf + data.bluef * alf;
+         that->bluee = that->bluee * that->alphaf + data.bluee * alf +
+            DBL_EPSILON;
+      }
+
+      /* Remove the alpha type and set the alpha (not in that order.) */
+      that->alphaf = 1;
+      that->alphae = 0;
+   }
+
+   if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      that->colour_type = PNG_COLOR_TYPE_RGB;
+   else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      that->colour_type = PNG_COLOR_TYPE_GRAY;
+   /* PNG_COLOR_TYPE_PALETTE is not changed */
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+#define image_transform_png_set_background_add image_transform_default_add
+
+#undef data
+IT(background);
+#undef PT
+#define PT ITSTRUCT(background)
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
+ *    int maximum_colors, png_const_uint_16p histogram, int full_quantize)
+ *
+ * Very difficult to validate this!
+ */
+/*NOTE: TBD NYI */
+
+/* The data layout transforms are handled by swapping our own channel data,
+ * necessarily these need to happen at the end of the transform list because the
+ * semantic of the channels changes after these are executed.  Some of these,
+ * like set_shift and set_packing, can't be done at present because they change
+ * the layout of the data at the sub-sample level so sample() won't get the
+ * right answer.
+ */
+/* png_set_invert_alpha */
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+/* Invert the alpha channel
+ *
+ *  png_set_invert_alpha(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_invert_alpha_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_invert_alpha(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_invert_alpha_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->colour_type & 4)
+      that->alpha_inverted = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_invert_alpha_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   /* Only has an effect on pixels with alpha: */
+   return (colour_type & 4) != 0;
+}
+
+IT(invert_alpha);
+#undef PT
+#define PT ITSTRUCT(invert_alpha)
+
+#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */
+
+/* png_set_bgr */
+#ifdef PNG_READ_BGR_SUPPORTED
+/* Swap R,G,B channels to order B,G,R.
+ *
+ *  png_set_bgr(png_structrp png_ptr)
+ *
+ * This only has an effect on RGB and RGBA pixels.
+ */
+static void
+image_transform_png_set_bgr_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_bgr(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_bgr_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->colour_type == PNG_COLOR_TYPE_RGB ||
+       that->colour_type == PNG_COLOR_TYPE_RGBA)
+       that->swap_rgb = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_bgr_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return colour_type == PNG_COLOR_TYPE_RGB ||
+       colour_type == PNG_COLOR_TYPE_RGBA;
+}
+
+IT(bgr);
+#undef PT
+#define PT ITSTRUCT(bgr)
+
+#endif /* PNG_READ_BGR_SUPPORTED */
+
+/* png_set_swap_alpha */
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+/* Put the alpha channel first.
+ *
+ *  png_set_swap_alpha(png_structrp png_ptr)
+ *
+ * This only has an effect on GA and RGBA pixels.
+ */
+static void
+image_transform_png_set_swap_alpha_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_swap_alpha(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_swap_alpha_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->colour_type == PNG_COLOR_TYPE_GA ||
+       that->colour_type == PNG_COLOR_TYPE_RGBA)
+      that->alpha_first = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_swap_alpha_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return colour_type == PNG_COLOR_TYPE_GA ||
+       colour_type == PNG_COLOR_TYPE_RGBA;
+}
+
+IT(swap_alpha);
+#undef PT
+#define PT ITSTRUCT(swap_alpha)
+
+#endif /* PNG_READ_SWAP_ALPHA_SUPPORTED */
+
+/* png_set_swap */
+#ifdef PNG_READ_SWAP_SUPPORTED
+/* Byte swap 16-bit components.
+ *
+ *  png_set_swap(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_swap_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_swap(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_swap_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->bit_depth == 16)
+      that->swap16 = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_swap_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   return bit_depth == 16;
+}
+
+IT(swap);
+#undef PT
+#define PT ITSTRUCT(swap)
+
+#endif /* PNG_READ_SWAP_SUPPORTED */
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images.
+ *
+ *  png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags));
+ *
+ * Flags:
+ *
+ *  PNG_FILLER_BEFORE
+ *  PNG_FILLER_AFTER
+ */
+#define data ITDATA(filler)
+static struct
+{
+   png_uint_32 filler;
+   int         flags;
+} data;
+
+static void
+image_transform_png_set_filler_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   /* Need a random choice for 'before' and 'after' as well as for the
+    * filler.  The 'filler' value has all 32 bits set, but only bit_depth
+    * will be used.  At this point we don't know bit_depth.
+    */
+   RANDOMIZE(data.filler);
+   data.flags = random_choice();
+
+   png_set_filler(pp, data.filler, data.flags);
+
+   /* The standard display handling stuff also needs to know that
+    * there is a filler, so set that here.
+    */
+   that->this.filler = 1;
+
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_filler_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->bit_depth >= 8 &&
+       (that->colour_type == PNG_COLOR_TYPE_RGB ||
+        that->colour_type == PNG_COLOR_TYPE_GRAY))
+   {
+      const unsigned int max = (1U << that->bit_depth)-1;
+      that->alpha = data.filler & max;
+      that->alphaf = ((double)that->alpha) / max;
+      that->alphae = 0;
+
+      /* The filler has been stored in the alpha channel, we must record
+       * that this has been done for the checking later on, the color
+       * type is faked to have an alpha channel, but libpng won't report
+       * this; the app has to know the extra channel is there and this
+       * was recording in standard_display::filler above.
+       */
+      that->colour_type |= 4; /* alpha added */
+      that->alpha_first = data.flags == PNG_FILLER_BEFORE;
+   }
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_filler_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   this->next = *that;
+   *that = this;
+
+   return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB ||
+           colour_type == PNG_COLOR_TYPE_GRAY);
+}
+
+#undef data
+IT(filler);
+#undef PT
+#define PT ITSTRUCT(filler)
+
+/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+#define data ITDATA(add_alpha)
+static struct
+{
+   png_uint_32 filler;
+   int         flags;
+} data;
+
+static void
+image_transform_png_set_add_alpha_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   /* Need a random choice for 'before' and 'after' as well as for the
+    * filler.  The 'filler' value has all 32 bits set, but only bit_depth
+    * will be used.  At this point we don't know bit_depth.
+    */
+   RANDOMIZE(data.filler);
+   data.flags = random_choice();
+
+   png_set_add_alpha(pp, data.filler, data.flags);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_add_alpha_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->bit_depth >= 8 &&
+       (that->colour_type == PNG_COLOR_TYPE_RGB ||
+        that->colour_type == PNG_COLOR_TYPE_GRAY))
+   {
+      const unsigned int max = (1U << that->bit_depth)-1;
+      that->alpha = data.filler & max;
+      that->alphaf = ((double)that->alpha) / max;
+      that->alphae = 0;
+
+      that->colour_type |= 4; /* alpha added */
+      that->alpha_first = data.flags == PNG_FILLER_BEFORE;
+   }
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_add_alpha_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   this->next = *that;
+   *that = this;
+
+   return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB ||
+           colour_type == PNG_COLOR_TYPE_GRAY);
+}
+
+#undef data
+IT(add_alpha);
+#undef PT
+#define PT ITSTRUCT(add_alpha)
+
+#endif /* PNG_READ_FILLER_SUPPORTED */
+
+/* png_set_packing */
+#ifdef PNG_READ_PACK_SUPPORTED
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files.
+ *
+ *  png_set_packing(png_structrp png_ptr)
+ *
+ * This should only affect grayscale and palette images with less than 8 bits
+ * per pixel.
+ */
+static void
+image_transform_png_set_packing_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_packing(pp);
+   that->unpacked = 1;
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_packing_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   /* The general expand case depends on what the colour type is,
+    * low bit-depth pixel values are unpacked into bytes without
+    * scaling, so sample_depth is not changed.
+    */
+   if (that->bit_depth < 8) /* grayscale or palette */
+      that->bit_depth = 8;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_packing_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   /* Nothing should happen unless the bit depth is less than 8: */
+   return bit_depth < 8;
+}
+
+IT(packing);
+#undef PT
+#define PT ITSTRUCT(packing)
+
+#endif /* PNG_READ_PACK_SUPPORTED */
+
+/* png_set_packswap */
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+/* Swap pixels packed into bytes; reverses the order on screen so that
+ * the high order bits correspond to the rightmost pixels.
+ *
+ *  png_set_packswap(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_packswap_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_packswap(pp);
+   that->this.littleendian = 1;
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_packswap_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->bit_depth < 8)
+      that->littleendian = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_packswap_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   return bit_depth < 8;
+}
+
+IT(packswap);
+#undef PT
+#define PT ITSTRUCT(packswap)
+
+#endif /* PNG_READ_PACKSWAP_SUPPORTED */
+
+
+/* png_set_invert_mono */
+#ifdef PNG_READ_INVERT_MONO_SUPPORTED
+/* Invert the gray channel
+ *
+ *  png_set_invert_mono(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_invert_mono_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_invert_mono(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_invert_mono_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   if (that->colour_type & 4)
+      that->mono_inverted = 1;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_invert_mono_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   /* Only has an effect on pixels with no colour: */
+   return (colour_type & 2) == 0;
+}
+
+IT(invert_mono);
+#undef PT
+#define PT ITSTRUCT(invert_mono)
+
+#endif /* PNG_READ_INVERT_MONO_SUPPORTED */
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+/* png_set_shift(png_structp, png_const_color_8p true_bits)
+ *
+ * The output pixels will be shifted by the given true_bits
+ * values.
+ */
+#define data ITDATA(shift)
+static png_color_8 data;
+
+static void
+image_transform_png_set_shift_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   /* Get a random set of shifts.  The shifts need to do something
+    * to test the transform, so they are limited to the bit depth
+    * of the input image.  Notice that in the following the 'gray'
+    * field is randomized independently.  This acts as a check that
+    * libpng does use the correct field.
+    */
+   const unsigned int depth = that->this.bit_depth;
+
+   data.red = (png_byte)/*SAFE*/(random_mod(depth)+1);
+   data.green = (png_byte)/*SAFE*/(random_mod(depth)+1);
+   data.blue = (png_byte)/*SAFE*/(random_mod(depth)+1);
+   data.gray = (png_byte)/*SAFE*/(random_mod(depth)+1);
+   data.alpha = (png_byte)/*SAFE*/(random_mod(depth)+1);
+
+   png_set_shift(pp, &data);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_shift_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   /* Copy the correct values into the sBIT fields, libpng does not do
+    * anything to palette data:
+    */
+   if (that->colour_type != PNG_COLOR_TYPE_PALETTE)
+   {
+       that->sig_bits = 1;
+
+       /* The sBIT fields are reset to the values previously sent to
+        * png_set_shift according to the colour type.
+        * does.
+        */
+       if (that->colour_type & 2) /* RGB channels */
+       {
+          that->red_sBIT = data.red;
+          that->green_sBIT = data.green;
+          that->blue_sBIT = data.blue;
+       }
+
+       else /* One grey channel */
+          that->red_sBIT = that->green_sBIT = that->blue_sBIT = data.gray;
+
+       that->alpha_sBIT = data.alpha;
+   }
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_shift_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(bit_depth)
+
+   this->next = *that;
+   *that = this;
+
+   return colour_type != PNG_COLOR_TYPE_PALETTE;
+}
+
+IT(shift);
+#undef PT
+#define PT ITSTRUCT(shift)
+
+#endif /* PNG_READ_SHIFT_SUPPORTED */
+
+#ifdef THIS_IS_THE_PROFORMA
+static void
+image_transform_png_set_@_set(const image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_@(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_@_mod(const image_transform *this,
+    image_pixel *that, png_const_structp pp,
+    const transform_display *display)
+{
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_@_add(image_transform *this,
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   this->next = *that;
+   *that = this;
+
+   return 1;
+}
+
+IT(@);
+#endif
+
+
+/* This may just be 'end' if all the transforms are disabled! */
+static image_transform *const image_transform_first = &PT;
+
+static void
+transform_enable(const char *name)
+{
+   /* Everything starts out enabled, so if we see an 'enable' disabled
+    * everything else the first time round.
+    */
+   static int all_disabled = 0;
+   int found_it = 0;
+   image_transform *list = image_transform_first;
+
+   while (list != &image_transform_end)
+   {
+      if (strcmp(list->name, name) == 0)
+      {
+         list->enable = 1;
+         found_it = 1;
+      }
+      else if (!all_disabled)
+         list->enable = 0;
+
+      list = list->list;
+   }
+
+   all_disabled = 1;
+
+   if (!found_it)
+   {
+      fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n",
+         name);
+      exit(99);
+   }
+}
+
+static void
+transform_disable(const char *name)
+{
+   image_transform *list = image_transform_first;
+
+   while (list != &image_transform_end)
+   {
+      if (strcmp(list->name, name) == 0)
+      {
+         list->enable = 0;
+         return;
+      }
+
+      list = list->list;
+   }
+
+   fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n",
+      name);
+   exit(99);
+}
+
+static void
+image_transform_reset_count(void)
+{
+   image_transform *next = image_transform_first;
+   int count = 0;
+
+   while (next != &image_transform_end)
+   {
+      next->local_use = 0;
+      next->next = 0;
+      next = next->list;
+      ++count;
+   }
+
+   /* This can only happen if we every have more than 32 transforms (excluding
+    * the end) in the list.
+    */
+   if (count > 32) abort();
+}
+
+static int
+image_transform_test_counter(png_uint_32 counter, unsigned int max)
+{
+   /* Test the list to see if there is any point contining, given a current
+    * counter and a 'max' value.
+    */
+   image_transform *next = image_transform_first;
+
+   while (next != &image_transform_end)
+   {
+      /* For max 0 or 1 continue until the counter overflows: */
+      counter >>= 1;
+
+      /* Continue if any entry hasn't reacked the max. */
+      if (max > 1 && next->local_use < max)
+         return 1;
+      next = next->list;
+   }
+
+   return max <= 1 && counter == 0;
+}
+
+static png_uint_32
+image_transform_add(const image_transform **this, unsigned int max,
+   png_uint_32 counter, char *name, size_t sizeof_name, size_t *pos,
+   png_byte colour_type, png_byte bit_depth)
+{
+   for (;;) /* until we manage to add something */
+   {
+      png_uint_32 mask;
+      image_transform *list;
+
+      /* Find the next counter value, if the counter is zero this is the start
+       * of the list.  This routine always returns the current counter (not the
+       * next) so it returns 0 at the end and expects 0 at the beginning.
+       */
+      if (counter == 0) /* first time */
+      {
+         image_transform_reset_count();
+         if (max <= 1)
+            counter = 1;
+         else
+            counter = random_32();
+      }
+      else /* advance the counter */
+      {
+         switch (max)
+         {
+            case 0:  ++counter; break;
+            case 1:  counter <<= 1; break;
+            default: counter = random_32(); break;
+         }
+      }
+
+      /* Now add all these items, if possible */
+      *this = &image_transform_end;
+      list = image_transform_first;
+      mask = 1;
+
+      /* Go through the whole list adding anything that the counter selects: */
+      while (list != &image_transform_end)
+      {
+         if ((counter & mask) != 0 && list->enable &&
+             (max == 0 || list->local_use < max))
+         {
+            /* Candidate to add: */
+            if (list->add(list, this, colour_type, bit_depth) || max == 0)
+            {
+               /* Added, so add to the name too. */
+               *pos = safecat(name, sizeof_name, *pos, " +");
+               *pos = safecat(name, sizeof_name, *pos, list->name);
+            }
+
+            else
+            {
+               /* Not useful and max>0, so remove it from *this: */
+               *this = list->next;
+               list->next = 0;
+
+               /* And, since we know it isn't useful, stop it being added again
+                * in this run:
+                */
+               list->local_use = max;
+            }
+         }
+
+         mask <<= 1;
+         list = list->list;
+      }
+
+      /* Now if anything was added we have something to do. */
+      if (*this != &image_transform_end)
+         return counter;
+
+      /* Nothing added, but was there anything in there to add? */
+      if (!image_transform_test_counter(counter, max))
+         return 0;
+   }
+}
+
+static void
+perform_transform_test(png_modifier *pm)
+{
+   png_byte colour_type = 0;
+   png_byte bit_depth = 0;
+   unsigned int palette_number = 0;
+
+   while (next_format(&colour_type, &bit_depth, &palette_number, pm->test_lbg,
+            pm->test_tRNS))
+   {
+      png_uint_32 counter = 0;
+      size_t base_pos;
+      char name[64];
+
+      base_pos = safecat(name, sizeof name, 0, "transform:");
+
+      for (;;)
+      {
+         size_t pos = base_pos;
+         const image_transform *list = 0;
+
+         /* 'max' is currently hardwired to '1'; this should be settable on the
+          * command line.
+          */
+         counter = image_transform_add(&list, 1/*max*/, counter,
+            name, sizeof name, &pos, colour_type, bit_depth);
+
+         if (counter == 0)
+            break;
+
+         /* The command line can change this to checking interlaced images. */
+         do
+         {
+            pm->repeat = 0;
+            transform_test(pm, FILEID(colour_type, bit_depth, palette_number,
+               pm->interlace_type, 0, 0, 0), list, name);
+
+            if (fail(pm))
+               return;
+         }
+         while (pm->repeat);
+      }
+   }
+}
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/********************************* GAMMA TESTS ********************************/
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Reader callbacks and implementations, where they differ from the standard
+ * ones.
+ */
+typedef struct gamma_display
+{
+   standard_display this;
+
+   /* Parameters */
+   png_modifier*    pm;
+   double           file_gamma;
+   double           screen_gamma;
+   double           background_gamma;
+   png_byte         sbit;
+   int              threshold_test;
+   int              use_input_precision;
+   int              scale16;
+   int              expand16;
+   int              do_background;
+   png_color_16     background_color;
+
+   /* Local variables */
+   double       maxerrout;
+   double       maxerrpc;
+   double       maxerrabs;
+} gamma_display;
+
+#define ALPHA_MODE_OFFSET 4
+
+static void
+gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
+    double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
+    int use_input_precision, int scale16, int expand16,
+    int do_background, const png_color_16 *pointer_to_the_background_color,
+    double background_gamma)
+{
+   /* Standard fields */
+   standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
+      pm->use_update_info);
+
+   /* Parameter fields */
+   dp->pm = pm;
+   dp->file_gamma = file_gamma;
+   dp->screen_gamma = screen_gamma;
+   dp->background_gamma = background_gamma;
+   dp->sbit = sbit;
+   dp->threshold_test = threshold_test;
+   dp->use_input_precision = use_input_precision;
+   dp->scale16 = scale16;
+   dp->expand16 = expand16;
+   dp->do_background = do_background;
+   if (do_background && pointer_to_the_background_color != 0)
+      dp->background_color = *pointer_to_the_background_color;
+   else
+      memset(&dp->background_color, 0, sizeof dp->background_color);
+
+   /* Local variable fields */
+   dp->maxerrout = dp->maxerrpc = dp->maxerrabs = 0;
+}
+
+static void
+gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
+{
+   /* Reuse the standard stuff as appropriate. */
+   standard_info_part1(&dp->this, pp, pi);
+
+   /* If requested strip 16 to 8 bits - this is handled automagically below
+    * because the output bit depth is read from the library.  Note that there
+    * are interactions with sBIT but, internally, libpng makes sbit at most
+    * PNG_MAX_GAMMA_8 prior to 1.7 when doing the following.
+    */
+   if (dp->scale16)
+#     ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+         png_set_scale_16(pp);
+#     else
+         /* The following works both in 1.5.4 and earlier versions: */
+#        ifdef PNG_READ_16_TO_8_SUPPORTED
+            png_set_strip_16(pp);
+#        else
+            png_error(pp, "scale16 (16 to 8 bit conversion) not supported");
+#        endif
+#     endif
+
+   if (dp->expand16)
+#     ifdef PNG_READ_EXPAND_16_SUPPORTED
+         png_set_expand_16(pp);
+#     else
+         png_error(pp, "expand16 (8 to 16 bit conversion) not supported");
+#     endif
+
+   if (dp->do_background >= ALPHA_MODE_OFFSET)
+   {
+#     ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+      {
+         /* This tests the alpha mode handling, if supported. */
+         int mode = dp->do_background - ALPHA_MODE_OFFSET;
+
+         /* The gamma value is the output gamma, and is in the standard,
+          * non-inverted, represenation.  It provides a default for the PNG file
+          * gamma, but since the file has a gAMA chunk this does not matter.
+          */
+         const double sg = dp->screen_gamma;
+#        ifndef PNG_FLOATING_POINT_SUPPORTED
+            const png_fixed_point g = fix(sg);
+#        endif
+
+#        ifdef PNG_FLOATING_POINT_SUPPORTED
+            png_set_alpha_mode(pp, mode, sg);
+#        else
+            png_set_alpha_mode_fixed(pp, mode, g);
+#        endif
+
+         /* However, for the standard Porter-Duff algorithm the output defaults
+          * to be linear, so if the test requires non-linear output it must be
+          * corrected here.
+          */
+         if (mode == PNG_ALPHA_STANDARD && sg != 1)
+         {
+#           ifdef PNG_FLOATING_POINT_SUPPORTED
+               png_set_gamma(pp, sg, dp->file_gamma);
+#           else
+               png_fixed_point f = fix(dp->file_gamma);
+               png_set_gamma_fixed(pp, g, f);
+#           endif
+         }
+      }
+#     else
+         png_error(pp, "alpha mode handling not supported");
+#     endif
+   }
+
+   else
+   {
+      /* Set up gamma processing. */
+#     ifdef PNG_FLOATING_POINT_SUPPORTED
+         png_set_gamma(pp, dp->screen_gamma, dp->file_gamma);
+#     else
+      {
+         png_fixed_point s = fix(dp->screen_gamma);
+         png_fixed_point f = fix(dp->file_gamma);
+         png_set_gamma_fixed(pp, s, f);
+      }
+#     endif
+
+      if (dp->do_background)
+      {
+#     ifdef PNG_READ_BACKGROUND_SUPPORTED
+         /* NOTE: this assumes the caller provided the correct background gamma!
+          */
+         const double bg = dp->background_gamma;
+#        ifndef PNG_FLOATING_POINT_SUPPORTED
+            const png_fixed_point g = fix(bg);
+#        endif
+
+#        ifdef PNG_FLOATING_POINT_SUPPORTED
+            png_set_background(pp, &dp->background_color, dp->do_background,
+               0/*need_expand*/, bg);
+#        else
+            png_set_background_fixed(pp, &dp->background_color,
+               dp->do_background, 0/*need_expand*/, g);
+#        endif
+#     else
+         png_error(pp, "png_set_background not supported");
+#     endif
+      }
+   }
+
+   {
+      int i = dp->this.use_update_info;
+      /* Always do one call, even if use_update_info is 0. */
+      do
+         png_read_update_info(pp, pi);
+      while (--i > 0);
+   }
+
+   /* Now we may get a different cbRow: */
+   standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
+}
+
+static void PNGCBAPI
+gamma_info(png_structp pp, png_infop pi)
+{
+   gamma_info_imp(voidcast(gamma_display*, png_get_progressive_ptr(pp)), pp,
+      pi);
+}
+
+/* Validate a single component value - the routine gets the input and output
+ * sample values as unscaled PNG component values along with a cache of all the
+ * information required to validate the values.
+ */
+typedef struct validate_info
+{
+   png_const_structp  pp;
+   gamma_display *dp;
+   png_byte sbit;
+   int use_input_precision;
+   int do_background;
+   int scale16;
+   unsigned int sbit_max;
+   unsigned int isbit_shift;
+   unsigned int outmax;
+
+   double gamma_correction; /* Overall correction required. */
+   double file_inverse;     /* Inverse of file gamma. */
+   double screen_gamma;
+   double screen_inverse;   /* Inverse of screen gamma. */
+
+   double background_red;   /* Linear background value, red or gray. */
+   double background_green;
+   double background_blue;
+
+   double maxabs;
+   double maxpc;
+   double maxcalc;
+   double maxout;
+   double maxout_total;     /* Total including quantization error */
+   double outlog;
+   int    outquant;
+}
+validate_info;
+
+static void
+init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
+    int in_depth, int out_depth)
+{
+   const unsigned int outmax = (1U<<out_depth)-1;
+
+   vi->pp = pp;
+   vi->dp = dp;
+
+   if (dp->sbit > 0 && dp->sbit < in_depth)
+   {
+      vi->sbit = dp->sbit;
+      vi->isbit_shift = in_depth - dp->sbit;
+   }
+
+   else
+   {
+      vi->sbit = (png_byte)in_depth;
+      vi->isbit_shift = 0;
+   }
+
+   vi->sbit_max = (1U << vi->sbit)-1;
+
+   /* This mimics the libpng threshold test, '0' is used to prevent gamma
+    * correction in the validation test.
+    */
+   vi->screen_gamma = dp->screen_gamma;
+   if (fabs(vi->screen_gamma-1) < PNG_GAMMA_THRESHOLD)
+      vi->screen_gamma = vi->screen_inverse = 0;
+   else
+      vi->screen_inverse = 1/vi->screen_gamma;
+
+   vi->use_input_precision = dp->use_input_precision;
+   vi->outmax = outmax;
+   vi->maxabs = abserr(dp->pm, in_depth, out_depth);
+   vi->maxpc = pcerr(dp->pm, in_depth, out_depth);
+   vi->maxcalc = calcerr(dp->pm, in_depth, out_depth);
+   vi->maxout = outerr(dp->pm, in_depth, out_depth);
+   vi->outquant = output_quantization_factor(dp->pm, in_depth, out_depth);
+   vi->maxout_total = vi->maxout + vi->outquant * .5;
+   vi->outlog = outlog(dp->pm, in_depth, out_depth);
+
+   if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 ||
+      (dp->this.colour_type == 3 && dp->this.is_transparent) ||
+      ((dp->this.colour_type == 0 || dp->this.colour_type == 2) &&
+       dp->this.has_tRNS))
+   {
+      vi->do_background = dp->do_background;
+
+      if (vi->do_background != 0)
+      {
+         const double bg_inverse = 1/dp->background_gamma;
+         double r, g, b;
+
+         /* Caller must at least put the gray value into the red channel */
+         r = dp->background_color.red; r /= outmax;
+         g = dp->background_color.green; g /= outmax;
+         b = dp->background_color.blue; b /= outmax;
+
+#     if 0
+         /* libpng doesn't do this optimization, if we do pngvalid will fail.
+          */
+         if (fabs(bg_inverse-1) >= PNG_GAMMA_THRESHOLD)
+#     endif
+         {
+            r = pow(r, bg_inverse);
+            g = pow(g, bg_inverse);
+            b = pow(b, bg_inverse);
+         }
+
+         vi->background_red = r;
+         vi->background_green = g;
+         vi->background_blue = b;
+      }
+   }
+   else /* Do not expect any background processing */
+      vi->do_background = 0;
+
+   if (vi->do_background == 0)
+      vi->background_red = vi->background_green = vi->background_blue = 0;
+
+   vi->gamma_correction = 1/(dp->file_gamma*dp->screen_gamma);
+   if (fabs(vi->gamma_correction-1) < PNG_GAMMA_THRESHOLD)
+      vi->gamma_correction = 0;
+
+   vi->file_inverse = 1/dp->file_gamma;
+   if (fabs(vi->file_inverse-1) < PNG_GAMMA_THRESHOLD)
+      vi->file_inverse = 0;
+
+   vi->scale16 = dp->scale16;
+}
+
+/* This function handles composition of a single non-alpha component.  The
+ * argument is the input sample value, in the range 0..1, and the alpha value.
+ * The result is the composed, linear, input sample.  If alpha is less than zero
+ * this is the alpha component and the function should not be called!
+ */
+static double
+gamma_component_compose(int do_background, double input_sample, double alpha,
+   double background, int *compose)
+{
+   switch (do_background)
+   {
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+      case PNG_BACKGROUND_GAMMA_SCREEN:
+      case PNG_BACKGROUND_GAMMA_FILE:
+      case PNG_BACKGROUND_GAMMA_UNIQUE:
+         /* Standard PNG background processing. */
+         if (alpha < 1)
+         {
+            if (alpha > 0)
+            {
+               input_sample = input_sample * alpha + background * (1-alpha);
+               if (compose != NULL)
+                  *compose = 1;
+            }
+
+            else
+               input_sample = background;
+         }
+         break;
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
+         /* The components are premultiplied in either case and the output is
+          * gamma encoded (to get standard Porter-Duff we expect the output
+          * gamma to be set to 1.0!)
+          */
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
+         /* The optimization is that the partial-alpha entries are linear
+          * while the opaque pixels are gamma encoded, but this only affects the
+          * output encoding.
+          */
+         if (alpha < 1)
+         {
+            if (alpha > 0)
+            {
+               input_sample *= alpha;
+               if (compose != NULL)
+                  *compose = 1;
+            }
+
+            else
+               input_sample = 0;
+         }
+         break;
+#endif
+
+      default:
+         /* Standard cases where no compositing is done (so the component
+          * value is already correct.)
+          */
+         UNUSED(alpha)
+         UNUSED(background)
+         UNUSED(compose)
+         break;
+   }
+
+   return input_sample;
+}
+
+/* This API returns the encoded *input* component, in the range 0..1 */
+static double
+gamma_component_validate(const char *name, const validate_info *vi,
+    const unsigned int id, const unsigned int od,
+    const double alpha /* <0 for the alpha channel itself */,
+    const double background /* component background value */)
+{
+   const unsigned int isbit = id >> vi->isbit_shift;
+   const unsigned int sbit_max = vi->sbit_max;
+   const unsigned int outmax = vi->outmax;
+   const int do_background = vi->do_background;
+
+   double i;
+
+   /* First check on the 'perfect' result obtained from the digitized input
+    * value, id, and compare this against the actual digitized result, 'od'.
+    * 'i' is the input result in the range 0..1:
+    */
+   i = isbit; i /= sbit_max;
+
+   /* Check for the fast route: if we don't do any background composition or if
+    * this is the alpha channel ('alpha' < 0) or if the pixel is opaque then
+    * just use the gamma_correction field to correct to the final output gamma.
+    */
+   if (alpha == 1 /* opaque pixel component */ || !do_background
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+      || do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_PNG
+#endif
+      || (alpha < 0 /* alpha channel */
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+      && do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN
+#endif
+      ))
+   {
+      /* Then get the gamma corrected version of 'i' and compare to 'od', any
+       * error less than .5 is insignificant - just quantization of the output
+       * value to the nearest digital value (nevertheless the error is still
+       * recorded - it's interesting ;-)
+       */
+      double encoded_sample = i;
+      double encoded_error;
+
+      /* alpha less than 0 indicates the alpha channel, which is always linear
+       */
+      if (alpha >= 0 && vi->gamma_correction > 0)
+         encoded_sample = pow(encoded_sample, vi->gamma_correction);
+      encoded_sample *= outmax;
+
+      encoded_error = fabs(od-encoded_sample);
+
+      if (encoded_error > vi->dp->maxerrout)
+         vi->dp->maxerrout = encoded_error;
+
+      if (encoded_error < vi->maxout_total && encoded_error < vi->outlog)
+         return i;
+   }
+
+   /* The slow route - attempt to do linear calculations. */
+   /* There may be an error, or background processing is required, so calculate
+    * the actual sample values - unencoded light intensity values.  Note that in
+    * practice these are not completely unencoded because they include a
+    * 'viewing correction' to decrease or (normally) increase the perceptual
+    * contrast of the image.  There's nothing we can do about this - we don't
+    * know what it is - so assume the unencoded value is perceptually linear.
+    */
+   {
+      double input_sample = i; /* In range 0..1 */
+      double output, error, encoded_sample, encoded_error;
+      double es_lo, es_hi;
+      int compose = 0;           /* Set to one if composition done */
+      int output_is_encoded;     /* Set if encoded to screen gamma */
+      int log_max_error = 1;     /* Check maximum error values */
+      png_const_charp pass = 0;  /* Reason test passes (or 0 for fail) */
+
+      /* Convert to linear light (with the above caveat.)  The alpha channel is
+       * already linear.
+       */
+      if (alpha >= 0)
+      {
+         int tcompose;
+
+         if (vi->file_inverse > 0)
+            input_sample = pow(input_sample, vi->file_inverse);
+
+         /* Handle the compose processing: */
+         tcompose = 0;
+         input_sample = gamma_component_compose(do_background, input_sample,
+            alpha, background, &tcompose);
+
+         if (tcompose)
+            compose = 1;
+      }
+
+      /* And similarly for the output value, but we need to check the background
+       * handling to linearize it correctly.
+       */
+      output = od;
+      output /= outmax;
+
+      output_is_encoded = vi->screen_gamma > 0;
+
+      if (alpha < 0) /* The alpha channel */
+      {
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+         if (do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN)
+#endif
+         {
+            /* In all other cases the output alpha channel is linear already,
+             * don't log errors here, they are much larger in linear data.
+             */
+            output_is_encoded = 0;
+            log_max_error = 0;
+         }
+      }
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+      else /* A component */
+      {
+         if (do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED &&
+            alpha < 1) /* the optimized case - linear output */
+         {
+            if (alpha > 0) log_max_error = 0;
+            output_is_encoded = 0;
+         }
+      }
+#endif
+
+      if (output_is_encoded)
+         output = pow(output, vi->screen_gamma);
+
+      /* Calculate (or recalculate) the encoded_sample value and repeat the
+       * check above (unnecessary if we took the fast route, but harmless.)
+       */
+      encoded_sample = input_sample;
+      if (output_is_encoded)
+         encoded_sample = pow(encoded_sample, vi->screen_inverse);
+      encoded_sample *= outmax;
+
+      encoded_error = fabs(od-encoded_sample);
+
+      /* Don't log errors in the alpha channel, or the 'optimized' case,
+       * neither are significant to the overall perception.
+       */
+      if (log_max_error && encoded_error > vi->dp->maxerrout)
+         vi->dp->maxerrout = encoded_error;
+
+      if (encoded_error < vi->maxout_total)
+      {
+         if (encoded_error < vi->outlog)
+            return i;
+
+         /* Test passed but error is bigger than the log limit, record why the
+          * test passed:
+          */
+         pass = "less than maxout:\n";
+      }
+
+      /* i: the original input value in the range 0..1
+       *
+       * pngvalid calculations:
+       *  input_sample: linear result; i linearized and composed, range 0..1
+       *  encoded_sample: encoded result; input_sample scaled to ouput bit depth
+       *
+       * libpng calculations:
+       *  output: linear result; od scaled to 0..1 and linearized
+       *  od: encoded result from libpng
+       */
+
+      /* Now we have the numbers for real errors, both absolute values as as a
+       * percentage of the correct value (output):
+       */
+      error = fabs(input_sample-output);
+
+      if (log_max_error && error > vi->dp->maxerrabs)
+         vi->dp->maxerrabs = error;
+
+      /* The following is an attempt to ignore the tendency of quantization to
+       * dominate the percentage errors for lower result values:
+       */
+      if (log_max_error && input_sample > .5)
+      {
+         double percentage_error = error/input_sample;
+         if (percentage_error > vi->dp->maxerrpc)
+            vi->dp->maxerrpc = percentage_error;
+      }
+
+      /* Now calculate the digitization limits for 'encoded_sample' using the
+       * 'max' values.  Note that maxout is in the encoded space but maxpc and
+       * maxabs are in linear light space.
+       *
+       * First find the maximum error in linear light space, range 0..1:
+       */
+      {
+         double tmp = input_sample * vi->maxpc;
+         if (tmp < vi->maxabs) tmp = vi->maxabs;
+         /* If 'compose' is true the composition was done in linear space using
+          * integer arithmetic.  This introduces an extra error of +/- 0.5 (at
+          * least) in the integer space used.  'maxcalc' records this, taking
+          * into account the possibility that even for 16 bit output 8 bit space
+          * may have been used.
+          */
+         if (compose && tmp < vi->maxcalc) tmp = vi->maxcalc;
+
+         /* The 'maxout' value refers to the encoded result, to compare with
+          * this encode input_sample adjusted by the maximum error (tmp) above.
+          */
+         es_lo = encoded_sample - vi->maxout;
+
+         if (es_lo > 0 && input_sample-tmp > 0)
+         {
+            double low_value = input_sample-tmp;
+            if (output_is_encoded)
+               low_value = pow(low_value, vi->screen_inverse);
+            low_value *= outmax;
+            if (low_value < es_lo) es_lo = low_value;
+
+            /* Quantize this appropriately: */
+            es_lo = ceil(es_lo / vi->outquant - .5) * vi->outquant;
+         }
+
+         else
+            es_lo = 0;
+
+         es_hi = encoded_sample + vi->maxout;
+
+         if (es_hi < outmax && input_sample+tmp < 1)
+         {
+            double high_value = input_sample+tmp;
+            if (output_is_encoded)
+               high_value = pow(high_value, vi->screen_inverse);
+            high_value *= outmax;
+            if (high_value > es_hi) es_hi = high_value;
+
+            es_hi = floor(es_hi / vi->outquant + .5) * vi->outquant;
+         }
+
+         else
+            es_hi = outmax;
+      }
+
+      /* The primary test is that the final encoded value returned by the
+       * library should be between the two limits (inclusive) that were
+       * calculated above.
+       */
+      if (od >= es_lo && od <= es_hi)
+      {
+         /* The value passes, but we may need to log the information anyway. */
+         if (encoded_error < vi->outlog)
+            return i;
+
+         if (pass == 0)
+            pass = "within digitization limits:\n";
+      }
+
+      {
+         /* There has been an error in processing, or we need to log this
+          * value.
+          */
+         double is_lo, is_hi;
+
+         /* pass is set at this point if either of the tests above would have
+          * passed.  Don't do these additional tests here - just log the
+          * original [es_lo..es_hi] values.
+          */
+         if (pass == 0 && vi->use_input_precision && vi->dp->sbit)
+         {
+            /* Ok, something is wrong - this actually happens in current libpng
+             * 16-to-8 processing.  Assume that the input value (id, adjusted
+             * for sbit) can be anywhere between value-.5 and value+.5 - quite a
+             * large range if sbit is low.
+             *
+             * NOTE: at present because the libpng gamma table stuff has been
+             * changed to use a rounding algorithm to correct errors in 8-bit
+             * calculations the precise sbit calculation (a shift) has been
+             * lost.  This can result in up to a +/-1 error in the presence of
+             * an sbit less than the bit depth.
+             */
+#           if PNG_LIBPNG_VER < 10700
+#              define SBIT_ERROR .5
+#           else
+#              define SBIT_ERROR 1.
+#           endif
+            double tmp = (isbit - SBIT_ERROR)/sbit_max;
+
+            if (tmp <= 0)
+               tmp = 0;
+
+            else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
+               tmp = pow(tmp, vi->file_inverse);
+
+            tmp = gamma_component_compose(do_background, tmp, alpha, background,
+               NULL);
+
+            if (output_is_encoded && tmp > 0 && tmp < 1)
+               tmp = pow(tmp, vi->screen_inverse);
+
+            is_lo = ceil(outmax * tmp - vi->maxout_total);
+
+            if (is_lo < 0)
+               is_lo = 0;
+
+            tmp = (isbit + SBIT_ERROR)/sbit_max;
+
+            if (tmp >= 1)
+               tmp = 1;
+
+            else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
+               tmp = pow(tmp, vi->file_inverse);
+
+            tmp = gamma_component_compose(do_background, tmp, alpha, background,
+               NULL);
+
+            if (output_is_encoded && tmp > 0 && tmp < 1)
+               tmp = pow(tmp, vi->screen_inverse);
+
+            is_hi = floor(outmax * tmp + vi->maxout_total);
+
+            if (is_hi > outmax)
+               is_hi = outmax;
+
+            if (!(od < is_lo || od > is_hi))
+            {
+               if (encoded_error < vi->outlog)
+                  return i;
+
+               pass = "within input precision limits:\n";
+            }
+
+            /* One last chance.  If this is an alpha channel and the 16to8
+             * option has been used and 'inaccurate' scaling is used then the
+             * bit reduction is obtained by simply using the top 8 bits of the
+             * value.
+             *
+             * This is only done for older libpng versions when the 'inaccurate'
+             * (chop) method of scaling was used.
+             */
+#           ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+#              if PNG_LIBPNG_VER < 10504
+                  /* This may be required for other components in the future,
+                   * but at present the presence of gamma correction effectively
+                   * prevents the errors in the component scaling (I don't quite
+                   * understand why, but since it's better this way I care not
+                   * to ask, JB 20110419.)
+                   */
+                  if (pass == 0 && alpha < 0 && vi->scale16 && vi->sbit > 8 &&
+                     vi->sbit + vi->isbit_shift == 16)
+                  {
+                     tmp = ((id >> 8) - .5)/255;
+
+                     if (tmp > 0)
+                     {
+                        is_lo = ceil(outmax * tmp - vi->maxout_total);
+                        if (is_lo < 0) is_lo = 0;
+                     }
+
+                     else
+                        is_lo = 0;
+
+                     tmp = ((id >> 8) + .5)/255;
+
+                     if (tmp < 1)
+                     {
+                        is_hi = floor(outmax * tmp + vi->maxout_total);
+                        if (is_hi > outmax) is_hi = outmax;
+                     }
+
+                     else
+                        is_hi = outmax;
+
+                     if (!(od < is_lo || od > is_hi))
+                     {
+                        if (encoded_error < vi->outlog)
+                           return i;
+
+                        pass = "within 8 bit limits:\n";
+                     }
+                  }
+#              endif
+#           endif
+         }
+         else /* !use_input_precision */
+            is_lo = es_lo, is_hi = es_hi;
+
+         /* Attempt to output a meaningful error/warning message: the message
+          * output depends on the background/composite operation being performed
+          * because this changes what parameters were actually used above.
+          */
+         {
+            size_t pos = 0;
+            /* Need either 1/255 or 1/65535 precision here; 3 or 6 decimal
+             * places.  Just use outmax to work out which.
+             */
+            int precision = (outmax >= 1000 ? 6 : 3);
+            int use_input=1, use_background=0, do_compose=0;
+            char msg[256];
+
+            if (pass != 0)
+               pos = safecat(msg, sizeof msg, pos, "\n\t");
+
+            /* Set up the various flags, the output_is_encoded flag above
+             * is also used below.  do_compose is just a double check.
+             */
+            switch (do_background)
+            {
+#           ifdef PNG_READ_BACKGROUND_SUPPORTED
+               case PNG_BACKGROUND_GAMMA_SCREEN:
+               case PNG_BACKGROUND_GAMMA_FILE:
+               case PNG_BACKGROUND_GAMMA_UNIQUE:
+                  use_background = (alpha >= 0 && alpha < 1);
+                  /*FALL THROUGH*/
+#           endif
+#           ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+               case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
+               case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
+               case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
+#           endif /* ALPHA_MODE_SUPPORTED */
+               do_compose = (alpha > 0 && alpha < 1);
+               use_input = (alpha != 0);
+               break;
+
+            default:
+               break;
+            }
+
+            /* Check the 'compose' flag */
+            if (compose != do_compose)
+               png_error(vi->pp, "internal error (compose)");
+
+            /* 'name' is the component name */
+            pos = safecat(msg, sizeof msg, pos, name);
+            pos = safecat(msg, sizeof msg, pos, "(");
+            pos = safecatn(msg, sizeof msg, pos, id);
+            if (use_input || pass != 0/*logging*/)
+            {
+               if (isbit != id)
+               {
+                  /* sBIT has reduced the precision of the input: */
+                  pos = safecat(msg, sizeof msg, pos, ", sbit(");
+                  pos = safecatn(msg, sizeof msg, pos, vi->sbit);
+                  pos = safecat(msg, sizeof msg, pos, "): ");
+                  pos = safecatn(msg, sizeof msg, pos, isbit);
+               }
+               pos = safecat(msg, sizeof msg, pos, "/");
+               /* The output is either "id/max" or "id sbit(sbit): isbit/max" */
+               pos = safecatn(msg, sizeof msg, pos, vi->sbit_max);
+            }
+            pos = safecat(msg, sizeof msg, pos, ")");
+
+            /* A component may have been multiplied (in linear space) by the
+             * alpha value, 'compose' says whether this is relevant.
+             */
+            if (compose || pass != 0)
+            {
+               /* If any form of composition is being done report our
+                * calculated linear value here (the code above doesn't record
+                * the input value before composition is performed, so what
+                * gets reported is the value after composition.)
+                */
+               if (use_input || pass != 0)
+               {
+                  if (vi->file_inverse > 0)
+                  {
+                     pos = safecat(msg, sizeof msg, pos, "^");
+                     pos = safecatd(msg, sizeof msg, pos, vi->file_inverse, 2);
+                  }
+
+                  else
+                     pos = safecat(msg, sizeof msg, pos, "[linear]");
+
+                  pos = safecat(msg, sizeof msg, pos, "*(alpha)");
+                  pos = safecatd(msg, sizeof msg, pos, alpha, precision);
+               }
+
+               /* Now record the *linear* background value if it was used
+                * (this function is not passed the original, non-linear,
+                * value but it is contained in the test name.)
+                */
+               if (use_background)
+               {
+                  pos = safecat(msg, sizeof msg, pos, use_input ? "+" : " ");
+                  pos = safecat(msg, sizeof msg, pos, "(background)");
+                  pos = safecatd(msg, sizeof msg, pos, background, precision);
+                  pos = safecat(msg, sizeof msg, pos, "*");
+                  pos = safecatd(msg, sizeof msg, pos, 1-alpha, precision);
+               }
+            }
+
+            /* Report the calculated value (input_sample) and the linearized
+             * libpng value (output) unless this is just a component gamma
+             * correction.
+             */
+            if (compose || alpha < 0 || pass != 0)
+            {
+               pos = safecat(msg, sizeof msg, pos,
+                  pass != 0 ? " =\n\t" : " = ");
+               pos = safecatd(msg, sizeof msg, pos, input_sample, precision);
+               pos = safecat(msg, sizeof msg, pos, " (libpng: ");
+               pos = safecatd(msg, sizeof msg, pos, output, precision);
+               pos = safecat(msg, sizeof msg, pos, ")");
+
+               /* Finally report the output gamma encoding, if any. */
+               if (output_is_encoded)
+               {
+                  pos = safecat(msg, sizeof msg, pos, " ^");
+                  pos = safecatd(msg, sizeof msg, pos, vi->screen_inverse, 2);
+                  pos = safecat(msg, sizeof msg, pos, "(to screen) =");
+               }
+
+               else
+                  pos = safecat(msg, sizeof msg, pos, " [screen is linear] =");
+            }
+
+            if ((!compose && alpha >= 0) || pass != 0)
+            {
+               if (pass != 0) /* logging */
+                  pos = safecat(msg, sizeof msg, pos, "\n\t[overall:");
+
+               /* This is the non-composition case, the internal linear
+                * values are irrelevant (though the log below will reveal
+                * them.)  Output a much shorter warning/error message and report
+                * the overall gamma correction.
+                */
+               if (vi->gamma_correction > 0)
+               {
+                  pos = safecat(msg, sizeof msg, pos, " ^");
+                  pos = safecatd(msg, sizeof msg, pos, vi->gamma_correction, 2);
+                  pos = safecat(msg, sizeof msg, pos, "(gamma correction) =");
+               }
+
+               else
+                  pos = safecat(msg, sizeof msg, pos,
+                     " [no gamma correction] =");
+
+               if (pass != 0)
+                  pos = safecat(msg, sizeof msg, pos, "]");
+            }
+
+            /* This is our calculated encoded_sample which should (but does
+             * not) match od:
+             */
+            pos = safecat(msg, sizeof msg, pos, pass != 0 ? "\n\t" : " ");
+            pos = safecatd(msg, sizeof msg, pos, is_lo, 1);
+            pos = safecat(msg, sizeof msg, pos, " < ");
+            pos = safecatd(msg, sizeof msg, pos, encoded_sample, 1);
+            pos = safecat(msg, sizeof msg, pos, " (libpng: ");
+            pos = safecatn(msg, sizeof msg, pos, od);
+            pos = safecat(msg, sizeof msg, pos, ")");
+            pos = safecat(msg, sizeof msg, pos, "/");
+            pos = safecatn(msg, sizeof msg, pos, outmax);
+            pos = safecat(msg, sizeof msg, pos, " < ");
+            pos = safecatd(msg, sizeof msg, pos, is_hi, 1);
+
+            if (pass == 0) /* The error condition */
+            {
+#              ifdef PNG_WARNINGS_SUPPORTED
+                  png_warning(vi->pp, msg);
+#              else
+                  store_warning(vi->pp, msg);
+#              endif
+            }
+
+            else /* logging this value */
+               store_verbose(&vi->dp->pm->this, vi->pp, pass, msg);
+         }
+      }
+   }
+
+   return i;
+}
+
+static void
+gamma_image_validate(gamma_display *dp, png_const_structp pp,
+   png_infop pi)
+{
+   /* Get some constants derived from the input and output file formats: */
+   const png_store* const ps = dp->this.ps;
+   const png_byte in_ct = dp->this.colour_type;
+   const png_byte in_bd = dp->this.bit_depth;
+   const png_uint_32 w = dp->this.w;
+   const png_uint_32 h = dp->this.h;
+   const size_t cbRow = dp->this.cbRow;
+   const png_byte out_ct = png_get_color_type(pp, pi);
+   const png_byte out_bd = png_get_bit_depth(pp, pi);
+
+   /* There are three sources of error, firstly the quantization in the
+    * file encoding, determined by sbit and/or the file depth, secondly
+    * the output (screen) gamma and thirdly the output file encoding.
+    *
+    * Since this API receives the screen and file gamma in double
+    * precision it is possible to calculate an exact answer given an input
+    * pixel value.  Therefore we assume that the *input* value is exact -
+    * sample/maxsample - calculate the corresponding gamma corrected
+    * output to the limits of double precision arithmetic and compare with
+    * what libpng returns.
+    *
+    * Since the library must quantize the output to 8 or 16 bits there is
+    * a fundamental limit on the accuracy of the output of +/-.5 - this
+    * quantization limit is included in addition to the other limits
+    * specified by the paramaters to the API.  (Effectively, add .5
+    * everywhere.)
+    *
+    * The behavior of the 'sbit' paramter is defined by section 12.5
+    * (sample depth scaling) of the PNG spec.  That section forces the
+    * decoder to assume that the PNG values have been scaled if sBIT is
+    * present:
+    *
+    *     png-sample = floor( input-sample * (max-out/max-in) + .5);
+    *
+    * This means that only a subset of the possible PNG values should
+    * appear in the input. However, the spec allows the encoder to use a
+    * variety of approximations to the above and doesn't require any
+    * restriction of the values produced.
+    *
+    * Nevertheless the spec requires that the upper 'sBIT' bits of the
+    * value stored in a PNG file be the original sample bits.
+    * Consequently the code below simply scales the top sbit bits by
+    * (1<<sbit)-1 to obtain an original sample value.
+    *
+    * Because there is limited precision in the input it is arguable that
+    * an acceptable result is any valid result from input-.5 to input+.5.
+    * The basic tests below do not do this, however if 'use_input_precision'
+    * is set a subsequent test is performed above.
+    */
+   const unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
+   int processing;
+   png_uint_32 y;
+   const store_palette_entry *in_palette = dp->this.palette;
+   const int in_is_transparent = dp->this.is_transparent;
+   int process_tRNS;
+   int out_npalette = -1;
+   int out_is_transparent = 0; /* Just refers to the palette case */
+   store_palette out_palette;
+   validate_info vi;
+
+   /* Check for row overwrite errors */
+   store_image_check(dp->this.ps, pp, 0);
+
+   /* Supply the input and output sample depths here - 8 for an indexed image,
+    * otherwise the bit depth.
+    */
+   init_validate_info(&vi, dp, pp, in_ct==3?8:in_bd, out_ct==3?8:out_bd);
+
+   processing = (vi.gamma_correction > 0 && !dp->threshold_test)
+      || in_bd != out_bd || in_ct != out_ct || vi.do_background;
+   process_tRNS = dp->this.has_tRNS && vi.do_background;
+
+   /* TODO: FIX THIS: MAJOR BUG!  If the transformations all happen inside
+    * the palette there is no way of finding out, because libpng fails to
+    * update the palette on png_read_update_info.  Indeed, libpng doesn't
+    * even do the required work until much later, when it doesn't have any
+    * info pointer.  Oops.  For the moment 'processing' is turned off if
+    * out_ct is palette.
+    */
+   if (in_ct == 3 && out_ct == 3)
+      processing = 0;
+
+   if (processing && out_ct == 3)
+      out_is_transparent = read_palette(out_palette, &out_npalette, pp, pi);
+
+   for (y=0; y<h; ++y)
+   {
+      png_const_bytep pRow = store_image_row(ps, pp, 0, y);
+      png_byte std[STANDARD_ROWMAX];
+
+      transform_row(pp, std, in_ct, in_bd, y);
+
+      if (processing)
+      {
+         unsigned int x;
+
+         for (x=0; x<w; ++x)
+         {
+            double alpha = 1; /* serves as a flag value */
+
+            /* Record the palette index for index images. */
+            const unsigned int in_index =
+               in_ct == 3 ? sample(std, 3, in_bd, x, 0, 0, 0) : 256;
+            const unsigned int out_index =
+               out_ct == 3 ? sample(std, 3, out_bd, x, 0, 0, 0) : 256;
+
+            /* Handle input alpha - png_set_background will cause the output
+             * alpha to disappear so there is nothing to check.
+             */
+            if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 ||
+                (in_ct == 3 && in_is_transparent))
+            {
+               const unsigned int input_alpha = in_ct == 3 ?
+                  dp->this.palette[in_index].alpha :
+                  sample(std, in_ct, in_bd, x, samples_per_pixel, 0, 0);
+
+               unsigned int output_alpha = 65536 /* as a flag value */;
+
+               if (out_ct == 3)
+               {
+                  if (out_is_transparent)
+                     output_alpha = out_palette[out_index].alpha;
+               }
+
+               else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0)
+                  output_alpha = sample(pRow, out_ct, out_bd, x,
+                     samples_per_pixel, 0, 0);
+
+               if (output_alpha != 65536)
+                  alpha = gamma_component_validate("alpha", &vi, input_alpha,
+                     output_alpha, -1/*alpha*/, 0/*background*/);
+
+               else /* no alpha in output */
+               {
+                  /* This is a copy of the calculation of 'i' above in order to
+                   * have the alpha value to use in the background calculation.
+                   */
+                  alpha = input_alpha >> vi.isbit_shift;
+                  alpha /= vi.sbit_max;
+               }
+            }
+
+            else if (process_tRNS)
+            {
+               /* alpha needs to be set appropriately for this pixel, it is
+                * currently 1 and needs to be 0 for an input pixel which matches
+                * the values in tRNS.
+                */
+               switch (in_ct)
+               {
+                  case 0: /* gray */
+                     if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
+                           dp->this.transparent.red)
+                        alpha = 0;
+                     break;
+
+                  case 2: /* RGB */
+                     if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
+                           dp->this.transparent.red &&
+                         sample(std, in_ct, in_bd, x, 1, 0, 0) ==
+                           dp->this.transparent.green &&
+                         sample(std, in_ct, in_bd, x, 2, 0, 0) ==
+                           dp->this.transparent.blue)
+                        alpha = 0;
+                     break;
+
+                  default:
+                     break;
+               }
+            }
+
+            /* Handle grayscale or RGB components. */
+            if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */
+               (void)gamma_component_validate("gray", &vi,
+                  sample(std, in_ct, in_bd, x, 0, 0, 0),
+                  sample(pRow, out_ct, out_bd, x, 0, 0, 0),
+                  alpha/*component*/, vi.background_red);
+            else /* RGB or palette */
+            {
+               (void)gamma_component_validate("red", &vi,
+                  in_ct == 3 ? in_palette[in_index].red :
+                     sample(std, in_ct, in_bd, x, 0, 0, 0),
+                  out_ct == 3 ? out_palette[out_index].red :
+                     sample(pRow, out_ct, out_bd, x, 0, 0, 0),
+                  alpha/*component*/, vi.background_red);
+
+               (void)gamma_component_validate("green", &vi,
+                  in_ct == 3 ? in_palette[in_index].green :
+                     sample(std, in_ct, in_bd, x, 1, 0, 0),
+                  out_ct == 3 ? out_palette[out_index].green :
+                     sample(pRow, out_ct, out_bd, x, 1, 0, 0),
+                  alpha/*component*/, vi.background_green);
+
+               (void)gamma_component_validate("blue", &vi,
+                  in_ct == 3 ? in_palette[in_index].blue :
+                     sample(std, in_ct, in_bd, x, 2, 0, 0),
+                  out_ct == 3 ? out_palette[out_index].blue :
+                     sample(pRow, out_ct, out_bd, x, 2, 0, 0),
+                  alpha/*component*/, vi.background_blue);
+            }
+         }
+      }
+
+      else if (memcmp(std, pRow, cbRow) != 0)
+      {
+         char msg[64];
+
+         /* No transform is expected on the threshold tests. */
+         sprintf(msg, "gamma: below threshold row %lu changed",
+            (unsigned long)y);
+
+         png_error(pp, msg);
+      }
+   } /* row (y) loop */
+
+   dp->this.ps->validated = 1;
+}
+
+static void PNGCBAPI
+gamma_end(png_structp ppIn, png_infop pi)
+{
+   png_const_structp pp = ppIn;
+   gamma_display *dp = voidcast(gamma_display*, png_get_progressive_ptr(pp));
+
+   if (!dp->this.speed)
+      gamma_image_validate(dp, pp, pi);
+   else
+      dp->this.ps->validated = 1;
+}
+
+/* A single test run checking a gamma transformation.
+ *
+ * maxabs: maximum absolute error as a fraction
+ * maxout: maximum output error in the output units
+ * maxpc:  maximum percentage error (as a percentage)
+ */
+static void
+gamma_test(png_modifier *pmIn, const png_byte colour_typeIn,
+    const png_byte bit_depthIn, const int palette_numberIn,
+    const int interlace_typeIn,
+    const double file_gammaIn, const double screen_gammaIn,
+    const png_byte sbitIn, const int threshold_testIn,
+    const char *name,
+    const int use_input_precisionIn, const int scale16In,
+    const int expand16In, const int do_backgroundIn,
+    const png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
+{
+   gamma_display d;
+   context(&pmIn->this, fault);
+
+   gamma_display_init(&d, pmIn, FILEID(colour_typeIn, bit_depthIn,
+      palette_numberIn, interlace_typeIn, 0, 0, 0),
+      file_gammaIn, screen_gammaIn, sbitIn,
+      threshold_testIn, use_input_precisionIn, scale16In,
+      expand16In, do_backgroundIn, bkgd_colorIn, bkgd_gammaIn);
+
+   Try
+   {
+      png_structp pp;
+      png_infop pi;
+      gama_modification gama_mod;
+      srgb_modification srgb_mod;
+      sbit_modification sbit_mod;
+
+      /* For the moment don't use the png_modifier support here. */
+      d.pm->encoding_counter = 0;
+      modifier_set_encoding(d.pm); /* Just resets everything */
+      d.pm->current_gamma = d.file_gamma;
+
+      /* Make an appropriate modifier to set the PNG file gamma to the
+       * given gamma value and the sBIT chunk to the given precision.
+       */
+      d.pm->modifications = NULL;
+      gama_modification_init(&gama_mod, d.pm, d.file_gamma);
+      srgb_modification_init(&srgb_mod, d.pm, 127 /*delete*/);
+      if (d.sbit > 0)
+         sbit_modification_init(&sbit_mod, d.pm, d.sbit);
+
+      modification_reset(d.pm->modifications);
+
+      /* Get a png_struct for reading the image. */
+      pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
+      standard_palette_init(&d.this);
+
+      /* Introduce the correct read function. */
+      if (d.pm->this.progressive)
+      {
+         /* Share the row function with the standard implementation. */
+         png_set_progressive_read_fn(pp, &d, gamma_info, progressive_row,
+            gamma_end);
+
+         /* Now feed data into the reader until we reach the end: */
+         modifier_progressive_read(d.pm, pp, pi);
+      }
+      else
+      {
+         /* modifier_read expects a png_modifier* */
+         png_set_read_fn(pp, d.pm, modifier_read);
+
+         /* Check the header values: */
+         png_read_info(pp, pi);
+
+         /* Process the 'info' requirements. Only one image is generated */
+         gamma_info_imp(&d, pp, pi);
+
+         sequential_row(&d.this, pp, pi, -1, 0);
+
+         if (!d.this.speed)
+            gamma_image_validate(&d, pp, pi);
+         else
+            d.this.ps->validated = 1;
+      }
+
+      modifier_reset(d.pm);
+
+      if (d.pm->log && !d.threshold_test && !d.this.speed)
+         fprintf(stderr, "%d bit %s %s: max error %f (%.2g, %2g%%)\n",
+            d.this.bit_depth, colour_types[d.this.colour_type], name,
+            d.maxerrout, d.maxerrabs, 100*d.maxerrpc);
+
+      /* Log the summary values too. */
+      if (d.this.colour_type == 0 || d.this.colour_type == 4)
+      {
+         switch (d.this.bit_depth)
+         {
+         case 1:
+            break;
+
+         case 2:
+            if (d.maxerrout > d.pm->error_gray_2)
+               d.pm->error_gray_2 = d.maxerrout;
+
+            break;
+
+         case 4:
+            if (d.maxerrout > d.pm->error_gray_4)
+               d.pm->error_gray_4 = d.maxerrout;
+
+            break;
+
+         case 8:
+            if (d.maxerrout > d.pm->error_gray_8)
+               d.pm->error_gray_8 = d.maxerrout;
+
+            break;
+
+         case 16:
+            if (d.maxerrout > d.pm->error_gray_16)
+               d.pm->error_gray_16 = d.maxerrout;
+
+            break;
+
+         default:
+            png_error(pp, "bad bit depth (internal: 1)");
+         }
+      }
+
+      else if (d.this.colour_type == 2 || d.this.colour_type == 6)
+      {
+         switch (d.this.bit_depth)
+         {
+         case 8:
+
+            if (d.maxerrout > d.pm->error_color_8)
+               d.pm->error_color_8 = d.maxerrout;
+
+            break;
+
+         case 16:
+
+            if (d.maxerrout > d.pm->error_color_16)
+               d.pm->error_color_16 = d.maxerrout;
+
+            break;
+
+         default:
+            png_error(pp, "bad bit depth (internal: 2)");
+         }
+      }
+
+      else if (d.this.colour_type == 3)
+      {
+         if (d.maxerrout > d.pm->error_indexed)
+            d.pm->error_indexed = d.maxerrout;
+      }
+   }
+
+   Catch(fault)
+      modifier_reset(voidcast(png_modifier*,(void*)fault));
+}
+
+static void gamma_threshold_test(png_modifier *pm, png_byte colour_type,
+    png_byte bit_depth, int interlace_type, double file_gamma,
+    double screen_gamma)
+{
+   size_t pos = 0;
+   char name[64];
+   pos = safecat(name, sizeof name, pos, "threshold ");
+   pos = safecatd(name, sizeof name, pos, file_gamma, 3);
+   pos = safecat(name, sizeof name, pos, "/");
+   pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
+
+   (void)gamma_test(pm, colour_type, bit_depth, 0/*palette*/, interlace_type,
+      file_gamma, screen_gamma, 0/*sBIT*/, 1/*threshold test*/, name,
+      0 /*no input precision*/,
+      0 /*no scale16*/, 0 /*no expand16*/, 0 /*no background*/, 0 /*hence*/,
+      0 /*no background gamma*/);
+}
+
+static void
+perform_gamma_threshold_tests(png_modifier *pm)
+{
+   png_byte colour_type = 0;
+   png_byte bit_depth = 0;
+   unsigned int palette_number = 0;
+
+   /* Don't test more than one instance of each palette - it's pointless, in
+    * fact this test is somewhat excessive since libpng doesn't make this
+    * decision based on colour type or bit depth!
+    *
+    * CHANGED: now test two palettes and, as a side effect, images with and
+    * without tRNS.
+    */
+   while (next_format(&colour_type, &bit_depth, &palette_number,
+                      pm->test_lbg_gamma_threshold, pm->test_tRNS))
+      if (palette_number < 2)
+   {
+      double test_gamma = 1.0;
+      while (test_gamma >= .4)
+      {
+         /* There's little point testing the interlacing vs non-interlacing,
+          * but this can be set from the command line.
+          */
+         gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
+            test_gamma, 1/test_gamma);
+         test_gamma *= .95;
+      }
+
+      /* And a special test for sRGB */
+      gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
+          .45455, 2.2);
+
+      if (fail(pm))
+         return;
+   }
+}
+
+static void gamma_transform_test(png_modifier *pm,
+   const png_byte colour_type, const png_byte bit_depth,
+   const int palette_number,
+   const int interlace_type, const double file_gamma,
+   const double screen_gamma, const png_byte sbit,
+   const int use_input_precision, const int scale16)
+{
+   size_t pos = 0;
+   char name[64];
+
+   if (sbit != bit_depth && sbit != 0)
+   {
+      pos = safecat(name, sizeof name, pos, "sbit(");
+      pos = safecatn(name, sizeof name, pos, sbit);
+      pos = safecat(name, sizeof name, pos, ") ");
+   }
+
+   else
+      pos = safecat(name, sizeof name, pos, "gamma ");
+
+   if (scale16)
+      pos = safecat(name, sizeof name, pos, "16to8 ");
+
+   pos = safecatd(name, sizeof name, pos, file_gamma, 3);
+   pos = safecat(name, sizeof name, pos, "->");
+   pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
+
+   gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
+      file_gamma, screen_gamma, sbit, 0, name, use_input_precision,
+      scale16, pm->test_gamma_expand16, 0 , 0, 0);
+}
+
+static void perform_gamma_transform_tests(png_modifier *pm)
+{
+   png_byte colour_type = 0;
+   png_byte bit_depth = 0;
+   unsigned int palette_number = 0;
+
+   while (next_format(&colour_type, &bit_depth, &palette_number,
+                      pm->test_lbg_gamma_transform, pm->test_tRNS))
+   {
+      unsigned int i, j;
+
+      for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j)
+         if (i != j)
+         {
+            gamma_transform_test(pm, colour_type, bit_depth, palette_number,
+               pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], 0/*sBIT*/,
+               pm->use_input_precision, 0 /*do not scale16*/);
+
+            if (fail(pm))
+               return;
+         }
+   }
+}
+
+static void perform_gamma_sbit_tests(png_modifier *pm)
+{
+   png_byte sbit;
+
+   /* The only interesting cases are colour and grayscale, alpha is ignored here
+    * for overall speed.  Only bit depths where sbit is less than the bit depth
+    * are tested.
+    */
+   for (sbit=pm->sbitlow; sbit<(1<<READ_BDHI); ++sbit)
+   {
+      png_byte colour_type = 0, bit_depth = 0;
+      unsigned int npalette = 0;
+
+      while (next_format(&colour_type, &bit_depth, &npalette,
+                         pm->test_lbg_gamma_sbit, pm->test_tRNS))
+         if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
+            ((colour_type == 3 && sbit < 8) ||
+            (colour_type != 3 && sbit < bit_depth)))
+      {
+         unsigned int i;
+
+         for (i=0; i<pm->ngamma_tests; ++i)
+         {
+            unsigned int j;
+
+            for (j=0; j<pm->ngamma_tests; ++j) if (i != j)
+            {
+               gamma_transform_test(pm, colour_type, bit_depth, npalette,
+                  pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+                  sbit, pm->use_input_precision_sbit, 0 /*scale16*/);
+
+               if (fail(pm))
+                  return;
+            }
+         }
+      }
+   }
+}
+
+/* Note that this requires a 16 bit source image but produces 8 bit output, so
+ * we only need the 16bit write support, but the 16 bit images are only
+ * generated if DO_16BIT is defined.
+ */
+#ifdef DO_16BIT
+static void perform_gamma_scale16_tests(png_modifier *pm)
+{
+#  ifndef PNG_MAX_GAMMA_8
+#     define PNG_MAX_GAMMA_8 11
+#  endif
+#  if defined PNG_MAX_GAMMA_8 || PNG_LIBPNG_VER < 10700
+#     define SBIT_16_TO_8 PNG_MAX_GAMMA_8
+#  else
+#     define SBIT_16_TO_8 16
+#  endif
+   /* Include the alpha cases here. Note that sbit matches the internal value
+    * used by the library - otherwise we will get spurious errors from the
+    * internal sbit style approximation.
+    *
+    * The threshold test is here because otherwise the 16 to 8 conversion will
+    * proceed *without* gamma correction, and the tests above will fail (but not
+    * by much) - this could be fixed, it only appears with the -g option.
+    */
+   unsigned int i, j;
+   for (i=0; i<pm->ngamma_tests; ++i)
+   {
+      for (j=0; j<pm->ngamma_tests; ++j)
+      {
+         if (i != j &&
+             fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD)
+         {
+            gamma_transform_test(pm, 0, 16, 0, pm->interlace_type,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+               pm->use_input_precision_16to8, 1 /*scale16*/);
+
+            if (fail(pm))
+               return;
+
+            gamma_transform_test(pm, 2, 16, 0, pm->interlace_type,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+               pm->use_input_precision_16to8, 1 /*scale16*/);
+
+            if (fail(pm))
+               return;
+
+            gamma_transform_test(pm, 4, 16, 0, pm->interlace_type,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+               pm->use_input_precision_16to8, 1 /*scale16*/);
+
+            if (fail(pm))
+               return;
+
+            gamma_transform_test(pm, 6, 16, 0, pm->interlace_type,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+               pm->use_input_precision_16to8, 1 /*scale16*/);
+
+            if (fail(pm))
+               return;
+         }
+      }
+   }
+}
+#endif /* 16 to 8 bit conversion */
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+   defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+static void gamma_composition_test(png_modifier *pm,
+   const png_byte colour_type, const png_byte bit_depth,
+   const int palette_number,
+   const int interlace_type, const double file_gamma,
+   const double screen_gamma,
+   const int use_input_precision, const int do_background,
+   const int expand_16)
+{
+   size_t pos = 0;
+   png_const_charp base;
+   double bg;
+   char name[128];
+   png_color_16 background;
+
+   /* Make up a name and get an appropriate background gamma value. */
+   switch (do_background)
+   {
+      default:
+         base = "";
+         bg = 4; /* should not be used */
+         break;
+      case PNG_BACKGROUND_GAMMA_SCREEN:
+         base = " bckg(Screen):";
+         bg = 1/screen_gamma;
+         break;
+      case PNG_BACKGROUND_GAMMA_FILE:
+         base = " bckg(File):";
+         bg = file_gamma;
+         break;
+      case PNG_BACKGROUND_GAMMA_UNIQUE:
+         base = " bckg(Unique):";
+         /* This tests the handling of a unique value, the math is such that the
+          * value tends to be <1, but is neither screen nor file (even if they
+          * match!)
+          */
+         bg = (file_gamma + screen_gamma) / 3;
+         break;
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_PNG:
+         base = " alpha(PNG)";
+         bg = 4; /* should not be used */
+         break;
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
+         base = " alpha(Porter-Duff)";
+         bg = 4; /* should not be used */
+         break;
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
+         base = " alpha(Optimized)";
+         bg = 4; /* should not be used */
+         break;
+      case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
+         base = " alpha(Broken)";
+         bg = 4; /* should not be used */
+         break;
+#endif
+   }
+
+   /* Use random background values - the background is always presented in the
+    * output space (8 or 16 bit components).
+    */
+   if (expand_16 || bit_depth == 16)
+   {
+      png_uint_32 r = random_32();
+
+      background.red = (png_uint_16)r;
+      background.green = (png_uint_16)(r >> 16);
+      r = random_32();
+      background.blue = (png_uint_16)r;
+      background.gray = (png_uint_16)(r >> 16);
+
+      /* In earlier libpng versions, those where DIGITIZE is set, any background
+       * gamma correction in the expand16 case was done using 8-bit gamma
+       * correction tables, resulting in larger errors.  To cope with those
+       * cases use a 16-bit background value which will handle this gamma
+       * correction.
+       */
+#     if DIGITIZE
+         if (expand_16 && (do_background == PNG_BACKGROUND_GAMMA_UNIQUE ||
+                           do_background == PNG_BACKGROUND_GAMMA_FILE) &&
+            fabs(bg*screen_gamma-1) > PNG_GAMMA_THRESHOLD)
+         {
+            /* The background values will be looked up in an 8-bit table to do
+             * the gamma correction, so only select values which are an exact
+             * match for the 8-bit table entries:
+             */
+            background.red = (png_uint_16)((background.red >> 8) * 257);
+            background.green = (png_uint_16)((background.green >> 8) * 257);
+            background.blue = (png_uint_16)((background.blue >> 8) * 257);
+            background.gray = (png_uint_16)((background.gray >> 8) * 257);
+         }
+#     endif
+   }
+
+   else /* 8 bit colors */
+   {
+      png_uint_32 r = random_32();
+
+      background.red = (png_byte)r;
+      background.green = (png_byte)(r >> 8);
+      background.blue = (png_byte)(r >> 16);
+      background.gray = (png_byte)(r >> 24);
+   }
+
+   background.index = 193; /* rgb(193,193,193) to detect errors */
+
+   if (!(colour_type & PNG_COLOR_MASK_COLOR))
+   {
+      /* Because, currently, png_set_background is always called with
+       * 'need_expand' false in this case and because the gamma test itself
+       * doesn't cause an expand to 8-bit for lower bit depths the colour must
+       * be reduced to the correct range.
+       */
+      if (bit_depth < 8)
+         background.gray &= (png_uint_16)((1U << bit_depth)-1);
+
+      /* Grayscale input, we do not convert to RGB (TBD), so we must set the
+       * background to gray - else libpng seems to fail.
+       */
+      background.red = background.green = background.blue = background.gray;
+   }
+
+   pos = safecat(name, sizeof name, pos, "gamma ");
+   pos = safecatd(name, sizeof name, pos, file_gamma, 3);
+   pos = safecat(name, sizeof name, pos, "->");
+   pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
+
+   pos = safecat(name, sizeof name, pos, base);
+   if (do_background < ALPHA_MODE_OFFSET)
+   {
+      /* Include the background color and gamma in the name: */
+      pos = safecat(name, sizeof name, pos, "(");
+      /* This assumes no expand gray->rgb - the current code won't handle that!
+       */
+      if (colour_type & PNG_COLOR_MASK_COLOR)
+      {
+         pos = safecatn(name, sizeof name, pos, background.red);
+         pos = safecat(name, sizeof name, pos, ",");
+         pos = safecatn(name, sizeof name, pos, background.green);
+         pos = safecat(name, sizeof name, pos, ",");
+         pos = safecatn(name, sizeof name, pos, background.blue);
+      }
+      else
+         pos = safecatn(name, sizeof name, pos, background.gray);
+      pos = safecat(name, sizeof name, pos, ")^");
+      pos = safecatd(name, sizeof name, pos, bg, 3);
+   }
+
+   gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
+      file_gamma, screen_gamma, 0/*sBIT*/, 0, name, use_input_precision,
+      0/*strip 16*/, expand_16, do_background, &background, bg);
+}
+
+
+static void
+perform_gamma_composition_tests(png_modifier *pm, int do_background,
+   int expand_16)
+{
+   png_byte colour_type = 0;
+   png_byte bit_depth = 0;
+   unsigned int palette_number = 0;
+
+   /* Skip the non-alpha cases - there is no setting of a transparency colour at
+    * present.
+    *
+    * TODO: incorrect; the palette case sets tRNS and, now RGB and gray do,
+    * however the palette case fails miserably so is commented out below.
+    */
+   while (next_format(&colour_type, &bit_depth, &palette_number,
+                      pm->test_lbg_gamma_composition, pm->test_tRNS))
+      if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0
+#if 0 /* TODO: FIXME */
+          /*TODO: FIXME: this should work */
+          || colour_type == 3
+#endif
+          || (colour_type != 3 && palette_number != 0))
+   {
+      unsigned int i, j;
+
+      /* Don't skip the i==j case here - it's relevant. */
+      for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j)
+      {
+         gamma_composition_test(pm, colour_type, bit_depth, palette_number,
+            pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+            pm->use_input_precision, do_background, expand_16);
+
+         if (fail(pm))
+            return;
+      }
+   }
+}
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
+
+static void
+init_gamma_errors(png_modifier *pm)
+{
+   /* Use -1 to catch tests that were not actually run */
+   pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = -1.;
+   pm->error_color_8 = -1.;
+   pm->error_indexed = -1.;
+   pm->error_gray_16 = pm->error_color_16 = -1.;
+}
+
+static void
+print_one(const char *leader, double err)
+{
+   if (err != -1.)
+      printf(" %s %.5f\n", leader, err);
+}
+
+static void
+summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth,
+   int indexed)
+{
+   fflush(stderr);
+
+   if (who)
+      printf("\nGamma correction with %s:\n", who);
+
+   else
+      printf("\nBasic gamma correction:\n");
+
+   if (low_bit_depth)
+   {
+      print_one(" 2 bit gray: ", pm->error_gray_2);
+      print_one(" 4 bit gray: ", pm->error_gray_4);
+      print_one(" 8 bit gray: ", pm->error_gray_8);
+      print_one(" 8 bit color:", pm->error_color_8);
+      if (indexed)
+         print_one(" indexed:    ", pm->error_indexed);
+   }
+
+   print_one("16 bit gray: ", pm->error_gray_16);
+   print_one("16 bit color:", pm->error_color_16);
+
+   fflush(stdout);
+}
+
+static void
+perform_gamma_test(png_modifier *pm, int summary)
+{
+   /*TODO: remove this*/
+   /* Save certain values for the temporary overrides below. */
+   unsigned int calculations_use_input_precision =
+      pm->calculations_use_input_precision;
+#  ifdef PNG_READ_BACKGROUND_SUPPORTED
+      double maxout8 = pm->maxout8;
+#  endif
+
+   /* First some arbitrary no-transform tests: */
+   if (!pm->this.speed && pm->test_gamma_threshold)
+   {
+      perform_gamma_threshold_tests(pm);
+
+      if (fail(pm))
+         return;
+   }
+
+   /* Now some real transforms. */
+   if (pm->test_gamma_transform)
+   {
+      if (summary)
+      {
+         fflush(stderr);
+         printf("Gamma correction error summary\n\n");
+         printf("The printed value is the maximum error in the pixel values\n");
+         printf("calculated by the libpng gamma correction code.  The error\n");
+         printf("is calculated as the difference between the output pixel\n");
+         printf("value (always an integer) and the ideal value from the\n");
+         printf("libpng specification (typically not an integer).\n\n");
+
+         printf("Expect this value to be less than .5 for 8 bit formats,\n");
+         printf("less than 1 for formats with fewer than 8 bits and a small\n");
+         printf("number (typically less than 5) for the 16 bit formats.\n");
+         printf("For performance reasons the value for 16 bit formats\n");
+         printf("increases when the image file includes an sBIT chunk.\n");
+         fflush(stdout);
+      }
+
+      init_gamma_errors(pm);
+      /*TODO: remove this.  Necessary because the current libpng
+       * implementation works in 8 bits:
+       */
+      if (pm->test_gamma_expand16)
+         pm->calculations_use_input_precision = 1;
+      perform_gamma_transform_tests(pm);
+      if (!calculations_use_input_precision)
+         pm->calculations_use_input_precision = 0;
+
+      if (summary)
+         summarize_gamma_errors(pm, 0/*who*/, 1/*low bit depth*/, 1/*indexed*/);
+
+      if (fail(pm))
+         return;
+   }
+
+   /* The sbit tests produce much larger errors: */
+   if (pm->test_gamma_sbit)
+   {
+      init_gamma_errors(pm);
+      perform_gamma_sbit_tests(pm);
+
+      if (summary)
+         summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U, 1/*indexed*/);
+
+      if (fail(pm))
+         return;
+   }
+
+#ifdef DO_16BIT /* Should be READ_16BIT_SUPPORTED */
+   if (pm->test_gamma_scale16)
+   {
+      /* The 16 to 8 bit strip operations: */
+      init_gamma_errors(pm);
+      perform_gamma_scale16_tests(pm);
+
+      if (summary)
+      {
+         fflush(stderr);
+         printf("\nGamma correction with 16 to 8 bit reduction:\n");
+         printf(" 16 bit gray:  %.5f\n", pm->error_gray_16);
+         printf(" 16 bit color: %.5f\n", pm->error_color_16);
+         fflush(stdout);
+      }
+
+      if (fail(pm))
+         return;
+   }
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+   if (pm->test_gamma_background)
+   {
+      init_gamma_errors(pm);
+
+      /*TODO: remove this.  Necessary because the current libpng
+       * implementation works in 8 bits:
+       */
+      if (pm->test_gamma_expand16)
+      {
+         pm->calculations_use_input_precision = 1;
+         pm->maxout8 = .499; /* because the 16 bit background is smashed */
+      }
+      perform_gamma_composition_tests(pm, PNG_BACKGROUND_GAMMA_UNIQUE,
+         pm->test_gamma_expand16);
+      if (!calculations_use_input_precision)
+         pm->calculations_use_input_precision = 0;
+      pm->maxout8 = maxout8;
+
+      if (summary)
+         summarize_gamma_errors(pm, "background", 1, 0/*indexed*/);
+
+      if (fail(pm))
+         return;
+   }
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+   if (pm->test_gamma_alpha_mode)
+   {
+      int do_background;
+
+      init_gamma_errors(pm);
+
+      /*TODO: remove this.  Necessary because the current libpng
+       * implementation works in 8 bits:
+       */
+      if (pm->test_gamma_expand16)
+         pm->calculations_use_input_precision = 1;
+      for (do_background = ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD;
+         do_background <= ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN && !fail(pm);
+         ++do_background)
+         perform_gamma_composition_tests(pm, do_background,
+            pm->test_gamma_expand16);
+      if (!calculations_use_input_precision)
+         pm->calculations_use_input_precision = 0;
+
+      if (summary)
+         summarize_gamma_errors(pm, "alpha mode", 1, 0/*indexed*/);
+
+      if (fail(pm))
+         return;
+   }
+#endif
+}
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED */
+
+/* INTERLACE MACRO VALIDATION */
+/* This is copied verbatim from the specification, it is simply the pass
+ * number in which each pixel in each 8x8 tile appears.  The array must
+ * be indexed adam7[y][x] and notice that the pass numbers are based at
+ * 1, not 0 - the base libpng uses.
+ */
+static const
+png_byte adam7[8][8] =
+{
+   { 1,6,4,6,2,6,4,6 },
+   { 7,7,7,7,7,7,7,7 },
+   { 5,6,5,6,5,6,5,6 },
+   { 7,7,7,7,7,7,7,7 },
+   { 3,6,4,6,3,6,4,6 },
+   { 7,7,7,7,7,7,7,7 },
+   { 5,6,5,6,5,6,5,6 },
+   { 7,7,7,7,7,7,7,7 }
+};
+
+/* This routine validates all the interlace support macros in png.h for
+ * a variety of valid PNG widths and heights.  It uses a number of similarly
+ * named internal routines that feed off the above array.
+ */
+static png_uint_32
+png_pass_start_row(int pass)
+{
+   int x, y;
+   ++pass;
+   for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+      return y;
+   return 0xf;
+}
+
+static png_uint_32
+png_pass_start_col(int pass)
+{
+   int x, y;
+   ++pass;
+   for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+      return x;
+   return 0xf;
+}
+
+static int
+png_pass_row_shift(int pass)
+{
+   int x, y, base=(-1), inc=8;
+   ++pass;
+   for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+   {
+      if (base == (-1))
+         base = y;
+      else if (base == y)
+         {}
+      else if (inc == y-base)
+         base=y;
+      else if (inc == 8)
+         inc = y-base, base=y;
+      else if (inc != y-base)
+         return 0xff; /* error - more than one 'inc' value! */
+   }
+
+   if (base == (-1)) return 0xfe; /* error - no row in pass! */
+
+   /* The shift is always 1, 2 or 3 - no pass has all the rows! */
+   switch (inc)
+   {
+case 2: return 1;
+case 4: return 2;
+case 8: return 3;
+default: break;
+   }
+
+   /* error - unrecognized 'inc' */
+   return (inc << 8) + 0xfd;
+}
+
+static int
+png_pass_col_shift(int pass)
+{
+   int x, y, base=(-1), inc=8;
+   ++pass;
+   for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+   {
+      if (base == (-1))
+         base = x;
+      else if (base == x)
+         {}
+      else if (inc == x-base)
+         base=x;
+      else if (inc == 8)
+         inc = x-base, base=x;
+      else if (inc != x-base)
+         return 0xff; /* error - more than one 'inc' value! */
+   }
+
+   if (base == (-1)) return 0xfe; /* error - no row in pass! */
+
+   /* The shift is always 1, 2 or 3 - no pass has all the rows! */
+   switch (inc)
+   {
+case 1: return 0; /* pass 7 has all the columns */
+case 2: return 1;
+case 4: return 2;
+case 8: return 3;
+default: break;
+   }
+
+   /* error - unrecognized 'inc' */
+   return (inc << 8) + 0xfd;
+}
+
+static png_uint_32
+png_row_from_pass_row(png_uint_32 yIn, int pass)
+{
+   /* By examination of the array: */
+   switch (pass)
+   {
+case 0: return yIn * 8;
+case 1: return yIn * 8;
+case 2: return yIn * 8 + 4;
+case 3: return yIn * 4;
+case 4: return yIn * 4 + 2;
+case 5: return yIn * 2;
+case 6: return yIn * 2 + 1;
+default: break;
+   }
+
+   return 0xff; /* bad pass number */
+}
+
+static png_uint_32
+png_col_from_pass_col(png_uint_32 xIn, int pass)
+{
+   /* By examination of the array: */
+   switch (pass)
+   {
+case 0: return xIn * 8;
+case 1: return xIn * 8 + 4;
+case 2: return xIn * 4;
+case 3: return xIn * 4 + 2;
+case 4: return xIn * 2;
+case 5: return xIn * 2 + 1;
+case 6: return xIn;
+default: break;
+   }
+
+   return 0xff; /* bad pass number */
+}
+
+static int
+png_row_in_interlace_pass(png_uint_32 y, int pass)
+{
+   /* Is row 'y' in pass 'pass'? */
+   int x;
+   y &= 7;
+   ++pass;
+   for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+      return 1;
+
+   return 0;
+}
+
+static int
+png_col_in_interlace_pass(png_uint_32 x, int pass)
+{
+   /* Is column 'x' in pass 'pass'? */
+   int y;
+   x &= 7;
+   ++pass;
+   for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+      return 1;
+
+   return 0;
+}
+
+static png_uint_32
+png_pass_rows(png_uint_32 height, int pass)
+{
+   png_uint_32 tiles = height>>3;
+   png_uint_32 rows = 0;
+   unsigned int x, y;
+
+   height &= 7;
+   ++pass;
+   for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+   {
+      rows += tiles;
+      if (y < height) ++rows;
+      break; /* i.e. break the 'x', column, loop. */
+   }
+
+   return rows;
+}
+
+static png_uint_32
+png_pass_cols(png_uint_32 width, int pass)
+{
+   png_uint_32 tiles = width>>3;
+   png_uint_32 cols = 0;
+   unsigned int x, y;
+
+   width &= 7;
+   ++pass;
+   for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+   {
+      cols += tiles;
+      if (x < width) ++cols;
+      break; /* i.e. break the 'y', row, loop. */
+   }
+
+   return cols;
+}
+
+static void
+perform_interlace_macro_validation(void)
+{
+   /* The macros to validate, first those that depend only on pass:
+    *
+    * PNG_PASS_START_ROW(pass)
+    * PNG_PASS_START_COL(pass)
+    * PNG_PASS_ROW_SHIFT(pass)
+    * PNG_PASS_COL_SHIFT(pass)
+    */
+   int pass;
+
+   for (pass=0; pass<7; ++pass)
+   {
+      png_uint_32 m, f, v;
+
+      m = PNG_PASS_START_ROW(pass);
+      f = png_pass_start_row(pass);
+      if (m != f)
+      {
+         fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
+         exit(99);
+      }
+
+      m = PNG_PASS_START_COL(pass);
+      f = png_pass_start_col(pass);
+      if (m != f)
+      {
+         fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
+         exit(99);
+      }
+
+      m = PNG_PASS_ROW_SHIFT(pass);
+      f = png_pass_row_shift(pass);
+      if (m != f)
+      {
+         fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
+         exit(99);
+      }
+
+      m = PNG_PASS_COL_SHIFT(pass);
+      f = png_pass_col_shift(pass);
+      if (m != f)
+      {
+         fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
+         exit(99);
+      }
+
+      /* Macros that depend on the image or sub-image height too:
+       *
+       * PNG_PASS_ROWS(height, pass)
+       * PNG_PASS_COLS(width, pass)
+       * PNG_ROW_FROM_PASS_ROW(yIn, pass)
+       * PNG_COL_FROM_PASS_COL(xIn, pass)
+       * PNG_ROW_IN_INTERLACE_PASS(y, pass)
+       * PNG_COL_IN_INTERLACE_PASS(x, pass)
+       */
+      for (v=0;;)
+      {
+         /* First the base 0 stuff: */
+         m = PNG_ROW_FROM_PASS_ROW(v, pass);
+         f = png_row_from_pass_row(v, pass);
+         if (m != f)
+         {
+            fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
+               v, pass, m, f);
+            exit(99);
+         }
+
+         m = PNG_COL_FROM_PASS_COL(v, pass);
+         f = png_col_from_pass_col(v, pass);
+         if (m != f)
+         {
+            fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
+               v, pass, m, f);
+            exit(99);
+         }
+
+         m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
+         f = png_row_in_interlace_pass(v, pass);
+         if (m != f)
+         {
+            fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
+               v, pass, m, f);
+            exit(99);
+         }
+
+         m = PNG_COL_IN_INTERLACE_PASS(v, pass);
+         f = png_col_in_interlace_pass(v, pass);
+         if (m != f)
+         {
+            fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
+               v, pass, m, f);
+            exit(99);
+         }
+
+         /* Then the base 1 stuff: */
+         ++v;
+         m = PNG_PASS_ROWS(v, pass);
+         f = png_pass_rows(v, pass);
+         if (m != f)
+         {
+            fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
+               v, pass, m, f);
+            exit(99);
+         }
+
+         m = PNG_PASS_COLS(v, pass);
+         f = png_pass_cols(v, pass);
+         if (m != f)
+         {
+            fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
+               v, pass, m, f);
+            exit(99);
+         }
+
+         /* Move to the next v - the stepping algorithm starts skipping
+          * values above 1024.
+          */
+         if (v > 1024)
+         {
+            if (v == PNG_UINT_31_MAX)
+               break;
+
+            v = (v << 1) ^ v;
+            if (v >= PNG_UINT_31_MAX)
+               v = PNG_UINT_31_MAX-1;
+         }
+      }
+   }
+}
+
+/* Test color encodings. These values are back-calculated from the published
+ * chromaticities.  The values are accurate to about 14 decimal places; 15 are
+ * given.  These values are much more accurate than the ones given in the spec,
+ * which typically don't exceed 4 decimal places.  This allows testing of the
+ * libpng code to its theoretical accuracy of 4 decimal places.  (If pngvalid
+ * used the published errors the 'slack' permitted would have to be +/-.5E-4 or
+ * more.)
+ *
+ * The png_modifier code assumes that encodings[0] is sRGB and treats it
+ * specially: do not change the first entry in this list!
+ */
+static const color_encoding test_encodings[] =
+{
+/* sRGB: must be first in this list! */
+/*gamma:*/ { 1/2.2,
+/*red:  */ { 0.412390799265959, 0.212639005871510, 0.019330818715592 },
+/*green:*/ { 0.357584339383878, 0.715168678767756, 0.119194779794626 },
+/*blue: */ { 0.180480788401834, 0.072192315360734, 0.950532152249660} },
+/* Kodak ProPhoto (wide gamut) */
+/*gamma:*/ { 1/1.6 /*approximate: uses 1.8 power law compared to sRGB 2.4*/,
+/*red:  */ { 0.797760489672303, 0.288071128229293, 0.000000000000000 },
+/*green:*/ { 0.135185837175740, 0.711843217810102, 0.000000000000000 },
+/*blue: */ { 0.031349349581525, 0.000085653960605, 0.825104602510460} },
+/* Adobe RGB (1998) */
+/*gamma:*/ { 1/(2+51./256),
+/*red:  */ { 0.576669042910131, 0.297344975250536, 0.027031361386412 },
+/*green:*/ { 0.185558237906546, 0.627363566255466, 0.070688852535827 },
+/*blue: */ { 0.188228646234995, 0.075291458493998, 0.991337536837639} },
+/* Adobe Wide Gamut RGB */
+/*gamma:*/ { 1/(2+51./256),
+/*red:  */ { 0.716500716779386, 0.258728243040113, 0.000000000000000 },
+/*green:*/ { 0.101020574397477, 0.724682314948566, 0.051211818965388 },
+/*blue: */ { 0.146774385252705, 0.016589442011321, 0.773892783545073} },
+/* Fake encoding which selects just the green channel */
+/*gamma:*/ { 1.45/2.2, /* the 'Mac' gamma */
+/*red:  */ { 0.716500716779386, 0.000000000000000, 0.000000000000000 },
+/*green:*/ { 0.101020574397477, 1.000000000000000, 0.051211818965388 },
+/*blue: */ { 0.146774385252705, 0.000000000000000, 0.773892783545073} },
+};
+
+/* signal handler
+ *
+ * This attempts to trap signals and escape without crashing.  It needs a
+ * context pointer so that it can throw an exception (call longjmp) to recover
+ * from the condition; this is handled by making the png_modifier used by 'main'
+ * into a global variable.
+ */
+static png_modifier pm;
+
+static void signal_handler(int signum)
+{
+
+   size_t pos = 0;
+   char msg[64];
+
+   pos = safecat(msg, sizeof msg, pos, "caught signal: ");
+
+   switch (signum)
+   {
+      case SIGABRT:
+         pos = safecat(msg, sizeof msg, pos, "abort");
+         break;
+
+      case SIGFPE:
+         pos = safecat(msg, sizeof msg, pos, "floating point exception");
+         break;
+
+      case SIGILL:
+         pos = safecat(msg, sizeof msg, pos, "illegal instruction");
+         break;
+
+      case SIGINT:
+         pos = safecat(msg, sizeof msg, pos, "interrupt");
+         break;
+
+      case SIGSEGV:
+         pos = safecat(msg, sizeof msg, pos, "invalid memory access");
+         break;
+
+      case SIGTERM:
+         pos = safecat(msg, sizeof msg, pos, "termination request");
+         break;
+
+      default:
+         pos = safecat(msg, sizeof msg, pos, "unknown ");
+         pos = safecatn(msg, sizeof msg, pos, signum);
+         break;
+   }
+
+   store_log(&pm.this, NULL/*png_structp*/, msg, 1/*error*/);
+
+   /* And finally throw an exception so we can keep going, unless this is
+    * SIGTERM in which case stop now.
+    */
+   if (signum != SIGTERM)
+   {
+      struct exception_context *the_exception_context =
+         &pm.this.exception_context;
+
+      Throw &pm.this;
+   }
+
+   else
+      exit(1);
+}
+
+/* main program */
+int main(int argc, char **argv)
+{
+   int summary = 1;  /* Print the error summary at the end */
+   int memstats = 0; /* Print memory statistics at the end */
+
+   /* Create the given output file on success: */
+   const char *touch = NULL;
+
+   /* This is an array of standard gamma values (believe it or not I've seen
+    * every one of these mentioned somewhere.)
+    *
+    * In the following list the most useful values are first!
+    */
+   static double
+      gammas[]={2.2, 1.0, 2.2/1.45, 1.8, 1.5, 2.4, 2.5, 2.62, 2.9};
+
+   /* This records the command and arguments: */
+   size_t cp = 0;
+   char command[1024];
+
+   anon_context(&pm.this);
+
+   gnu_volatile(summary)
+   gnu_volatile(memstats)
+   gnu_volatile(touch)
+
+   /* Add appropriate signal handlers, just the ANSI specified ones: */
+   signal(SIGABRT, signal_handler);
+   signal(SIGFPE, signal_handler);
+   signal(SIGILL, signal_handler);
+   signal(SIGINT, signal_handler);
+   signal(SIGSEGV, signal_handler);
+   signal(SIGTERM, signal_handler);
+
+#ifdef HAVE_FEENABLEEXCEPT
+   /* Only required to enable FP exceptions on platforms where they start off
+    * disabled; this is not necessary but if it is not done pngvalid will likely
+    * end up ignoring FP conditions that other platforms fault.
+    */
+   feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
+   modifier_init(&pm);
+
+   /* Preallocate the image buffer, because we know how big it needs to be,
+    * note that, for testing purposes, it is deliberately mis-aligned by tag
+    * bytes either side.  All rows have an additional five bytes of padding for
+    * overwrite checking.
+    */
+   store_ensure_image(&pm.this, NULL, 2, TRANSFORM_ROWMAX, TRANSFORM_HEIGHTMAX);
+
+   /* Don't give argv[0], it's normally some horrible libtool string: */
+   cp = safecat(command, sizeof command, cp, "pngvalid");
+
+   /* Default to error on warning: */
+   pm.this.treat_warnings_as_errors = 1;
+
+   /* Default assume_16_bit_calculations appropriately; this tells the checking
+    * code that 16-bit arithmetic is used for 8-bit samples when it would make a
+    * difference.
+    */
+   pm.assume_16_bit_calculations = PNG_LIBPNG_VER >= 10700;
+
+   /* Currently 16 bit expansion happens at the end of the pipeline, so the
+    * calculations are done in the input bit depth not the output.
+    *
+    * TODO: fix this
+    */
+   pm.calculations_use_input_precision = 1U;
+
+   /* Store the test gammas */
+   pm.gammas = gammas;
+   pm.ngammas = ARRAY_SIZE(gammas);
+   pm.ngamma_tests = 0; /* default to off */
+
+   /* Low bit depth gray images don't do well in the gamma tests, until
+    * this is fixed turn them off for some gamma cases:
+    */
+#  ifdef PNG_WRITE_tRNS_SUPPORTED
+      pm.test_tRNS = 1;
+#  endif
+   pm.test_lbg = PNG_LIBPNG_VER >= 10600;
+   pm.test_lbg_gamma_threshold = 1;
+   pm.test_lbg_gamma_transform = PNG_LIBPNG_VER >= 10600;
+   pm.test_lbg_gamma_sbit = 1;
+   pm.test_lbg_gamma_composition = PNG_LIBPNG_VER >= 10700;
+
+   /* And the test encodings */
+   pm.encodings = test_encodings;
+   pm.nencodings = ARRAY_SIZE(test_encodings);
+
+#  if PNG_LIBPNG_VER < 10700
+      pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+#  else
+      pm.sbitlow = 1U;
+#  endif
+
+   /* The following allows results to pass if they correspond to anything in the
+    * transformed range [input-.5,input+.5]; this is is required because of the
+    * way libpng treates the 16_TO_8 flag when building the gamma tables in
+    * releases up to 1.6.0.
+    *
+    * TODO: review this
+    */
+   pm.use_input_precision_16to8 = 1U;
+   pm.use_input_precision_sbit = 1U; /* because libpng now rounds sBIT */
+
+   /* Some default values (set the behavior for 'make check' here).
+    * These values simply control the maximum error permitted in the gamma
+    * transformations.  The practial limits for human perception are described
+    * below (the setting for maxpc16), however for 8 bit encodings it isn't
+    * possible to meet the accepted capabilities of human vision - i.e. 8 bit
+    * images can never be good enough, regardless of encoding.
+    */
+   pm.maxout8 = .1;     /* Arithmetic error in *encoded* value */
+   pm.maxabs8 = .00005; /* 1/20000 */
+   pm.maxcalc8 = 1./255;  /* +/-1 in 8 bits for compose errors */
+   pm.maxpc8 = .499;    /* I.e., .499% fractional error */
+   pm.maxout16 = .499;  /* Error in *encoded* value */
+   pm.maxabs16 = .00005;/* 1/20000 */
+   pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */
+#  if PNG_LIBPNG_VER < 10700
+      pm.maxcalcG = 1./((1<<PNG_MAX_GAMMA_8)-1);
+#  else
+      pm.maxcalcG = 1./((1<<16)-1);
+#  endif
+
+   /* NOTE: this is a reasonable perceptual limit. We assume that humans can
+    * perceive light level differences of 1% over a 100:1 range, so we need to
+    * maintain 1 in 10000 accuracy (in linear light space), which is what the
+    * following guarantees.  It also allows significantly higher errors at
+    * higher 16 bit values, which is important for performance.  The actual
+    * maximum 16 bit error is about +/-1.9 in the fixed point implementation but
+    * this is only allowed for values >38149 by the following:
+    */
+   pm.maxpc16 = .005;   /* I.e., 1/200% - 1/20000 */
+
+   /* Now parse the command line options. */
+   while (--argc >= 1)
+   {
+      int catmore = 0; /* Set if the argument has an argument. */
+
+      /* Record each argument for posterity: */
+      cp = safecat(command, sizeof command, cp, " ");
+      cp = safecat(command, sizeof command, cp, *++argv);
+
+      if (strcmp(*argv, "-v") == 0)
+         pm.this.verbose = 1;
+
+      else if (strcmp(*argv, "-l") == 0)
+         pm.log = 1;
+
+      else if (strcmp(*argv, "-q") == 0)
+         summary = pm.this.verbose = pm.log = 0;
+
+      else if (strcmp(*argv, "-w") == 0 ||
+               strcmp(*argv, "--strict") == 0)
+         pm.this.treat_warnings_as_errors = 0;
+
+      else if (strcmp(*argv, "--speed") == 0)
+         pm.this.speed = 1, pm.ngamma_tests = pm.ngammas, pm.test_standard = 0,
+            summary = 0;
+
+      else if (strcmp(*argv, "--memory") == 0)
+         memstats = 1;
+
+      else if (strcmp(*argv, "--size") == 0)
+         pm.test_size = 1;
+
+      else if (strcmp(*argv, "--nosize") == 0)
+         pm.test_size = 0;
+
+      else if (strcmp(*argv, "--standard") == 0)
+         pm.test_standard = 1;
+
+      else if (strcmp(*argv, "--nostandard") == 0)
+         pm.test_standard = 0;
+
+      else if (strcmp(*argv, "--transform") == 0)
+         pm.test_transform = 1;
+
+      else if (strcmp(*argv, "--notransform") == 0)
+         pm.test_transform = 0;
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+      else if (strncmp(*argv, "--transform-disable=",
+         sizeof "--transform-disable") == 0)
+         {
+         pm.test_transform = 1;
+         transform_disable(*argv + sizeof "--transform-disable");
+         }
+
+      else if (strncmp(*argv, "--transform-enable=",
+         sizeof "--transform-enable") == 0)
+         {
+         pm.test_transform = 1;
+         transform_enable(*argv + sizeof "--transform-enable");
+         }
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+      else if (strcmp(*argv, "--gamma") == 0)
+         {
+         /* Just do two gamma tests here (2.2 and linear) for speed: */
+         pm.ngamma_tests = 2U;
+         pm.test_gamma_threshold = 1;
+         pm.test_gamma_transform = 1;
+         pm.test_gamma_sbit = 1;
+         pm.test_gamma_scale16 = 1;
+         pm.test_gamma_background = 1; /* composition */
+         pm.test_gamma_alpha_mode = 1;
+         }
+
+      else if (strcmp(*argv, "--nogamma") == 0)
+         pm.ngamma_tests = 0;
+
+      else if (strcmp(*argv, "--gamma-threshold") == 0)
+         pm.ngamma_tests = 2U, pm.test_gamma_threshold = 1;
+
+      else if (strcmp(*argv, "--nogamma-threshold") == 0)
+         pm.test_gamma_threshold = 0;
+
+      else if (strcmp(*argv, "--gamma-transform") == 0)
+         pm.ngamma_tests = 2U, pm.test_gamma_transform = 1;
+
+      else if (strcmp(*argv, "--nogamma-transform") == 0)
+         pm.test_gamma_transform = 0;
+
+      else if (strcmp(*argv, "--gamma-sbit") == 0)
+         pm.ngamma_tests = 2U, pm.test_gamma_sbit = 1;
+
+      else if (strcmp(*argv, "--nogamma-sbit") == 0)
+         pm.test_gamma_sbit = 0;
+
+      else if (strcmp(*argv, "--gamma-16-to-8") == 0)
+         pm.ngamma_tests = 2U, pm.test_gamma_scale16 = 1;
+
+      else if (strcmp(*argv, "--nogamma-16-to-8") == 0)
+         pm.test_gamma_scale16 = 0;
+
+      else if (strcmp(*argv, "--gamma-background") == 0)
+         pm.ngamma_tests = 2U, pm.test_gamma_background = 1;
+
+      else if (strcmp(*argv, "--nogamma-background") == 0)
+         pm.test_gamma_background = 0;
+
+      else if (strcmp(*argv, "--gamma-alpha-mode") == 0)
+         pm.ngamma_tests = 2U, pm.test_gamma_alpha_mode = 1;
+
+      else if (strcmp(*argv, "--nogamma-alpha-mode") == 0)
+         pm.test_gamma_alpha_mode = 0;
+
+      else if (strcmp(*argv, "--expand16") == 0)
+         pm.test_gamma_expand16 = 1;
+
+      else if (strcmp(*argv, "--noexpand16") == 0)
+         pm.test_gamma_expand16 = 0;
+
+      else if (strcmp(*argv, "--low-depth-gray") == 0)
+         pm.test_lbg = pm.test_lbg_gamma_threshold =
+            pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
+            pm.test_lbg_gamma_composition = 1;
+
+      else if (strcmp(*argv, "--nolow-depth-gray") == 0)
+         pm.test_lbg = pm.test_lbg_gamma_threshold =
+            pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
+            pm.test_lbg_gamma_composition = 0;
+
+#     ifdef PNG_WRITE_tRNS_SUPPORTED
+         else if (strcmp(*argv, "--tRNS") == 0)
+            pm.test_tRNS = 1;
+#     endif
+
+      else if (strcmp(*argv, "--notRNS") == 0)
+         pm.test_tRNS = 0;
+
+      else if (strcmp(*argv, "--more-gammas") == 0)
+         pm.ngamma_tests = 3U;
+
+      else if (strcmp(*argv, "--all-gammas") == 0)
+         pm.ngamma_tests = pm.ngammas;
+
+      else if (strcmp(*argv, "--progressive-read") == 0)
+         pm.this.progressive = 1;
+
+      else if (strcmp(*argv, "--use-update-info") == 0)
+         ++pm.use_update_info; /* Can call multiple times */
+
+      else if (strcmp(*argv, "--interlace") == 0)
+      {
+#        if CAN_WRITE_INTERLACE
+            pm.interlace_type = PNG_INTERLACE_ADAM7;
+#        else /* !CAN_WRITE_INTERLACE */
+            fprintf(stderr, "pngvalid: no write interlace support\n");
+            return SKIP;
+#        endif /* !CAN_WRITE_INTERLACE */
+      }
+
+      else if (strcmp(*argv, "--use-input-precision") == 0)
+         pm.use_input_precision = 1U;
+
+      else if (strcmp(*argv, "--use-calculation-precision") == 0)
+         pm.use_input_precision = 0;
+
+      else if (strcmp(*argv, "--calculations-use-input-precision") == 0)
+         pm.calculations_use_input_precision = 1U;
+
+      else if (strcmp(*argv, "--assume-16-bit-calculations") == 0)
+         pm.assume_16_bit_calculations = 1U;
+
+      else if (strcmp(*argv, "--calculations-follow-bit-depth") == 0)
+         pm.calculations_use_input_precision =
+            pm.assume_16_bit_calculations = 0;
+
+      else if (strcmp(*argv, "--exhaustive") == 0)
+         pm.test_exhaustive = 1;
+
+      else if (argc > 1 && strcmp(*argv, "--sbitlow") == 0)
+         --argc, pm.sbitlow = (png_byte)atoi(*++argv), catmore = 1;
+
+      else if (argc > 1 && strcmp(*argv, "--touch") == 0)
+         --argc, touch = *++argv, catmore = 1;
+
+      else if (argc > 1 && strncmp(*argv, "--max", 5) == 0)
+      {
+         --argc;
+
+         if (strcmp(5+*argv, "abs8") == 0)
+            pm.maxabs8 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "abs16") == 0)
+            pm.maxabs16 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "calc8") == 0)
+            pm.maxcalc8 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "calc16") == 0)
+            pm.maxcalc16 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "out8") == 0)
+            pm.maxout8 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "out16") == 0)
+            pm.maxout16 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "pc8") == 0)
+            pm.maxpc8 = atof(*++argv);
+
+         else if (strcmp(5+*argv, "pc16") == 0)
+            pm.maxpc16 = atof(*++argv);
+
+         else
+         {
+            fprintf(stderr, "pngvalid: %s: unknown 'max' option\n", *argv);
+            exit(99);
+         }
+
+         catmore = 1;
+      }
+
+      else if (strcmp(*argv, "--log8") == 0)
+         --argc, pm.log8 = atof(*++argv), catmore = 1;
+
+      else if (strcmp(*argv, "--log16") == 0)
+         --argc, pm.log16 = atof(*++argv), catmore = 1;
+
+#ifdef PNG_SET_OPTION_SUPPORTED
+      else if (strncmp(*argv, "--option=", 9) == 0)
+      {
+         /* Syntax of the argument is <option>:{on|off} */
+         const char *arg = 9+*argv;
+         unsigned char option=0, setting=0;
+
+#ifdef PNG_ARM_NEON
+         if (strncmp(arg, "arm-neon:", 9) == 0)
+            option = PNG_ARM_NEON, arg += 9;
+
+         else
+#endif
+#ifdef PNG_EXTENSIONS
+         if (strncmp(arg, "extensions:", 11) == 0)
+            option = PNG_EXTENSIONS, arg += 11;
+
+         else
+#endif
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+         if (strncmp(arg, "max-inflate-window:", 19) == 0)
+            option = PNG_MAXIMUM_INFLATE_WINDOW, arg += 19;
+
+         else
+#endif
+         {
+            fprintf(stderr, "pngvalid: %s: %s: unknown option\n", *argv, arg);
+            exit(99);
+         }
+
+         if (strcmp(arg, "off") == 0)
+            setting = PNG_OPTION_OFF;
+
+         else if (strcmp(arg, "on") == 0)
+            setting = PNG_OPTION_ON;
+
+         else
+         {
+            fprintf(stderr,
+               "pngvalid: %s: %s: unknown setting (use 'on' or 'off')\n",
+               *argv, arg);
+            exit(99);
+         }
+
+         pm.this.options[pm.this.noptions].option = option;
+         pm.this.options[pm.this.noptions++].setting = setting;
+      }
+#endif /* PNG_SET_OPTION_SUPPORTED */
+
+      else
+      {
+         fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
+         exit(99);
+      }
+
+      if (catmore) /* consumed an extra *argv */
+      {
+         cp = safecat(command, sizeof command, cp, " ");
+         cp = safecat(command, sizeof command, cp, *argv);
+      }
+   }
+
+   /* If pngvalid is run with no arguments default to a reasonable set of the
+    * tests.
+    */
+   if (pm.test_standard == 0 && pm.test_size == 0 && pm.test_transform == 0 &&
+      pm.ngamma_tests == 0)
+   {
+      /* Make this do all the tests done in the test shell scripts with the same
+       * parameters, where possible.  The limitation is that all the progressive
+       * read and interlace stuff has to be done in separate runs, so only the
+       * basic 'standard' and 'size' tests are done.
+       */
+      pm.test_standard = 1;
+      pm.test_size = 1;
+      pm.test_transform = 1;
+      pm.ngamma_tests = 2U;
+   }
+
+   if (pm.ngamma_tests > 0 &&
+      pm.test_gamma_threshold == 0 && pm.test_gamma_transform == 0 &&
+      pm.test_gamma_sbit == 0 && pm.test_gamma_scale16 == 0 &&
+      pm.test_gamma_background == 0 && pm.test_gamma_alpha_mode == 0)
+   {
+      pm.test_gamma_threshold = 1;
+      pm.test_gamma_transform = 1;
+      pm.test_gamma_sbit = 1;
+      pm.test_gamma_scale16 = 1;
+      pm.test_gamma_background = 1;
+      pm.test_gamma_alpha_mode = 1;
+   }
+
+   else if (pm.ngamma_tests == 0)
+   {
+      /* Nothing to test so turn everything off: */
+      pm.test_gamma_threshold = 0;
+      pm.test_gamma_transform = 0;
+      pm.test_gamma_sbit = 0;
+      pm.test_gamma_scale16 = 0;
+      pm.test_gamma_background = 0;
+      pm.test_gamma_alpha_mode = 0;
+   }
+
+   Try
+   {
+      /* Make useful base images */
+      make_transform_images(&pm);
+
+      /* Perform the standard and gamma tests. */
+      if (pm.test_standard)
+      {
+         perform_interlace_macro_validation();
+         perform_formatting_test(&pm.this);
+#        ifdef PNG_READ_SUPPORTED
+            perform_standard_test(&pm);
+#        endif
+         perform_error_test(&pm);
+      }
+
+      /* Various oddly sized images: */
+      if (pm.test_size)
+      {
+         make_size_images(&pm.this);
+#        ifdef PNG_READ_SUPPORTED
+            perform_size_test(&pm);
+#        endif
+      }
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+      /* Combinatorial transforms: */
+      if (pm.test_transform)
+         perform_transform_test(&pm);
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+      if (pm.ngamma_tests > 0)
+         perform_gamma_test(&pm, summary);
+#endif
+   }
+
+   Catch_anonymous
+   {
+      fprintf(stderr, "pngvalid: test aborted (probably failed in cleanup)\n");
+      if (!pm.this.verbose)
+      {
+         if (pm.this.error[0] != 0)
+            fprintf(stderr, "pngvalid: first error: %s\n", pm.this.error);
+
+         fprintf(stderr, "pngvalid: run with -v to see what happened\n");
+      }
+      exit(1);
+   }
+
+   if (summary)
+   {
+      printf("%s: %s (%s point arithmetic)\n",
+         (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
+            pm.this.nwarnings)) ? "FAIL" : "PASS",
+         command,
+#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || PNG_LIBPNG_VER < 10500
+         "floating"
+#else
+         "fixed"
+#endif
+         );
+   }
+
+   if (memstats)
+   {
+      printf("Allocated memory statistics (in bytes):\n"
+         "\tread  %lu maximum single, %lu peak, %lu total\n"
+         "\twrite %lu maximum single, %lu peak, %lu total\n",
+         (unsigned long)pm.this.read_memory_pool.max_max,
+         (unsigned long)pm.this.read_memory_pool.max_limit,
+         (unsigned long)pm.this.read_memory_pool.max_total,
+         (unsigned long)pm.this.write_memory_pool.max_max,
+         (unsigned long)pm.this.write_memory_pool.max_limit,
+         (unsigned long)pm.this.write_memory_pool.max_total);
+   }
+
+   /* Do this here to provoke memory corruption errors in memory not directly
+    * allocated by libpng - not a complete test, but better than nothing.
+    */
+   store_delete(&pm.this);
+
+   /* Error exit if there are any errors, and maybe if there are any
+    * warnings.
+    */
+   if (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
+       pm.this.nwarnings))
+   {
+      if (!pm.this.verbose)
+         fprintf(stderr, "pngvalid: %s\n", pm.this.error);
+
+      fprintf(stderr, "pngvalid: %d errors, %d warnings\n", pm.this.nerrors,
+          pm.this.nwarnings);
+
+      exit(1);
+   }
+
+   /* Success case. */
+   if (touch != NULL)
+   {
+      FILE *fsuccess = fopen(touch, "wt");
+
+      if (fsuccess != NULL)
+      {
+         int error = 0;
+         fprintf(fsuccess, "PNG validation succeeded\n");
+         fflush(fsuccess);
+         error = ferror(fsuccess);
+
+         if (fclose(fsuccess) || error)
+         {
+            fprintf(stderr, "%s: write failed\n", touch);
+            exit(1);
+         }
+      }
+
+      else
+      {
+         fprintf(stderr, "%s: open failed\n", touch);
+         exit(1);
+      }
+   }
+
+   /* This is required because some very minimal configurations do not use it:
+    */
+   UNUSED(fail)
+   return 0;
+}
+#else /* write or low level APIs not supported */
+int main(void)
+{
+   fprintf(stderr,
+      "pngvalid: no low level write support in libpng, all tests skipped\n");
+   /* So the test is skipped: */
+   return SKIP;
+}
+#endif
diff --git a/contrib/libtests/readpng.c b/contrib/libtests/readpng.c
new file mode 100644
index 0000000..3336d4e
--- /dev/null
+++ b/contrib/libtests/readpng.c
@@ -0,0 +1,115 @@
+/* readpng.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.1 [March 28, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Load an arbitrary number of PNG files (from the command line, or, if there
+ * are no arguments on the command line, from stdin) then run a time test by
+ * reading each file by row.  The test does nothing with the read result and
+ * does no transforms.  The only output is a time as a floating point number of
+ * seconds with 9 decimal digits.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+static int
+read_png(FILE *fp)
+{
+   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+   png_infop info_ptr = NULL;
+   png_bytep row = NULL, display = NULL;
+
+   if (png_ptr == NULL)
+      return 0;
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      if (row != NULL) free(row);
+      if (display != NULL) free(display);
+      return 0;
+   }
+
+   png_init_io(png_ptr, fp);
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+      png_error(png_ptr, "OOM allocating info structure");
+
+   png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
+
+   png_read_info(png_ptr, info_ptr);
+
+   {
+      png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+      /* Failure to initialize these is harmless */
+      row = malloc(rowbytes);
+      display = malloc(rowbytes);
+
+      if (row == NULL || display == NULL)
+         png_error(png_ptr, "OOM allocating row buffers");
+
+      {
+         png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+#        ifdef PNG_READ_INTERLACING_SUPPORTED
+            int passes = png_set_interlace_handling(png_ptr);
+#        else /* !READ_INTERLACING */
+            int passes = png_get_interlace_type(png_ptr, info_ptr) ==
+               PNG_INTERLACE_ADAM7 ? PNG_INTERLACE_ADAM7_PASSES : 1;
+#        endif /* !READ_INTERLACING */
+         int pass;
+
+         png_start_read_image(png_ptr);
+
+         for (pass = 0; pass < passes; ++pass)
+         {
+            png_uint_32 y = height;
+
+#           ifndef PNG_READ_INTERLACING_SUPPORTED
+               if (passes == PNG_INTERLACE_ADAM7_PASSES)
+                  y = PNG_PASS_ROWS(y, pass);
+#           endif /* READ_INTERLACING */
+
+            /* NOTE: this trashes the row each time; interlace handling won't
+             * work, but this avoids memory thrashing for speed testing.
+             */
+            while (y-- > 0)
+               png_read_row(png_ptr, row, display);
+         }
+      }
+   }
+
+   /* Make sure to read to the end of the file: */
+   png_read_end(png_ptr, info_ptr);
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+   free(row);
+   free(display);
+   return 1;
+}
+
+int
+main(void)
+{
+   /* Exit code 0 on success. */
+   return !read_png(stdin);
+}
diff --git a/contrib/libtests/tarith.c b/contrib/libtests/tarith.c
new file mode 100644
index 0000000..cb17ffa
--- /dev/null
+++ b/contrib/libtests/tarith.c
@@ -0,0 +1,999 @@
+
+/* tarith.c
+ *
+ * Copyright (c) 2011-2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Test internal arithmetic functions of libpng.
+ *
+ * This code must be linked against a math library (-lm), but does not require
+ * libpng or zlib to work.  Because it includes the complete source of 'png.c'
+ * it tests the code with whatever compiler options are used to build it.
+ * Changing these options can substantially change the errors in the
+ * calculations that the compiler chooses!
+ */
+#define _POSIX_SOURCE 1
+#define _ISOC99_SOURCE 1
+
+/* Obtain a copy of the code to be tested (plus other things), disabling
+ * stuff that is not required.
+ */
+#include <math.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../../pngpriv.h"
+
+#define png_error png_warning
+
+void png_warning(png_const_structrp png_ptr, png_const_charp msg)
+{
+   fprintf(stderr, "validation: %s\n", msg);
+}
+
+#define png_fixed_error png_fixed_warning
+
+void png_fixed_warning(png_const_structrp png_ptr, png_const_charp msg)
+{
+   fprintf(stderr, "overflow in: %s\n", msg);
+}
+
+#define png_set_error_fn(pp, ep, efp, wfp) ((void)0)
+#define png_malloc(pp, s) malloc(s)
+#define png_malloc_warn(pp, s) malloc(s)
+#define png_malloc_base(pp, s) malloc(s)
+#define png_calloc(pp, s) calloc(1, (s))
+#define png_free(pp, s) free(s)
+
+#define png_safecat(b, sb, pos, str) (pos)
+#define png_format_number(start, end, format, number) (start)
+
+#define crc32(crc, pp, s) (crc)
+#define inflateReset(zs) Z_OK
+
+#define png_create_struct(type) (0)
+#define png_destroy_struct(pp) ((void)0)
+#define png_create_struct_2(type, m, mm) (0)
+#define png_destroy_struct_2(pp, f, mm) ((void)0)
+
+#undef PNG_SIMPLIFIED_READ_SUPPORTED
+#undef PNG_SIMPLIFIED_WRITE_SUPPORTED
+#undef PNG_USER_MEM_SUPPORTED
+
+#include "../../png.c"
+
+/* Validate ASCII to fp routines. */
+static int verbose = 0;
+
+int validation_ascii_to_fp(int count, int argc, char **argv)
+{
+   int    showall = 0;
+   double max_error=2;      /* As a percentage error-in-last-digit/.5 */
+   double max_error_abs=17; /* Used when precision is DBL_DIG */
+   double max = 0;
+   double max_abs = 0;
+   double test = 0; /* Important to test this. */
+   int    precision = 5;
+   int    nonfinite = 0;
+   int    finite = 0;
+   int    ok = 0;
+   int    failcount = 0;
+   int    minorarith = 0;
+
+   while (--argc > 0)
+      if (strcmp(*++argv, "-a") == 0)
+         showall = 1;
+      else if (strcmp(*argv, "-e") == 0 && argc > 0)
+      {
+         --argc;
+         max_error = atof(*++argv);
+      }
+      else if (strcmp(*argv, "-E") == 0 && argc > 0)
+      {
+         --argc;
+         max_error_abs = atof(*++argv);
+      }
+      else
+      {
+         fprintf(stderr, "unknown argument %s\n", *argv);
+         return 1;
+      }
+
+   do
+   {
+      png_size_t index;
+      int state, failed = 0;
+      char buffer[64];
+
+      if (isfinite(test))
+         ++finite;
+      else
+         ++nonfinite;
+
+      if (verbose)
+         fprintf(stderr, "%.*g %d\n", DBL_DIG, test, precision);
+
+      /* Check for overflow in the buffer by setting a marker. */
+      memset(buffer, 71, sizeof buffer);
+
+      png_ascii_from_fp(0, buffer, precision+10, test, precision);
+
+      /* Allow for a three digit exponent, this stuff will fail if
+       * the exponent is bigger than this!
+       */
+      if (buffer[precision+7] != 71)
+      {
+         fprintf(stderr, "%g[%d] -> '%s'[%lu] buffer overflow\n", test,
+            precision, buffer, (unsigned long)strlen(buffer));
+         failed = 1;
+      }
+
+      /* Following are used for the number parser below and must be
+       * initialized to zero.
+       */
+      state = 0;
+      index = 0;
+      if (!isfinite(test))
+      {
+         /* Expect 'inf' */
+         if (test >= 0 && strcmp(buffer, "inf") ||
+             test <  0 && strcmp(buffer, "-inf"))
+         {
+            fprintf(stderr, "%g[%d] -> '%s' but expected 'inf'\n", test,
+               precision, buffer);
+            failed = 1;
+         }
+      }
+      else if (!png_check_fp_number(buffer, precision+10, &state, &index) ||
+          buffer[index] != 0)
+      {
+         fprintf(stderr, "%g[%d] -> '%s' but has bad format ('%c')\n", test,
+         precision, buffer, buffer[index]);
+         failed = 1;
+      }
+      else if (PNG_FP_IS_NEGATIVE(state) && !(test < 0))
+      {
+         fprintf(stderr, "%g[%d] -> '%s' but negative value not so reported\n",
+            test, precision, buffer);
+         failed = 1;
+         assert(!PNG_FP_IS_ZERO(state));
+         assert(!PNG_FP_IS_POSITIVE(state));
+      }
+      else if (PNG_FP_IS_ZERO(state) && !(test == 0))
+      {
+         fprintf(stderr, "%g[%d] -> '%s' but zero value not so reported\n",
+            test, precision, buffer);
+         failed = 1;
+         assert(!PNG_FP_IS_NEGATIVE(state));
+         assert(!PNG_FP_IS_POSITIVE(state));
+      }
+      else if (PNG_FP_IS_POSITIVE(state) && !(test > 0))
+      {
+         fprintf(stderr, "%g[%d] -> '%s' but postive value not so reported\n",
+            test, precision, buffer);
+         failed = 1;
+         assert(!PNG_FP_IS_NEGATIVE(state));
+         assert(!PNG_FP_IS_ZERO(state));
+      }
+      else
+      {
+         /* Check the result against the original. */
+         double out = atof(buffer);
+         double change = fabs((out - test)/test);
+         double allow = .5/pow(10,
+            (precision >= DBL_DIG) ? DBL_DIG-1 : precision-1);
+
+         /* NOTE: if you hit this error case are you compiling with gcc
+          * and -O0?  Try -O2 - the errors can accumulate if the FP
+          * code above is not optimized and may drift outside the .5 in
+          * DBL_DIG allowed.  In any case a small number of errors may
+          * occur (very small ones - 1 or 2%) because of rounding in the
+          * calculations, either in the conversion API or in atof.
+          */
+         if (change >= allow && (isfinite(out) ||
+             fabs(test/DBL_MAX) <= 1-allow))
+         {
+            double percent = (precision >= DBL_DIG) ? max_error_abs : max_error;
+            double allowp = (change-allow)*100/allow;
+
+            if (precision >= DBL_DIG)
+            {
+               if (max_abs < allowp) max_abs = allowp;
+            }
+
+            else
+            {
+               if (max < allowp) max = allowp;
+            }
+
+            if (showall || allowp >= percent)
+            {
+               fprintf(stderr,
+                  "%.*g[%d] -> '%s' -> %.*g number changed (%g > %g (%d%%))\n",
+                  DBL_DIG, test, precision, buffer, DBL_DIG, out, change, allow,
+                  (int)round(allowp));
+               failed = 1;
+            }
+            else
+               ++minorarith;
+         }
+      }
+
+      if (failed)
+         ++failcount;
+      else
+         ++ok;
+
+skip:
+      /* Generate a new number and precision. */
+      precision = rand();
+      if (precision & 1) test = -test;
+      precision >>= 1;
+
+      /* Generate random numbers. */
+      if (test == 0 || !isfinite(test))
+         test = precision+1;
+      else
+      {
+         /* Derive the exponent from the previous rand() value. */
+         int exponent = precision % (DBL_MAX_EXP - DBL_MIN_EXP) + DBL_MIN_EXP;
+         int tmp;
+         test = frexp(test * rand(), &tmp);
+         test = ldexp(test, exponent);
+         precision >>= 8; /* arbitrary */
+      }
+
+      /* This limits the precision to 32 digits, enough for standard
+       * IEEE implementations which have at most 15 digits.
+       */
+      precision = (precision & 0x1f) + 1;
+   }
+   while (--count);
+
+   printf("Tested %d finite values, %d non-finite, %d OK (%d failed) %d minor "
+      "arithmetic errors\n", finite, nonfinite, ok, failcount, minorarith);
+   printf(" Error with >=%d digit precision %.2f%%\n", DBL_DIG, max_abs);
+   printf(" Error with < %d digit precision %.2f%%\n", DBL_DIG, max);
+
+   return 0;
+}
+
+/* Observe that valid FP numbers have the forms listed in the PNG extensions
+ * specification:
+ *
+ * [+,-]{integer,integer.fraction,.fraction}[{e,E}[+,-]integer]
+ *
+ * Test each of these in turn, including invalid cases.
+ */
+typedef enum checkfp_state
+{
+   start, fraction, exponent, states
+} checkfp_state;
+
+/* The characters (other than digits) that characterize the states: */
+static const char none[] = "";
+static const char hexdigits[16] = "0123456789ABCDEF";
+
+static const struct
+{
+   const char *start; /* Characters valid at the start */
+   const char *end;   /* Valid characters that end the state */
+   const char *tests; /* Characters to test after 2 digits seen */
+}
+state_characters[states] =
+{
+   /* start:    */ { "+-.", ".eE", "+-.e*0369" },
+   /* fraction: */ { none, "eE",  "+-.E#0147" },
+   /* exponent: */ { "+-", none,  "+-.eE^0258" }
+};
+
+typedef struct
+{
+   char number[1024];  /* Buffer for number being tested */
+   int  limit;         /* Command line limit */
+   int  verbose;       /* Shadows global variable */
+   int  ctimes;        /* Number of numbers tested */
+   int  cmillions;     /* Count of millions of numbers */
+   int  cinvalid;      /* Invalid strings checked */
+   int  cnoaccept;     /* Characters not accepted */
+}
+checkfp_command;
+
+typedef struct
+{
+   int           cnumber;          /* Index into number string */
+   checkfp_state check_state;      /* Current number state */
+   int           at_start;         /* At start (first character) of state */
+   int           cdigits_in_state; /* Digits seen in that state */
+   int           limit;            /* Limit on same for checking all chars */
+   int           state;            /* Current parser state */
+   int           is_negative;      /* Number is negative */
+   int           is_zero;          /* Number is (still) zero */
+   int           number_was_valid; /* Previous character validity */
+}
+checkfp_control;
+
+static int check_all_characters(checkfp_command *co, checkfp_control c);
+
+static int check_some_characters(checkfp_command *co, checkfp_control c,
+   const char *tests);
+
+static int check_one_character(checkfp_command *co, checkfp_control c, int ch)
+{
+   /* Test this character (ch) to ensure the parser does the correct thing.
+    */
+   png_size_t index = 0;
+   const char test = (char)ch;
+   const int number_is_valid = png_check_fp_number(&test, 1, &c.state, &index);
+   const int character_accepted = (index == 1);
+
+   if (c.check_state != exponent && isdigit(ch) && ch != '0')
+      c.is_zero = 0;
+
+   if (c.check_state == start && c.at_start && ch == '-')
+      c.is_negative = 1;
+
+   if (isprint(ch))
+      co->number[c.cnumber++] = (char)ch;
+   else
+   {
+      co->number[c.cnumber++] = '<';
+      co->number[c.cnumber++] = hexdigits[(ch >> 4) & 0xf];
+      co->number[c.cnumber++] = hexdigits[ch & 0xf];
+      co->number[c.cnumber++] = '>';
+   }
+   co->number[c.cnumber] = 0;
+
+   if (co->verbose > 1)
+      fprintf(stderr, "%s\n", co->number);
+
+   if (++(co->ctimes) == 1000000)
+   {
+      if (co->verbose == 1)
+         fputc('.', stderr);
+      co->ctimes = 0;
+      ++(co->cmillions);
+   }
+
+   if (!number_is_valid)
+      ++(co->cinvalid);
+
+   if (!character_accepted)
+      ++(co->cnoaccept);
+
+   /* This should never fail (it's a serious bug if it does): */
+   if (index != 0 && index != 1)
+   {
+      fprintf(stderr, "%s: read beyond end of string (%lu)\n", co->number,
+         (unsigned long)index);
+      return 0;
+   }
+
+   /* Validate the new state, note that the PNG_FP_IS_ macros all return
+    * false unless the number is valid.
+    */
+   if (PNG_FP_IS_NEGATIVE(c.state) !=
+      (number_is_valid && !c.is_zero && c.is_negative))
+   {
+      fprintf(stderr, "%s: negative when it is not\n", co->number);
+      return 0;
+   }
+
+   if (PNG_FP_IS_ZERO(c.state) != (number_is_valid && c.is_zero))
+   {
+      fprintf(stderr, "%s: zero when it is not\n", co->number);
+      return 0;
+   }
+
+   if (PNG_FP_IS_POSITIVE(c.state) !=
+      (number_is_valid && !c.is_zero && !c.is_negative))
+   {
+      fprintf(stderr, "%s: positive when it is not\n", co->number);
+      return 0;
+   }
+
+   /* Testing a digit */
+   if (isdigit(ch))
+   {
+      if (!character_accepted)
+      {
+         fprintf(stderr, "%s: digit '%c' not accepted\n", co->number, ch);
+         return 0;
+      }
+
+      if (!number_is_valid)
+      {
+         fprintf(stderr, "%s: saw a digit (%c) but number not valid\n",
+            co->number, ch);
+         return 0;
+      }
+
+      ++c.cdigits_in_state;
+      c.at_start = 0;
+      c.number_was_valid = 1;
+
+      /* Continue testing characters in this state.  Either test all of
+       * them or, if we have already seen one digit in this state, just test a
+       * limited set.
+       */
+      if (c.cdigits_in_state < 1)
+         return check_all_characters(co, c);
+
+      else
+         return check_some_characters(co, c,
+            state_characters[c.check_state].tests);
+   }
+
+   /* A non-digit; is it allowed here? */
+   else if (((ch == '+' || ch == '-') && c.check_state != fraction &&
+               c.at_start) ||
+            (ch == '.' && c.check_state == start) ||
+            ((ch == 'e' || ch == 'E') && c.number_was_valid &&
+               c.check_state != exponent))
+   {
+      if (!character_accepted)
+      {
+         fprintf(stderr, "%s: character '%c' not accepted\n", co->number, ch);
+         return 0;
+      }
+
+      /* The number remains valid after start of fraction but nowhere else. */
+      if (number_is_valid && (c.check_state != start || ch != '.'))
+      {
+         fprintf(stderr, "%s: saw a non-digit (%c) but number valid\n",
+            co->number, ch);
+         return 0;
+      }
+
+      c.number_was_valid = number_is_valid;
+
+      /* Check for a state change.  When changing to 'fraction' if the number
+       * is valid at this point set the at_start to false to allow an exponent
+       * 'e' to come next.
+       */
+      if (c.check_state == start && ch == '.')
+      {
+         c.check_state = fraction;
+         c.at_start = !number_is_valid;
+         c.cdigits_in_state = 0;
+         c.limit = co->limit;
+         return check_all_characters(co, c);
+      }
+
+      else if (c.check_state < exponent && (ch == 'e' || ch == 'E'))
+      {
+         c.check_state = exponent;
+         c.at_start = 1;
+         c.cdigits_in_state = 0;
+         c.limit = co->limit;
+         return check_all_characters(co, c);
+      }
+
+      /* Else it was a sign, and the state doesn't change. */
+      else
+      {
+         if (ch != '-' && ch != '+')
+         {
+            fprintf(stderr, "checkfp: internal error (1)\n");
+            return 0;
+         }
+
+         c.at_start = 0;
+         return check_all_characters(co, c);
+      }
+   }
+
+   /* Testing an invalid character */
+   else
+   {
+      if (character_accepted)
+      {
+         fprintf(stderr, "%s: character '%c' [0x%.2x] accepted\n", co->number,
+            ch, ch);
+         return 0;
+      }
+
+      if (number_is_valid != c.number_was_valid)
+      {
+         fprintf(stderr,
+            "%s: character '%c' [0x%.2x] changed number validity\n", co->number,
+            ch, ch);
+         return 0;
+      }
+
+      /* Do nothing - the parser has stuck; return success and keep going with
+       * the next character.
+       */
+   }
+
+   /* Successful return (the caller will try the next character.) */
+   return 1;
+}
+
+static int check_all_characters(checkfp_command *co, checkfp_control c)
+{
+   int ch;
+
+   if (c.cnumber+4 < sizeof co->number) for (ch=0; ch<256; ++ch)
+   {
+      if (!check_one_character(co, c, ch))
+         return 0;
+   }
+
+   return 1;
+}
+
+static int check_some_characters(checkfp_command *co, checkfp_control c,
+   const char *tests)
+{
+   int i;
+
+   --(c.limit);
+
+   if (c.cnumber+4 < sizeof co->number && c.limit >= 0)
+   {
+      if (c.limit > 0) for (i=0; tests[i]; ++i)
+      {
+         if (!check_one_character(co, c, tests[i]))
+               return 0;
+      }
+
+      /* At the end check all the characters. */
+      else
+         return check_all_characters(co, c);
+   }
+
+   return 1;
+}
+
+int validation_checkfp(int count, int argc, char **argv)
+{
+   int result;
+   checkfp_command command;
+   checkfp_control control;
+
+   command.number[0] = 0;
+   command.limit = 3;
+   command.verbose = verbose;
+   command.ctimes = 0;
+   command.cmillions = 0;
+   command.cinvalid = 0;
+   command.cnoaccept = 0;
+
+   while (--argc > 0)
+   {
+      ++argv;
+      if (argc > 1 && strcmp(*argv, "-l") == 0)
+      {
+         --argc;
+         command.limit = atoi(*++argv);
+      }
+
+      else
+      {
+         fprintf(stderr, "unknown argument %s\n", *argv);
+         return 1;
+      }
+   }
+
+   control.cnumber = 0;
+   control.check_state = start;
+   control.at_start = 1;
+   control.cdigits_in_state = 0;
+   control.limit = command.limit;
+   control.state = 0;
+   control.is_negative = 0;
+   control.is_zero = 1;
+   control.number_was_valid = 0;
+
+   result = check_all_characters(&command, control);
+
+   printf("checkfp: %s: checked %d,%.3d,%.3d,%.3d strings (%d invalid)\n",
+      result ? "pass" : "FAIL", command.cmillions / 1000,
+      command.cmillions % 1000, command.ctimes / 1000, command.ctimes % 1000,
+      command.cinvalid);
+
+   return result;
+}
+
+int validation_muldiv(int count, int argc, char **argv)
+{
+   int tested = 0;
+   int overflow = 0;
+   int error = 0;
+   int error64 = 0;
+   int passed = 0;
+   int randbits = 0;
+   png_uint_32 randbuffer;
+   png_fixed_point a;
+   png_int_32 times, div;
+
+   while (--argc > 0)
+      {
+         fprintf(stderr, "unknown argument %s\n", *++argv);
+         return 1;
+      }
+
+   /* Find out about the random number generator. */
+   randbuffer = RAND_MAX;
+   while (randbuffer != 0) ++randbits, randbuffer >>= 1;
+   printf("Using random number generator that makes %d bits\n", randbits);
+   for (div=0; div<32; div += randbits)
+      randbuffer = (randbuffer << randbits) ^ rand();
+
+   a = 0;
+   times = div = 0;
+   do
+   {
+      png_fixed_point result;
+      /* NOTE: your mileage may vary, a type is required below that can
+       * hold 64 bits or more, if floating point is used a 64-bit or
+       * better mantissa is required.
+       */
+      long long int fp, fpround;
+      unsigned long hi, lo;
+      int ok;
+
+      /* Check the values, png_64bit_product can only handle positive
+       * numbers, so correct for that here.
+       */
+      {
+         long u1, u2;
+         int n = 0;
+         if (a < 0) u1 = -a, n = 1; else u1 = a;
+         if (times < 0) u2 = -times, n = !n; else u2 = times;
+         png_64bit_product(u1, u2, &hi, &lo);
+         if (n)
+         {
+            /* -x = ~x+1 */
+            lo = ((~lo) + 1) & 0xffffffff;
+            hi = ~hi;
+            if (lo == 0) ++hi;
+         }
+      }
+
+      fp = a;
+      fp *= times;
+      if ((fp & 0xffffffff) != lo || ((fp >> 32) & 0xffffffff) != hi)
+      {
+         fprintf(stderr, "png_64bit_product %d * %d -> %lx|%.8lx not %llx\n",
+            a, times, hi, lo, fp);
+         ++error64;
+      }
+
+      if (div != 0)
+      {
+         /* Round - this is C round to zero. */
+         if ((fp < 0) != (div < 0))
+           fp -= div/2;
+         else
+           fp += div/2;
+
+         fp /= div;
+         fpround = fp;
+         /* Assume 2's complement here: */
+         ok = fpround <= PNG_UINT_31_MAX &&
+              fpround >= -1-(long long int)PNG_UINT_31_MAX;
+         if (!ok) ++overflow;
+      }
+      else
+        ok = 0, ++overflow, fpround = fp/*misleading*/;
+
+      if (verbose)
+         fprintf(stderr, "TEST %d * %d / %d -> %lld (%s)\n", a, times, div,
+            fp, ok ? "ok" : "overflow");
+
+      ++tested;
+      if (png_muldiv(&result, a, times, div) != ok)
+      {
+         ++error;
+         if (ok)
+             fprintf(stderr, "%d * %d / %d -> overflow (expected %lld)\n", a,
+                times, div, fp);
+         else
+             fprintf(stderr, "%d * %d / %d -> %d (expected overflow %lld)\n", a,
+                times, div, result, fp);
+      }
+      else if (ok && result != fpround)
+      {
+         ++error;
+         fprintf(stderr, "%d * %d / %d -> %d not %lld\n", a, times, div, result,
+            fp);
+      }
+      else
+         ++passed;
+
+      /* Generate three new values, this uses rand() and rand() only returns
+       * up to RAND_MAX.
+       */
+      /* CRUDE */
+      a += times;
+      times += div;
+      div = randbuffer;
+      randbuffer = (randbuffer << randbits) ^ rand();
+   }
+   while (--count > 0);
+
+   printf("%d tests including %d overflows, %d passed, %d failed (%d 64-bit "
+      "errors)\n", tested, overflow, passed, error, error64);
+   return 0;
+}
+
+/* When FP is on this just becomes a speed test - compile without FP to get real
+ * validation.
+ */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define LN2 .000010576586617430806112933839 /* log(2)/65536 */
+#define L2INV 94548.46219969910586572651    /* 65536/log(2) */
+
+/* For speed testing, need the internal functions too: */
+static png_uint_32 png_log8bit(unsigned x)
+{
+   if (x > 0)
+      return (png_uint_32)floor(.5-log(x/255.)*L2INV);
+
+   return 0xffffffff;
+}
+
+static png_uint_32 png_log16bit(png_uint_32 x)
+{
+   if (x > 0)
+      return (png_uint_32)floor(.5-log(x/65535.)*L2INV);
+
+   return 0xffffffff;
+}
+
+static png_uint_32 png_exp(png_uint_32 x)
+{
+   return (png_uint_32)floor(.5 + exp(x * -LN2) * 0xffffffffU);
+}
+
+static png_byte png_exp8bit(png_uint_32 log)
+{
+   return (png_byte)floor(.5 + exp(log * -LN2) * 255);
+}
+
+static png_uint_16 png_exp16bit(png_uint_32 log)
+{
+   return (png_uint_16)floor(.5 + exp(log * -LN2) * 65535);
+}
+#endif /* FLOATING_ARITHMETIC */
+
+int validation_gamma(int argc, char **argv)
+{
+   double gamma[9] = { 2.2, 1.8, 1.52, 1.45, 1., 1/1.45, 1/1.52, 1/1.8, 1/2.2 };
+   double maxerr;
+   int i, silent=0, onlygamma=0;
+
+   /* Silence the output with -s, just test the gamma functions with -g: */
+   while (--argc > 0)
+      if (strcmp(*++argv, "-s") == 0)
+         silent = 1;
+      else if (strcmp(*argv, "-g") == 0)
+         onlygamma = 1;
+      else
+      {
+         fprintf(stderr, "unknown argument %s\n", *argv);
+         return 1;
+      }
+
+   if (!onlygamma)
+   {
+      /* First validate the log functions: */
+      maxerr = 0;
+      for (i=0; i<256; ++i)
+      {
+         double correct = -log(i/255.)/log(2.)*65536;
+         double error = png_log8bit(i) - correct;
+
+         if (i != 0 && fabs(error) > maxerr)
+            maxerr = fabs(error);
+
+         if (i == 0 && png_log8bit(i) != 0xffffffff ||
+             i != 0 && png_log8bit(i) != floor(correct+.5))
+         {
+            fprintf(stderr, "8-bit log error: %d: got %u, expected %f\n",
+               i, png_log8bit(i), correct);
+         }
+      }
+
+      if (!silent)
+         printf("maximum 8-bit log error = %f\n", maxerr);
+
+      maxerr = 0;
+      for (i=0; i<65536; ++i)
+      {
+         double correct = -log(i/65535.)/log(2.)*65536;
+         double error = png_log16bit(i) - correct;
+
+         if (i != 0 && fabs(error) > maxerr)
+            maxerr = fabs(error);
+
+         if (i == 0 && png_log16bit(i) != 0xffffffff ||
+             i != 0 && png_log16bit(i) != floor(correct+.5))
+         {
+            if (error > .68) /* By experiment error is less than .68 */
+            {
+               fprintf(stderr, "16-bit log error: %d: got %u, expected %f"
+                  " error: %f\n", i, png_log16bit(i), correct, error);
+            }
+         }
+      }
+
+      if (!silent)
+         printf("maximum 16-bit log error = %f\n", maxerr);
+
+      /* Now exponentiations. */
+      maxerr = 0;
+      for (i=0; i<=0xfffff; ++i)
+      {
+         double correct = exp(-i/65536. * log(2.)) * (65536. * 65536);
+         double error = png_exp(i) - correct;
+
+         if (fabs(error) > maxerr)
+            maxerr = fabs(error);
+         if (fabs(error) > 1883) /* By experiment. */
+         {
+            fprintf(stderr, "32-bit exp error: %d: got %u, expected %f"
+                  " error: %f\n", i, png_exp(i), correct, error);
+         }
+      }
+
+      if (!silent)
+         printf("maximum 32-bit exp error = %f\n", maxerr);
+
+      maxerr = 0;
+      for (i=0; i<=0xfffff; ++i)
+      {
+         double correct = exp(-i/65536. * log(2.)) * 255;
+         double error = png_exp8bit(i) - correct;
+
+         if (fabs(error) > maxerr)
+            maxerr = fabs(error);
+         if (fabs(error) > .50002) /* By experiment */
+         {
+            fprintf(stderr, "8-bit exp error: %d: got %u, expected %f"
+                  " error: %f\n", i, png_exp8bit(i), correct, error);
+         }
+      }
+
+      if (!silent)
+         printf("maximum 8-bit exp error = %f\n", maxerr);
+
+      maxerr = 0;
+      for (i=0; i<=0xfffff; ++i)
+      {
+         double correct = exp(-i/65536. * log(2.)) * 65535;
+         double error = png_exp16bit(i) - correct;
+
+         if (fabs(error) > maxerr)
+            maxerr = fabs(error);
+         if (fabs(error) > .524) /* By experiment */
+         {
+            fprintf(stderr, "16-bit exp error: %d: got %u, expected %f"
+                  " error: %f\n", i, png_exp16bit(i), correct, error);
+         }
+      }
+
+      if (!silent)
+         printf("maximum 16-bit exp error = %f\n", maxerr);
+   } /* !onlygamma */
+
+   /* Test the overall gamma correction. */
+   for (i=0; i<9; ++i)
+   {
+      unsigned j;
+      double g = gamma[i];
+      png_fixed_point gfp = floor(g * PNG_FP_1 + .5);
+
+      if (!silent)
+         printf("Test gamma %f\n", g);
+
+      maxerr = 0;
+      for (j=0; j<256; ++j)
+      {
+         double correct = pow(j/255., g) * 255;
+         png_byte out = png_gamma_8bit_correct(j, gfp);
+         double error = out - correct;
+
+         if (fabs(error) > maxerr)
+            maxerr = fabs(error);
+         if (out != floor(correct+.5))
+         {
+            fprintf(stderr, "8bit %d ^ %f: got %d expected %f error %f\n",
+               j, g, out, correct, error);
+         }
+      }
+
+      if (!silent)
+         printf("gamma %f: maximum 8-bit error %f\n", g, maxerr);
+
+      maxerr = 0;
+      for (j=0; j<65536; ++j)
+      {
+         double correct = pow(j/65535., g) * 65535;
+         png_uint_16 out = png_gamma_16bit_correct(j, gfp);
+         double error = out - correct;
+
+         if (fabs(error) > maxerr)
+            maxerr = fabs(error);
+         if (fabs(error) > 1.62)
+         {
+            fprintf(stderr, "16bit %d ^ %f: got %d expected %f error %f\n",
+               j, g, out, correct, error);
+         }
+      }
+
+      if (!silent)
+         printf("gamma %f: maximum 16-bit error %f\n", g, maxerr);
+   }
+
+   return 0;
+}
+
+/**************************** VALIDATION TESTS ********************************/
+/* Various validation routines are included herein, they require some
+ * definition for png_warning and png_error, seetings of VALIDATION:
+ *
+ * 1: validates the ASCII to floating point conversions
+ * 2: validates png_muldiv
+ * 3: accuracy test of fixed point gamma tables
+ */
+
+/* The following COUNT (10^8) takes about 1 hour on a 1GHz Pentium IV
+ * processor.
+ */
+#define COUNT 1000000000
+
+int main(int argc, char **argv)
+{
+   int count = COUNT;
+
+   while (argc > 1)
+   {
+      if (argc > 2 && strcmp(argv[1], "-c") == 0)
+      {
+         count = atoi(argv[2]);
+         argc -= 2;
+         argv += 2;
+      }
+
+      else if (strcmp(argv[1], "-v") == 0)
+      {
+         ++verbose;
+         --argc;
+         ++argv;
+      }
+
+      else
+         break;
+   }
+
+   if (count > 0 && argc > 1)
+   {
+      if (strcmp(argv[1], "ascii") == 0)
+         return validation_ascii_to_fp(count, argc-1, argv+1);
+      else if (strcmp(argv[1], "checkfp") == 0)
+         return validation_checkfp(count, argc-1, argv+1);
+      else if (strcmp(argv[1], "muldiv") == 0)
+         return validation_muldiv(count, argc-1, argv+1);
+      else if (strcmp(argv[1], "gamma") == 0)
+         return validation_gamma(argc-1, argv+1);
+   }
+
+   /* Bad argument: */
+   fprintf(stderr,
+      "usage: tarith [-v] [-c count] {ascii,muldiv,gamma} [args]\n");
+   fprintf(stderr, " arguments: ascii [-a (all results)] [-e error%%]\n");
+   fprintf(stderr, "            checkfp [-l max-number-chars]\n");
+   fprintf(stderr, "            muldiv\n");
+   fprintf(stderr, "            gamma -s (silent) -g (only gamma; no log)\n");
+   return 1;
+}
diff --git a/contrib/libtests/timepng.c b/contrib/libtests/timepng.c
new file mode 100644
index 0000000..7c93797
--- /dev/null
+++ b/contrib/libtests/timepng.c
@@ -0,0 +1,303 @@
+/* timepng.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.1 [March 28, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Load an arbitrary number of PNG files (from the command line, or, if there
+ * are no arguments on the command line, from stdin) then run a time test by
+ * reading each file by row.  The test does nothing with the read result and
+ * does no transforms.  The only output is a time as a floating point number of
+ * seconds with 9 decimal digits.
+ */
+#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <time.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+static int read_png(FILE *fp)
+{
+   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+   png_infop info_ptr = NULL;
+   png_bytep row = NULL, display = NULL;
+
+   if (png_ptr == NULL)
+      return 0;
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      if (row != NULL) free(row);
+      if (display != NULL) free(display);
+      return 0;
+   }
+
+   png_init_io(png_ptr, fp);
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+      png_error(png_ptr, "OOM allocating info structure");
+
+   png_read_info(png_ptr, info_ptr);
+
+   {
+      png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+      row = malloc(rowbytes);
+      display = malloc(rowbytes);
+
+      if (row == NULL || display == NULL)
+         png_error(png_ptr, "OOM allocating row buffers");
+
+      {
+         png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+         int passes = png_set_interlace_handling(png_ptr);
+         int pass;
+
+         png_start_read_image(png_ptr);
+
+         for (pass = 0; pass < passes; ++pass)
+         {
+            png_uint_32 y = height;
+
+            /* NOTE: this trashes the row each time; interlace handling won't
+             * work, but this avoids memory thrashing for speed testing.
+             */
+            while (y-- > 0)
+               png_read_row(png_ptr, row, display);
+         }
+      }
+   }
+
+   /* Make sure to read to the end of the file: */
+   png_read_end(png_ptr, info_ptr);
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+   free(row);
+   free(display);
+   return 1;
+}
+
+static int mytime(struct timespec *t)
+{
+   /* Do the timing using clock_gettime and the per-process timer. */
+   if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t))
+      return 1;
+
+   perror("CLOCK_PROCESS_CPUTIME_ID");
+   fprintf(stderr, "timepng: could not get the time\n");
+   return 0;
+}
+
+static int perform_one_test(FILE *fp, int nfiles)
+{
+   int i;
+   struct timespec before, after;
+
+   /* Clear out all errors: */
+   rewind(fp);
+
+   if (mytime(&before))
+   {
+      for (i=0; i<nfiles; ++i)
+      {
+         if (read_png(fp))
+         {
+            if (ferror(fp))
+            {
+               perror("temporary file");
+               fprintf(stderr, "file %d: error reading PNG data\n", i);
+               return 0;
+            }
+         }
+
+         else
+         {
+            perror("temporary file");
+            fprintf(stderr, "file %d: error from libpng\n", i);
+            return 0;
+         }
+      }
+   }
+
+   else
+      return 0;
+
+   if (mytime(&after))
+   {
+      /* Work out the time difference and print it - this is the only output,
+       * so flush it immediately.
+       */
+      unsigned long s = after.tv_sec - before.tv_sec;
+      long ns = after.tv_nsec - before.tv_nsec;
+
+      if (ns < 0)
+      {
+         --s;
+         ns += 1000000000;
+
+         if (ns < 0)
+         {
+            fprintf(stderr, "timepng: bad clock from kernel\n");
+            return 0;
+         }
+      }
+
+      printf("%lu.%.9ld\n", s, ns);
+      fflush(stdout);
+      if (ferror(stdout))
+      {
+         fprintf(stderr, "timepng: error writing output\n");
+         return 0;
+      }
+
+      /* Successful return */
+      return 1;
+   }
+
+   else
+      return 0;
+}
+
+static int add_one_file(FILE *fp, char *name)
+{
+   FILE *ip = fopen(name, "rb");
+
+   if (ip != NULL)
+   {
+      int ch;
+      for (;;)
+      {
+         ch = getc(ip);
+         if (ch == EOF) break;
+         putc(ch, fp);
+      }
+
+      if (ferror(ip))
+      {
+         perror(name);
+         fprintf(stderr, "%s: read error\n", name);
+         return 0;
+      }
+
+      (void)fclose(ip);
+
+      if (ferror(fp))
+      {
+         perror("temporary file");
+         fprintf(stderr, "temporary file write error\n");
+         return 0;
+      }
+   }
+
+   else
+   {
+      perror(name);
+      fprintf(stderr, "%s: open failed\n", name);
+      return 0;
+   }
+
+   return 1;
+}
+
+int main(int argc, char **argv)
+{
+   int ok = 0;
+   FILE *fp = tmpfile();
+
+   if (fp != NULL)
+   {
+      int err = 0;
+      int nfiles = 0;
+
+      if (argc > 1)
+      {
+         int i;
+
+         for (i=1; i<argc; ++i)
+         {
+            if (add_one_file(fp, argv[i]))
+               ++nfiles;
+
+            else
+            {
+               err = 1;
+               break;
+            }
+         }
+      }
+
+      else
+      {
+         char filename[FILENAME_MAX+1];
+
+         while (fgets(filename, FILENAME_MAX+1, stdin))
+         {
+            size_t len = strlen(filename);
+
+            if (filename[len-1] == '\n')
+            {
+               filename[len-1] = 0;
+               if (add_one_file(fp, filename))
+                  ++nfiles;
+
+               else
+               {
+                  err = 1;
+                  break;
+               }
+            }
+
+            else
+            {
+               fprintf(stderr, "timepng: truncated file name ...%s\n",
+                  filename+len-32);
+               err = 1;
+               break;
+            }
+         }
+
+         if (ferror(stdin))
+         {
+            fprintf(stderr, "timepng: stdin: read error\n");
+            err = 1;
+         }
+      }
+
+      if (!err)
+      {
+         if (nfiles > 0)
+            ok = perform_one_test(fp, nfiles);
+
+         else
+            fprintf(stderr, "usage: timepng {files} or ls files | timepng\n");
+      }
+
+      (void)fclose(fp);
+   }
+
+   else
+      fprintf(stderr, "timepng: could not open temporary file\n");
+
+   /* Exit code 0 on success. */
+   return ok == 0;
+}
diff --git a/contrib/pngminim/README b/contrib/pngminim/README
new file mode 100644
index 0000000..e17fe35
--- /dev/null
+++ b/contrib/pngminim/README
@@ -0,0 +1,5 @@
+
+This demonstrates the use of PNG_USER_CONFIG, pngusr.h and pngusr.dfa
+to build minimal decoder, encoder, and progressive reader applications.
+
+See the individual README and pngusr.dfa files for more explanation.
diff --git a/contrib/pngminim/decoder/README b/contrib/pngminim/decoder/README
index a656e0b..fa979fc 100644
--- a/contrib/pngminim/decoder/README
+++ b/contrib/pngminim/decoder/README
@@ -1,9 +1,10 @@
-This demonstrates the use of PNG_USER_CONFIG and pngusr.h
+This demonstrates the use of PNG_USER_CONFIG, pngusr.h and pngusr.dfa
 
-To build a minimal read-only decoder with embedded libpng and zlib, run
+The makefile builds a minimal read-only decoder with embedded libpng
+and zlib.
 
-    gather.sh  # to collect needed files from pngminus, libpng, and zlib
-    make
+Specify the location of the zlib source (1.2.1 or later) as ZLIBSRC
+on the make command line.
 
 If you prefer to use the shared libraries, go to contrib/pngminus
-and build the png2pnm application there. 
+and build the png2pnm application there.
diff --git a/contrib/pngminim/decoder/gather.sh b/contrib/pngminim/decoder/gather.sh
deleted file mode 100755
index 913b78d..0000000
--- a/contrib/pngminim/decoder/gather.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-cp ../../pngminus/png2pnm.c pngm2pnm.c
-cp ../../../*.h .
-cp ../../../*.c .
-rm pnggccrd.c pngvcrd.c
-rm example.c pngtest.c pngpread.c pngw*.c
-# change the following 2 lines if zlib is somewhere else
-cp ../../../../zlib/*.h .
-cp ../../../../zlib/*.c .
-rm minigzip.c example.c compress.c deflate.c gz*
diff --git a/contrib/pngminim/decoder/makefile b/contrib/pngminim/decoder/makefile
index 4c73839..4acf3c1 100644
--- a/contrib/pngminim/decoder/makefile
+++ b/contrib/pngminim/decoder/makefile
@@ -5,28 +5,84 @@
 CC=gcc
 LD=$(CC)
 
-RM=rm -f
+# If awk fails try
+# make AWK=nawk
 
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \
-       -DdeflateParams\(a,b,c\)=Z_OK -I. -O1
+# If cpp fails try
+# make CPP=/lib/cpp
+
+RM=rm -f
+COPY=cp
+
+CPPFLAGS=-I. -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
 
 C=.c
 O=.o
 L=.a
 E=
 
-ZOBJS  = adler32$(O) crc32$(O) \
-	 infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
-	 trees$(O) uncompr$(O) zutil$(O)
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/pngminus
 
-OBJS  = pngm2pnm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# Zlib (minimal inflate requirements - crc32 is used by libpng)
+# zutil can be eliminated if you provide your own zcalloc and zcfree
+ZSRCS  = adler32$(C) crc32$(C) \
+	 inffast$(C) inflate$(C) inftrees$(C) \
+	 zutil$(C)
+
+# Standard headers
+ZH     = zlib.h crc32.h inffast.h inffixed.h \
+	 inflate.h inftrees.h zutil.h
+
+# Machine generated headers
+ZCONF  = zconf.h
+
+# Headers callers use
+ZINC   = zlib.h $(ZCONF)
+
+# Headers the Zlib source uses
+ZHDRS  = $(ZH) $(ZCONF)
+
+ZOBJS  = adler32$(O) crc32$(O) \
+	 inffast$(O) inflate$(O) inftrees$(O) \
+	 zutil$(O)
+
+# libpng
+PNGSRCS=png$(C) pngerror$(C) pngget$(C) pngmem$(C) \
+	pngread$(C) pngrio$(C) pngrtran$(C) pngrutil$(C) \
+	pngset$(C) pngtrans$(C)
+	
+# Standard headers
+PNGH   =png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h
+
+# Machine generated headers
+PNGCONF=pnglibconf.h
+
+# Headers callers use
+PNGINC= png.h pngconf.h pngusr.h $(PNGCONF)
+
+# Headers the PNG library uses
+PNGHDRS=$(PNGH) $(PNGCONF) pngusr.h
+
+PNGOBJS=png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
 	pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
-	pngset$(O) pngtrans$(O)  $(ZOBJS)
+	pngset$(O) pngtrans$(O)
+
+PROGSRCS= pngm2pnm$(C)
+PROGHDRS=
+PROGDOCS=
+PROGOBJS= pngm2pnm$(O)
+
+OBJS    = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
 
 # implicit make rules -------------------------------------------------------
 
-.c$(O): png.h pngconf.h pngusr.h zlib.h
-	$(CC) -c $(CFLAGS) $<
+# note: dependencies do not work on implicit rule lines
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 # dependencies
 
@@ -34,11 +90,62 @@
 
 pngm2pnm$(E): $(OBJS)
 	$(LD) -o pngm2pnm$(E) $(OBJS)
-	strip pngm2pnm$(E)
+
+# The DFA_XTRA setting turns all libpng options off then
+# turns on those required for this minimal build.
+# The CPP_FLAGS setting causes pngusr.h to be included in
+# both the build of pnglibconf.h and, subsequently, when
+# building libpng itself.
+$(PNGCONF): $(PNGSRC)/scripts/pnglibconf.mak $(ZH)\
+	$(PNGSRC)/scripts/pnglibconf.dfa \
+	$(PNGSRC)/scripts/options.awk pngusr.h pngusr.dfa
+	$(RM) pnglibconf.h pnglibconf.dfn
+	$(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+	    srcdir=$(PNGSRC) CPPFLAGS="-DPNG_USER_CONFIG -I."\
+	    DFA_XTRA="pngusr.dfa" $@
 
 clean:
+	$(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+	    srcdir=$(PNGSRC) clean
 	$(RM) pngm2pnm$(O)
 	$(RM) pngm2pnm$(E)
 	$(RM) $(OBJS)
 
+# distclean also removes the copied source and headers
+distclean: clean
+	$(RM) -r scripts # historical reasons
+	$(RM) $(PNGSRCS) $(PNGH)
+	$(RM) $(ZSRCS) $(ZH) $(ZCONF)
+	$(RM) $(PROGSRCS) $(PROGHDRS) $(PROGDOCS)
+
+# Header file dependencies:
+$(PROGOBJS): $(PROGHDRS) $(PNGINC) $(ZINC)
+$(PNGOBJS): $(PNGHDRS) $(ZINC)
+$(ZOBJS): $(ZHDRS)
+
+# Gather the source code from the respective directories
+$(PNGSRCS) $(PNGH): $(PNGSRC)/$@
+	$(RM) $@
+	$(COPY) $(PNGSRC)/$@ $@
+
+# No dependency on the ZLIBSRC target so that it only needs
+# to be specified once.
+$(ZSRCS) $(ZH):
+	$(RM) $@
+	$(COPY) $(ZLIBSRC)/$@ $@
+
+# The unconfigured zconf.h varies in name according to the
+# zlib release
+$(ZCONF):
+	$(RM) $@
+	@for f in zconf.h.in zconf.in.h zconf.h; do\
+	    test -r $(ZLIBSRC)/$$f &&\
+	    echo $(COPY) $(ZLIBSRC)/$$f $@ &&\
+	    $(COPY) $(ZLIBSRC)/$$f $@ && exit 0;\
+	done; echo copy: $(ZLIBSRC)/zconf.h not found; exit 1
+
+pngm2pnm.c: $(PROGSRC)/png2pnm.c
+	$(RM) $@
+	$(COPY) $(PROGSRC)/png2pnm.c $@
+
 # End of makefile for pngm2pnm
diff --git a/contrib/pngminim/decoder/makefile.std b/contrib/pngminim/decoder/makefile.std
deleted file mode 100644
index 27e04cb..0000000
--- a/contrib/pngminim/decoder/makefile.std
+++ /dev/null
@@ -1,44 +0,0 @@
-# Makefile for PngMinus (pngm2pnm)
-# Linux / Unix
-
-#CC=cc
-CC=gcc
-LD=$(CC)
-
-RM=rm -f
-
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \
-       -DdeflateParams\(a,b,c\)=Z_OK -I. -O1
-
-C=.c
-O=.o
-L=.a
-E=
-
-ZOBJS  = adler32$(O) crc32$(O) gzio$(O) \
-	 infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
-	 trees$(O) uncompr$(O) zutil$(O)
-
-OBJS  = pngm2pnm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
-	pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
-	pngset$(O) pngtrans$(O)  $(ZOBJS)
-
-# implicit make rules -------------------------------------------------------
-
-.c$(O): png.h pngconf.h pngusr.h zlib.h
-	$(CC) -c $(CFLAGS) $<
-
-# dependencies
-
-all: pngm2pnm$(E)
-
-pngm2pnm$(E): $(OBJS)
-	$(LD) -o pngm2pnm$(E) $(OBJS)
-	strip pngm2pnm$(E)
-
-clean:
-	$(RM) pngm2pnm$(O)
-	$(RM) pngm2pnm$(E)
-	$(RM) $(OBJS)
-
-# End of makefile for pngm2pnm
diff --git a/contrib/pngminim/decoder/pngusr.dfa b/contrib/pngminim/decoder/pngusr.dfa
new file mode 100644
index 0000000..4f40272
--- /dev/null
+++ b/contrib/pngminim/decoder/pngusr.dfa
@@ -0,0 +1,40 @@
+# pngminim/decoder/pngusr.dfa
+#
+# Copyright (c) 2010-2013 Glenn Randers-Pehrson
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# First all the build options off:
+
+everything = off
+
+# All that is required is some read code. This example switches
+# on the sequential read code (see ../preader for a progressive
+# read example).
+
+option SEQUENTIAL_READ on
+
+# You must choose fixed or floating point arithmetic:
+# option FLOATING_POINT on
+
+option FIXED_POINT on
+
+# You must chose the internal fixed point implementation or to
+# use the system floating point.  The latter is considerably
+# smaller (by about 1kbyte on an x86 system):
+# option FLOATING_ARITHMETIC on
+
+option FLOATING_ARITHMETIC off
+
+# Your program will probably need other options.  The example
+# program here, pngm2pnm, requires the following.  Take a look
+# at pnglibconf.h to find out the full set of what has to be
+# enabled to make the following work.
+
+option SETJMP on
+option STDIO on
+option READ_EXPAND on
+option READ_STRIP_16_TO_8 on
+option USER_LIMITS on
diff --git a/contrib/pngminim/decoder/pngusr.h b/contrib/pngminim/decoder/pngusr.h
index 2312a78..cbd7890 100644
--- a/contrib/pngminim/decoder/pngusr.h
+++ b/contrib/pngminim/decoder/pngusr.h
@@ -1,6 +1,6 @@
 /* minrdpngconf.h: headers to make a minimal png-read-only library
  *
- * Copyright (c) 2007, 2009 Glenn Randers-Pehrson
+ * Copyright (c) 2007, 2010-2013 Glenn Randers-Pehrson
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -12,67 +12,12 @@
 #ifndef MINRDPNGCONF_H
 #define MINRDPNGCONF_H
 
-#ifdef NJET
-  /* No 16-bit support beyond reading with strip_16 */
-#endif
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
 
-#define PNG_NO_GLOBAL_ARRAYS
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
 
-#define PNG_NO_WARNINGS
-#define png_warning(s1,s2) ""
-#define png_chunk_warning(s1,s2) ""
-#define PNG_NO_ERROR_TEXT
-#define png_error(s1,s2) png_err(s1)
-#define png_chunk_error(s1,s2) png_err(s1)
-
-#define PNG_NO_ASSEMBLER_CODE
-#define PNG_NO_OPTIMIZED_CODE
-#define PNG_NO_READ_GAMMA
-#define PNG_NO_READ_BACKGROUND
-#define PNG_NO_READ_DITHER
-#define PNG_NO_READ_INVERT
-#define PNG_NO_READ_SHIFT
-#define PNG_NO_READ_PACK
-#define PNG_NO_READ_PACKSWAP
-#define PNG_NO_READ_FILLER
-#define PNG_NO_READ_SWAP
-#define PNG_NO_READ_SWAP_ALPHA
-#define PNG_NO_READ_INVERT_ALPHA
-#define PNG_NO_READ_RGB_TO_GRAY
-#define PNG_NO_READ_USER_TRANSFORM
-#define PNG_NO_READ_bKGD
-#define PNG_NO_READ_cHRM
-#define PNG_NO_READ_gAMA
-#define PNG_NO_READ_hIST
-#define PNG_NO_READ_iCCP
-#define PNG_NO_READ_pCAL
-#define PNG_NO_READ_pHYs
-#define PNG_NO_READ_sBIT
-#define PNG_NO_READ_sCAL
-#define PNG_NO_READ_sPLT
-#define PNG_NO_READ_sRGB
-#define PNG_NO_READ_TEXT
-#define PNG_NO_READ_tIME
-#define PNG_NO_READ_UNKNOWN_CHUNKS
-#define PNG_NO_READ_USER_CHUNKS
-#define PNG_NO_READ_EMPTY_PLTE
-#define PNG_NO_READ_OPT_PLTE
-#define PNG_NO_READ_STRIP_ALPHA
-#define PNG_NO_READ_oFFs
-#define PNG_NO_WARN_UNINITIALIZED_ROW
-
-#define PNG_NO_WRITE_SUPPORTED
-
-#define PNG_NO_INFO_IMAGE
-#define PNG_NO_USER_MEM
-#define PNG_NO_FIXED_POINT_SUPPORTED
-#define PNG_NO_MNG_FEATURES
-#define PNG_NO_USER_TRANSFORM_PTR
-#define PNG_NO_HANDLE_AS_UNKNOWN
-#define PNG_NO_CONSOLE_IO
-#define PNG_NO_ZALLOC_ZERO
-#define PNG_NO_ERROR_NUMBERS
-#define PNG_NO_EASY_ACCESS
-#define PNG_NO_PROGRESSIVE_READ
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
 
 #endif /* MINRDPNGCONF_H */
diff --git a/contrib/pngminim/encoder/README b/contrib/pngminim/encoder/README
index 2285544..d8d9a64 100644
--- a/contrib/pngminim/encoder/README
+++ b/contrib/pngminim/encoder/README
@@ -1,9 +1,10 @@
 This demonstrates the use of PNG_USER_CONFIG and pngusr.h
 
-To build a minimal write-only decoder with embedded libpng and zlib, run
+The makefile builds a minimal write-only encoder with embedded libpng
+and zlib.
 
-    gather.sh  # to collect needed files from pngminus, libpng, and zlib
-    make
- 
+Specify the location of the zlib source (1.2.1 or later) as ZLIBSRC
+on the make command line.
+
 If you prefer to use the shared libraries, go to contrib/pngminus
-and build the pnm2png application there. 
+and build the pnm2png application there.
diff --git a/contrib/pngminim/encoder/dummy_inflate.c b/contrib/pngminim/encoder/dummy_inflate.c
deleted file mode 100644
index 1422edb..0000000
--- a/contrib/pngminim/encoder/dummy_inflate.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "zlib.h"
-
-int ZEXPORT inflate(strm, flush)
-z_streamp strm;
-int flush;
-{ return Z_OK ; }
-
-int ZEXPORT inflateReset(strm)
-z_streamp strm;
-{ return Z_OK ; }
-
-int ZEXPORT inflateEnd(strm)
-z_streamp strm;
-{ return Z_STREAM_ERROR ; }
-
-int ZEXPORT inflateInit_(strm, version, stream_size)
-z_streamp strm;
-const char *version;
-int stream_size;
-{ return Z_OK ; }
-
-int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
-z_streamp strm;
-int windowBits;
-const char *version;
-int stream_size;
-{ return Z_STREAM_ERROR ; }
diff --git a/contrib/pngminim/encoder/gather.sh b/contrib/pngminim/encoder/gather.sh
deleted file mode 100755
index 61d0b13..0000000
--- a/contrib/pngminim/encoder/gather.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-cp ../../pngminus/pnm2png.c pnm2pngm.c
-cp ../../../*.h .
-cp ../../../*.c .
-rm pnggccrd.c pngvcrd.c
-rm example.c pngtest.c pngr*.c pngpread.c
-# Change the next 2 lines if zlib is somewhere else.
-cp ../../../../zlib/*.h .
-cp ../../../../zlib/*.c .
-rm inf*.[ch]
-rm minigzip.c example.c gz*
diff --git a/contrib/pngminim/encoder/makefile b/contrib/pngminim/encoder/makefile
index dfde950..41b205c 100644
--- a/contrib/pngminim/encoder/makefile
+++ b/contrib/pngminim/encoder/makefile
@@ -5,27 +5,83 @@
 CC=gcc
 LD=$(CC)
 
-RM=rm -f
+# If awk fails try
+# make AWK=nawk
 
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZIP -I. -O1
+# If cpp fails try
+# make CPP=/lib/cpp
+
+RM=rm -f
+COPY=cp
+
+CPPFLAGS=-I. -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
 
 C=.c
 O=.o
 L=.a
 E=
 
-ZOBJS  = adler32$(O) compress$(O) crc32$(O) deflate$(O) \
-	 dummy_inflate$(O) \
-	 trees$(O) uncompr$(O) zutil$(O)
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/pngminus
 
-OBJS  = pnm2pngm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# Zlib
+ZSRCS  = adler32$(C) compress$(C) crc32$(C) deflate$(C) \
+	 trees$(C) zutil$(C)
+
+# Standard headers
+#ZH     = zlib.h crc32.h deflate.h trees.h zutil.h
+ZH     = zlib.h crc32.h deflate.h trees.h zutil.h
+
+# Machine generated headers
+ZCONF  = zconf.h
+
+# Headers callers use
+ZINC   = zlib.h $(ZCONF)
+
+# Headers the Zlib source uses
+ZHDRS  = $(ZH) $(ZCONF)
+
+# compress is not required; it is needed to link the zlib
+# code because deflate defines an unused API function deflateBound
+# which itself calls compressBound from compress.
+ZOBJS  = adler32$(O) compress$(O) crc32$(O) deflate$(O) \
+	 trees$(O) zutil$(O)
+
+# libpng
+PNGSRCS=png$(C) pngerror$(C) pngget$(C) pngmem$(C) \
+	pngset$(C) pngtrans$(C) pngwio$(C) pngwrite$(C) \
+	pngwtran$(C) pngwutil$(C)
+
+# Standard headers
+PNGH   =png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h
+
+# Machine generated headers
+PNGCONF=pnglibconf.h
+
+# Headers callers use
+PNGINC= png.h pngconf.h pngusr.h $(PNGCONF)
+
+# Headers the PNG library uses
+PNGHDRS=$(PNGH) $(PNGCONF) pngusr.h
+
+PNGOBJS=png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
 	pngset$(O) pngtrans$(O) pngwio$(O) pngwrite$(O) \
-	pngwtran$(O) pngwutil$(O) $(ZOBJS)
+	pngwtran$(O) pngwutil$(O)
+
+PROGSRCS= pnm2pngm$(C)
+PROGHDRS=
+PROGDOCS=
+PROGOBJS= pnm2pngm$(O)
+
+OBJS    = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
 
 # implicit make rules -------------------------------------------------------
 
-.c$(O): png.h pngconf.h pngusr.h zlib.h
-	$(CC) -c $(CFLAGS) $<
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 # dependencies
 
@@ -33,11 +89,62 @@
 
 pnm2pngm$(E): $(OBJS)
 	$(LD) -o pnm2pngm$(E) $(OBJS)
-	strip pnm2pngm$(E)
+
+# The DFA_XTRA setting turns all libpng options off then
+# turns on those required for this minimal build.
+# The CPP_FLAGS setting causes pngusr.h to be included in
+# both the build of pnglibconf.h and, subsequently, when
+# building libpng itself.
+$(PNGCONF): $(PNGSRC)/scripts/pnglibconf.mak $(ZH)\
+	$(PNGSRC)/scripts/pnglibconf.dfa \
+	$(PNGSRC)/scripts/options.awk pngusr.h pngusr.dfa
+	$(RM) pnglibconf.h pnglibconf.dfn
+	$(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+	    srcdir=$(PNGSRC) CPPFLAGS="-DPNG_USER_CONFIG -I."\
+	    DFA_XTRA="pngusr.dfa" $@
 
 clean:
+	$(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+	    srcdir=$(PNGSRC) clean
 	$(RM) pnm2pngm$(O)
 	$(RM) pnm2pngm$(E)
 	$(RM) $(OBJS)
 
+# distclean also removes the copied source and headers
+distclean: clean
+	$(RM) -r scripts # historical reasons
+	$(RM) $(PNGSRCS) $(PNGH)
+	$(RM) $(ZSRCS) $(ZH) $(ZCONF)
+	$(RM) $(PROGSRCS) $(PROGHDRS) $(PROGDOCS)
+
+# Header file dependencies:
+$(PROGOBJS): $(PROGHDRS) $(PNGINC) $(ZINC)
+$(PNGOBJS): $(PNGHDRS) $(ZINC)
+$(ZOBJS): $(ZHDRS)
+
+# Gather the source code from the respective directories
+$(PNGSRCS) $(PNGH): $(PNGSRC)/$@
+	$(RM) $@
+	$(COPY) $(PNGSRC)/$@ $@
+
+# No dependency on the ZLIBSRC target so that it only needs
+# to be specified once.
+$(ZSRCS) $(ZH):
+	$(RM) $@
+	$(COPY) $(ZLIBSRC)/$@ $@
+
+# The unconfigured zconf.h varies in name according to the
+# zlib release
+$(ZCONF):
+	$(RM) $@
+	@for f in zconf.h.in zconf.in.h zconf.h; do\
+	    test -r $(ZLIBSRC)/$$f &&\
+	    echo $(COPY) $(ZLIBSRC)/$$f $@ &&\
+	    $(COPY) $(ZLIBSRC)/$$f $@ && exit 0;\
+	done; echo copy: $(ZLIBSRC)/zconf.h not found; exit 1
+
+pnm2pngm.c: $(PROGSRC)/pnm2png.c
+	$(RM) $@
+	$(COPY) $(PROGSRC)/pnm2png.c $@
+
 # End of makefile for pnm2pngm
diff --git a/contrib/pngminim/encoder/makefile.std b/contrib/pngminim/encoder/makefile.std
deleted file mode 100644
index 1182b5b..0000000
--- a/contrib/pngminim/encoder/makefile.std
+++ /dev/null
@@ -1,43 +0,0 @@
-# Makefile for PngMinus (pnm2pngm)
-# Linux / Unix
-
-#CC=cc
-CC=gcc
-LD=$(CC)
-
-RM=rm -f
-
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZIP -I. -O1
-
-C=.c
-O=.o
-L=.a
-E=
-
-ZOBJS  = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzio$(O) \
-	 dummy_inflate$(O) \
-	 trees$(O) uncompr$(O) zutil$(O)
-
-OBJS  = pnm2pngm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
-	pngset$(O) pngtrans$(O) pngwio$(O) pngwrite$(O) \
-	pngwtran$(O) pngwutil$(O) $(ZOBJS)
-
-# implicit make rules -------------------------------------------------------
-
-.c$(O): png.h pngconf.h pngusr.h zlib.h
-	$(CC) -c $(CFLAGS) $<
-
-# dependencies
-
-all: pnm2pngm$(E)
-
-pnm2pngm$(E): $(OBJS)
-	$(LD) -o pnm2pngm$(E) $(OBJS)
-	strip pnm2pngm$(E)
-
-clean:
-	$(RM) pnm2pngm$(O)
-	$(RM) pnm2pngm$(E)
-	$(RM) $(OBJS)
-
-# End of makefile for pnm2pngm
diff --git a/contrib/pngminim/encoder/pngusr.dfa b/contrib/pngminim/encoder/pngusr.dfa
new file mode 100644
index 0000000..1fc24f3
--- /dev/null
+++ b/contrib/pngminim/encoder/pngusr.dfa
@@ -0,0 +1,39 @@
+# pngminim/encoder/pngusr.dfa
+#
+# Copyright (c) 2010-2013 Glenn Randers-Pehrson
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# First all the build options off:
+
+everything = off
+
+# Switch on the write code - this makes a minimalist encoder
+
+option WRITE on
+
+# These 2 options are required if you need to read PBM (P1 or P4) files.
+option WRITE_INVERT on
+option WRITE_PACK on
+
+# You must choose fixed or floating point arithmetic:
+# option FLOATING_POINT on
+
+option FIXED_POINT on
+
+# You must chose the internal fixed point implementation or to
+# use the system floating point.  The latter is considerably
+# smaller (by about 1kbyte on an x86 system):
+# option FLOATING_ARITHMETIC on
+
+option FLOATING_ARITHMETIC off
+
+# Your program will probably need other options.  The example
+# program here, pnm2pngm, requires the following.  Take a look
+# at pnglibconf.h to find out the full set of what has to be
+# enabled to make the following work.
+
+option SETJMP on
+option STDIO on
diff --git a/contrib/pngminim/encoder/pngusr.h b/contrib/pngminim/encoder/pngusr.h
index c98b547..997d44f 100644
--- a/contrib/pngminim/encoder/pngusr.h
+++ b/contrib/pngminim/encoder/pngusr.h
@@ -1,6 +1,6 @@
 /* minwrpngconf.h: headers to make a minimal png-write-only library
  *
- * Copyright (c) 2007, 2009 Glenn Randers-Pehrson
+ * Copyright (c) 2007, 2010-2013 Glenn Randers-Pehrson
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -12,62 +12,12 @@
 #ifndef MINWRPNGCONF_H
 #define MINWRPNGCONF_H
 
-#define PNG_NO_GLOBAL_ARRAYS
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
 
-#define PNG_NO_READ_SUPPORTED
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
 
-#define PNG_NO_WARNINGS
-#define png_warning(s1,s2) ""
-#define png_chunk_warning(s1,s2) ""
-#define PNG_NO_ERROR_TEXT
-#define png_error(s1,s2) png_err(s1)
-#define png_chunk_error(s1,s2) png_err(s1)
-
-#define PNG_NO_WRITE_BACKGROUND
-#define PNG_NO_WRITE_GAMMA
-#define PNG_NO_WRITE_DITHER
-#define PNG_NO_WRITE_INVERT
-#define PNG_NO_WRITE_SHIFT
-#define PNG_NO_WRITE_PACK
-#define PNG_NO_WRITE_PACKSWAP
-#define PNG_NO_WRITE_FILLER
-#define PNG_NO_WRITE_SWAP
-#define PNG_NO_WRITE_SWAP_ALPHA
-#define PNG_NO_WRITE_INVERT_ALPHA
-#define PNG_NO_WRITE_RGB_TO_GRAY
-#define PNG_NO_WRITE_USER_TRANSFORM
-#define PNG_NO_WRITE_bKGD
-#define PNG_NO_WRITE_cHRM
-#define PNG_NO_WRITE_gAMA
-#define PNG_NO_WRITE_hIST
-#define PNG_NO_WRITE_iCCP
-#define PNG_NO_WRITE_oFFs
-#define PNG_NO_WRITE_pCAL
-#define PNG_NO_WRITE_pHYs
-#define PNG_NO_WRITE_sBIT
-#define PNG_NO_WRITE_sCAL
-#define PNG_NO_WRITE_sPLT
-#define PNG_NO_WRITE_sRGB
-#define PNG_NO_WRITE_TEXT
-#define PNG_NO_WRITE_tIME
-#define PNG_NO_WRITE_UNKNOWN_CHUNKS
-#define PNG_NO_WRITE_USER_CHUNKS
-#define PNG_NO_WRITE_EMPTY_PLTE
-#define PNG_NO_WRITE_OPT_PLTE
-#define PNG_NO_WRITE_FILTER
-#define PNG_NO_WRITE_WEIGHTED_FILTER
-#define PNG_NO_WRITE_INTERLACING_SUPPORTED
-#define PNG_NO_WRITE_FLUSH
-
-#define PNG_NO_INFO_IMAGE
-#define PNG_NO_USER_MEM
-#define PNG_NO_FIXED_POINT_SUPPORTED
-#define PNG_NO_MNG_FEATURES
-#define PNG_NO_USER_TRANSFORM_PTR
-#define PNG_NO_HANDLE_AS_UNKNOWN
-#define PNG_NO_CONSOLE_IO
-#define PNG_NO_ZALLOC_ZERO
-#define PNG_NO_ERROR_NUMBERS
-#define PNG_NO_EASY_ACCESS
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
 
 #endif /* MINWRPNGCONF_H */
diff --git a/contrib/pngminim/preader/README b/contrib/pngminim/preader/README
index 377d641..faa8356 100644
--- a/contrib/pngminim/preader/README
+++ b/contrib/pngminim/preader/README
@@ -1,14 +1,15 @@
 This demonstrates the use of PNG_USER_CONFIG and pngusr.h
 
-To build a minimal read-only progressive decoder embedded libpng and
-zlib and with your system's X library, run
+The makefile builds a minimal read-only progressive decoder with
+embedded libpng, zlib and your system's X library.
 
-    gather.sh # to collect needed files from gregbook, libpng, and zlib
+Specify the location of the zlib source (1.2.1 or later) as ZLIBSRC
+on the make command line.
 
 Edit makefile if required, to find your X library and include files,
 then
-    
-    make
- 
+
+    make ZLIBSRC=directory
+
 If you prefer to use the shared libraries, go to contrib/gregbook
-and build the rpng2-x application there. 
+and build the rpng2-x application there.
diff --git a/contrib/pngminim/preader/gather.sh b/contrib/pngminim/preader/gather.sh
deleted file mode 100755
index 357bb9a..0000000
--- a/contrib/pngminim/preader/gather.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-cp ../../gregbook/rpng2-x.c ../../gregbook/readpng2.[ch] .
-cp ../../gregbook/COPYING ../../gregbook/LICENSE .
-cp ../../../*.h .
-cp ../../../*.c .
-rm pnggccrd.c pngvcrd.c
-rm example.c pngtest.c pngw*.c
-# change the following 2 lines if zlib is somewhere else
-cp ../../../../zlib/*.h .
-cp ../../../../zlib/*.c .
-rm minigzip.c example.c compress.c deflate.c gz*
diff --git a/contrib/pngminim/preader/makefile b/contrib/pngminim/preader/makefile
index 717978d..b625199 100644
--- a/contrib/pngminim/preader/makefile
+++ b/contrib/pngminim/preader/makefile
@@ -5,7 +5,14 @@
 CC=gcc
 LD=$(CC)
 
+# If awk fails try
+# make AWK=nawk
+
+# If cpp fails try
+# make CPP=/lib/cpp
+
 RM=rm -f
+COPY=cp
 
 #XINC = -I/usr/include			# old-style, stock X distributions
 #XLIB = -L/usr/lib/X11 -lX11		#  (including SGI IRIX)
@@ -23,38 +30,137 @@
 #LIBS = $(XLIB)
 LIBS = $(XLIB) -lm                      #platforms that need libm
 
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \
-       -DdeflateParams\(a,b,c\)=Z_OK -I. $(XINC) -O1
+CPPFLAGS=-I. $(XINC) -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
 
 C=.c
 O=.o
 L=.a
 E=
 
-ZOBJS  = adler32$(O) crc32$(O) \
-	 infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
-	 trees$(O) uncompr$(O) zutil$(O)
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/gregbook
 
-OBJS  = rpng2-x$(O) readpng2$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# Zlib (minimal inflate requirements - crc32 is used by libpng)
+# zutil can be eliminated if you provide your own zcalloc and zcfree
+ZSRCS  = adler32$(C) crc32$(C) \
+	 inffast$(C) inflate$(C) inftrees$(C) \
+	 zutil$(C)
+
+# Standard headers
+ZH     = zlib.h crc32.h inffast.h inffixed.h \
+	 inflate.h inftrees.h zutil.h
+
+# Machine generated headers
+ZCONF  = zconf.h
+
+# Headers callers use
+ZINC   = zlib.h $(ZCONF)
+
+# Headers the Zlib source uses
+ZHDRS  = $(ZH) $(ZCONF)
+
+ZOBJS  = adler32$(O) crc32$(O) \
+	 inffast$(O) inflate$(O) inftrees$(O) \
+	 zutil$(O)
+
+# libpng
+PNGSRCS=png$(C) pngerror$(C) pngget$(C) pngmem$(C) \
+	pngpread$(C) pngread$(C) pngrio$(C) pngrtran$(C) pngrutil$(C) \
+	pngset$(C) pngtrans$(C)
+
+# Standard headers
+PNGH   =png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h
+
+# Machine generated headers
+PNGCONF=pnglibconf.h
+
+# Headers callers use
+PNGINC= png.h pngconf.h pngusr.h $(PNGCONF)
+
+# Headers the PNG library uses
+PNGHDRS=$(PNGH) $(PNGCONF) pngusr.h
+
+PNGOBJS=png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
 	pngpread$(O) pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
-	pngset$(O) pngtrans$(O)  $(ZOBJS)
+	pngset$(O) pngtrans$(O)
+
+PROGSRCS= rpng2-x$(C) readpng2$(C)
+PROGHDRS= readpng2.h
+PROGDOCS= COPYING LICENSE
+PROGOBJS= rpng2-x$(O) readpng2$(O)
+
+OBJS    = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
 
 # implicit make rules -------------------------------------------------------
 
-.c$(O): png.h pngconf.h readpng2.h pngusr.h zlib.h
-	$(CC) -c $(CFLAGS) $<
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
 
 # dependencies
 
-all: rpng2-x$(E)
+all: $(PROGDOCS) rpng2-x$(E)
 
 rpng2-x$(E): $(OBJS)
 	$(LD) -o rpng2-x$(E) $(OBJS) $(LIBS)
-	strip rpng2-x$(E)
+
+# The DFA_XTRA setting turns all libpng options off then
+# turns on those required for this minimal build.
+# The CPP_FLAGS setting causes pngusr.h to be included in
+# both the build of pnglibconf.h and, subsequently, when
+# building libpng itself.
+$(PNGCONF): $(PNGSRC)/scripts/pnglibconf.mak $(ZH)\
+	$(PNGSRC)/scripts/pnglibconf.dfa \
+	$(PNGSRC)/scripts/options.awk pngusr.h pngusr.dfa
+	$(RM) pnglibconf.h pnglibconf.dfn
+	$(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+	    srcdir=$(PNGSRC) CPPFLAGS="-DPNG_USER_CONFIG -I."\
+	    DFA_XTRA="pngusr.dfa" $@
 
 clean:
+	$(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+	    srcdir=$(PNGSRC) clean
 	$(RM) rpng2-x$(O)
 	$(RM) rpng2-x$(E)
 	$(RM) $(OBJS)
 
+# distclean also removes the copied source and headers
+distclean: clean
+	$(RM) -r scripts # historical reasons
+	$(RM) $(PNGSRCS) $(PNGH)
+	$(RM) $(ZSRCS) $(ZH) $(ZCONF)
+	$(RM) $(PROGSRCS) $(PROGHDRS) $(PROGDOCS)
+
+# Header file dependencies:
+$(PROGOBJS): $(PROGHDRS) $(PNGINC) $(ZINC)
+$(PNGOBJS): $(PNGHDRS) $(ZINC)
+$(ZOBJS): $(ZHDRS)
+
+# Gather the source code from the respective directories
+$(PNGSRCS) $(PNGH): $(PNGSRC)/$@
+	$(RM) $@
+	$(COPY) $(PNGSRC)/$@ $@
+
+# No dependency on the ZLIBSRC target so that it only needs
+# to be specified once.
+$(ZSRCS) $(ZH):
+	$(RM) $@
+	$(COPY) $(ZLIBSRC)/$@ $@
+
+# The unconfigured zconf.h varies in name according to the
+# zlib release
+$(ZCONF):
+	$(RM) $@
+	@for f in zconf.h.in zconf.in.h zconf.h; do\
+	    test -r $(ZLIBSRC)/$$f &&\
+	    echo $(COPY) $(ZLIBSRC)/$$f $@ &&\
+	    $(COPY) $(ZLIBSRC)/$$f $@ && exit 0;\
+	done; echo copy: $(ZLIBSRC)/zconf.h not found; exit 1
+
+$(PROGSRCS) $(PROGHDRS) $(PROGDOCS): $(PROGSRC)/$@
+	$(RM) $@
+	$(COPY) $(PROGSRC)/$@ $@
+
 # End of makefile for rpng2-x
diff --git a/contrib/pngminim/preader/pngusr.dfa b/contrib/pngminim/preader/pngusr.dfa
new file mode 100644
index 0000000..ac297f4
--- /dev/null
+++ b/contrib/pngminim/preader/pngusr.dfa
@@ -0,0 +1,40 @@
+# pngminim/preader/pngusr.dfa
+#
+# Copyright (c) 2010-2013 Glenn Randers-Pehrson
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# First all the build options off:
+
+everything = off
+
+# Just switch on the progressive read code
+
+option PROGRESSIVE_READ on
+
+# You may choose fixed or floating point APIs:
+# option FLOATING_POINT on
+
+option FIXED_POINT on
+
+# You must chose the internal fixed point implementation or to
+# use the system floating point.  The latter is considerably
+# smaller (by about 1kbyte on an x86 system):
+
+option FLOATING_ARITHMETIC on
+# option FLOATING_ARITHMETIC off
+
+# Your program will probably need other options.  The example
+# program here, rpng2-x, requires the following.  Take a look
+# at pnglibconf.h to find out the full set of what has to be
+# enabled to make the following work.
+
+option SETJMP on
+option STDIO on
+option READ_bKGD on
+option READ_GAMMA on
+option READ_EXPAND on
+option READ_STRIP_16_TO_8 on
+option READ_GRAY_TO_RGB on
diff --git a/contrib/pngminim/preader/pngusr.h b/contrib/pngminim/preader/pngusr.h
index 7d728c8..80db3bb 100644
--- a/contrib/pngminim/preader/pngusr.h
+++ b/contrib/pngminim/preader/pngusr.h
@@ -1,6 +1,6 @@
 /* minrdpngconf.h: headers to make a minimal png-read-only library
  *
- * Copyright (c) 2009 Glenn Randers-Pehrson
+ * Copyright (c) 2009, 2010-2013 Glenn Randers-Pehrson
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -12,57 +12,12 @@
 #ifndef MINPRDPNGCONF_H
 #define MINPRDPNGCONF_H
 
-#define PNG_NO_GLOBAL_ARRAYS
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
 
-#define PNG_NO_WARNINGS
-#define png_warning(s1,s2) ""
-#define png_chunk_warning(s1,s2) ""
-#define PNG_NO_ERROR_TEXT
-#define png_error(s1,s2) png_err(s1)
-#define png_chunk_error(s1,s2) png_err(s1)
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
 
-#define PNG_NO_ASSEMBLER_CODE
-#define PNG_NO_OPTIMIZED_CODE
-#define PNG_NO_READ_DITHER
-#define PNG_NO_READ_INVERT
-#define PNG_NO_READ_SHIFT
-#define PNG_NO_READ_PACK
-#define PNG_NO_READ_PACKSWAP
-#define PNG_NO_READ_FILLER
-#define PNG_NO_READ_SWAP
-#define PNG_NO_READ_SWAP_ALPHA
-#define PNG_NO_READ_INVERT_ALPHA
-#define PNG_NO_READ_RGB_TO_GRAY
-#define PNG_NO_READ_USER_TRANSFORM
-#define PNG_NO_READ_cHRM
-#define PNG_NO_READ_hIST
-#define PNG_NO_READ_iCCP
-#define PNG_NO_READ_pCAL
-#define PNG_NO_READ_pHYs
-#define PNG_NO_READ_sBIT
-#define PNG_NO_READ_sCAL
-#define PNG_NO_READ_sPLT
-#define PNG_NO_READ_TEXT
-#define PNG_NO_READ_tIME
-#define PNG_NO_READ_UNKNOWN_CHUNKS
-#define PNG_NO_READ_USER_CHUNKS
-#define PNG_NO_READ_EMPTY_PLTE
-#define PNG_NO_READ_OPT_PLTE
-#define PNG_NO_READ_STRIP_ALPHA
-#define PNG_NO_READ_oFFs
-#define PNG_NO_WARN_UNINITIALIZED_ROW
-
-#define PNG_NO_WRITE_SUPPORTED
-
-#define PNG_NO_INFO_IMAGE
-#define PNG_NO_USER_MEM
-#define PNG_NO_FIXED_POINT_SUPPORTED
-#define PNG_NO_MNG_FEATURES
-#define PNG_NO_USER_TRANSFORM_PTR
-#define PNG_NO_HANDLE_AS_UNKNOWN
-#define PNG_NO_CONSOLE_IO
-#define PNG_NO_ZALLOC_ZERO
-#define PNG_NO_ERROR_NUMBERS
-#define PNG_NO_EASY_ACCESS
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
 
 #endif /* MINPRDPNGCONF_H */
diff --git a/contrib/pngminus/makefile.std b/contrib/pngminus/makefile.std
index 2fb061b..14e25cd 100644
--- a/contrib/pngminus/makefile.std
+++ b/contrib/pngminus/makefile.std
@@ -8,9 +8,9 @@
 RM=rm -f
 
 #PNGPATH = /usr/local
-#PNGINC = -I$(PNGPATH)/include/libpng12
-#PNGLIB = -L$(PNGPATH)/lib -lpng12
-#PNGLIBS = $(PNGPATH)/lib/libpng12.a
+#PNGINC = -I$(PNGPATH)/include/libpng16
+#PNGLIB = -L$(PNGPATH)/lib -lpng16
+#PNGLIBS = $(PNGPATH)/lib/libpng16.a
 PNGINC = -I../..
 PNGLIB = -L../.. -lpng
 PNGLIBS = ../../libpng.a
@@ -23,9 +23,10 @@
 ZLIB = -L../../../zlib -lz
 ZLIBS = ../../../zlib/libz.a
 
-CFLAGS=-O3 $(PNGINC) $(ZINC)
-LDFLAGS=$(PNGLIB) $(ZLIB)
-LDFLAGSS=$(PNGLIBS) $(ZLIBS)
+CPPFLAGS=$(PNGINC) $(ZINC)
+CFLAGS=
+LDLIBS=$(PNGLIB) $(ZLIB)
+LDLIBSS=$(PNGLIBS) $(ZLIBS)
 C=.c
 O=.o
 L=.a
@@ -37,22 +38,22 @@
 all: png2pnm$(E) pnm2png$(E) png2pnm-static$(E) pnm2png-static$(E)
 
 png2pnm$(O): png2pnm$(C)
-	$(CC) -c $(CFLAGS) png2pnm$(C)
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) png2pnm$(C)
 
 png2pnm$(E): png2pnm$(O)
-	$(LD) -o png2pnm$(E) png2pnm$(O) $(LDFLAGS) -lm
+	$(LD) $(LDFLAGS) -o png2pnm$(E) png2pnm$(O) $(LDLIBS) -lm
 
 png2pnm-static$(E): png2pnm$(O)
-	$(LD) -o png2pnm-static$(E) png2pnm$(O) $(LDFLAGSS) -lm
+	$(LD) $(LDFLAGS) -o png2pnm-static$(E) png2pnm$(O) $(LDLIBSS) -lm
 
 pnm2png$(O): pnm2png$(C)
-	$(CC) -c $(CFLAGS) pnm2png$(C)
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) pnm2png$(C)
 
 pnm2png$(E): pnm2png$(O)
-	$(LD) -o pnm2png$(E) pnm2png$(O) $(LDFLAGS) -lm
+	$(LD) $(LDFLAGS) -o pnm2png$(E) pnm2png$(O) $(LDLIBS) -lm
 
 pnm2png-static$(E): pnm2png$(O)
-	$(LD) -o pnm2png-static$(E) pnm2png$(O) $(LDFLAGSS) -lm
+	$(LD) $(LDFLAGS) -o pnm2png-static$(E) pnm2png$(O) $(LDLIBSS) -lm
 
 clean:
 	$(RM) png2pnm$(O)
diff --git a/contrib/pngminus/makefile.tc3 b/contrib/pngminus/makefile.tc3
index 404f18d..6a2f4b9 100644
--- a/contrib/pngminus/makefile.tc3
+++ b/contrib/pngminus/makefile.tc3
@@ -7,7 +7,8 @@
 RM=del
 CP=copy
 MODEL=l
-CCFLAGS=-O -m$(MODEL) -I..\libpng -I..\zlib
+CPPFLAGS=-I..\libpng -I..\zlib
+CFLAGS=-O -m$(MODEL)
 LDFLAGS=-m$(MODEL) -L..\libpng -L..\zlib
 C=.c
 O=.obj
@@ -19,13 +20,13 @@
 all: png2pnm$(E) pnm2png$(E)
 
 png2pnm$(O): png2pnm$(C)
-        $(CC) -c $(CCFLAGS) png2pnm$(C)
+        $(CC) -c $(CPPFLAGS) $(CFLAGS) png2pnm$(C)
 
 png2pnm$(E): png2pnm$(O)
         $(LD) $(LDFLAGS) png2pnm$(O) libpng$(L) zlib$(L)
 
 pnm2png$(O): pnm2png$(C)
-        $(CC) -c $(CCFLAGS) pnm2png$(C)
+        $(CC) -c $(CPPFLAGS) $(CFLAGS) pnm2png$(C)
 
 pnm2png$(E): pnm2png$(O)
         $(LD) $(LDFLAGS) pnm2png$(O) libpng$(L) zlib$(L)
@@ -35,4 +36,3 @@
         $(RM) *$(E)
 
 # End of makefile for png2pnm / pnm2png
-
diff --git a/contrib/pngminus/png2pnm.bat b/contrib/pngminus/png2pnm.bat
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/png2pnm.c b/contrib/pngminus/png2pnm.c
index ac295aa..f68d7ff 100644
--- a/contrib/pngminus/png2pnm.c
+++ b/contrib/pngminus/png2pnm.c
@@ -18,6 +18,7 @@
 #include <mem.h>
 #include <fcntl.h>
 #endif
+#include <zlib.h>
 
 #ifndef BOOL
 #define BOOL unsigned char
@@ -51,7 +52,8 @@
 
 int  main (int argc, char *argv[]);
 void usage ();
-BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha);
+BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw,
+   BOOL alpha);
 
 /*
  *  main
@@ -84,7 +86,8 @@
           if ((fp_al = fopen (argv[argi], "wb")) == NULL)
           {
             fprintf (stderr, "PNM2PNG\n");
-            fprintf (stderr, "Error:  can not create alpha-channel file %s\n", argv[argi]);
+            fprintf (stderr, "Error:  can not create alpha-channel file %s\n",
+               argv[argi]);
             exit (1);
           }
           break;
@@ -144,7 +147,7 @@
   if (png2pnm (fp_rd, fp_wr, fp_al, raw, alpha) == FALSE)
   {
     fprintf (stderr, "PNG2PNM\n");
-    fprintf (stderr, "Error:  unsuccessful convertion of PNG-image\n");
+    fprintf (stderr, "Error:  unsuccessful conversion of PNG-image\n");
     exit(1);
   }
 
@@ -175,9 +178,11 @@
   fprintf (stderr, "Usage:  png2pnm [options] <file>.png [<file>.pnm]\n");
   fprintf (stderr, "   or:  ... | png2pnm [options]\n");
   fprintf (stderr, "Options:\n");
-  fprintf (stderr, "   -r[aw]   write pnm-file in binary format (P4/P5/P6) (default)\n");
+  fprintf (stderr,
+     "   -r[aw]   write pnm-file in binary format (P4/P5/P6) (default)\n");
   fprintf (stderr, "   -n[oraw] write pnm-file in ascii format (P1/P2/P3)\n");
-  fprintf (stderr, "   -a[lpha] <file>.pgm write PNG alpha channel as pgm-file\n");
+  fprintf (stderr,
+     "   -a[lpha] <file>.pgm write PNG alpha channel as pgm-file\n");
   fprintf (stderr, "   -h | -?  print this help-information\n");
 }
 
@@ -185,10 +190,11 @@
  *  png2pnm
  */
 
-BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha)
+BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
+    volatile BOOL raw, BOOL alpha)
 {
   png_struct    *png_ptr = NULL;
-  png_info	*info_ptr = NULL;
+  png_info        *info_ptr = NULL;
   png_byte      buf[8];
   png_byte      *png_pixels = NULL;
   png_byte      **row_pointers = NULL;
@@ -211,13 +217,13 @@
   if (ret != 8)
     return FALSE;
 
-  ret = !png_sig_cmp (buf, 0, 8);
-  if (!ret)
+  ret = png_sig_cmp (buf, 0, 8);
+  if (ret)
     return FALSE;
 
   /* create png and info structures */
 
-  png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+  png_ptr = png_create_read_struct (png_get_libpng_ver(NULL),
     NULL, NULL, NULL);
   if (!png_ptr)
     return FALSE;   /* out of memory */
@@ -260,7 +266,7 @@
     png_set_expand (png_ptr);
 
 #ifdef NJET
-  /* downgrade 16-bit images to 8 bit */
+  /* downgrade 16-bit images to 8-bit */
   if (bit_depth == 16)
     png_set_strip_16 (png_ptr);
   /* transform grayscale images into full-color */
@@ -314,12 +320,14 @@
   /* row_bytes is the width x number of channels x (bit-depth / 8) */
   row_bytes = png_get_rowbytes (png_ptr, info_ptr);
 
-  if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL) {
+  if ((png_pixels = (png_byte *)
+     malloc (row_bytes * height * sizeof (png_byte))) == NULL) {
     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
     return FALSE;
   }
 
-  if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL)
+  if ((row_pointers = (png_byte **)
+     malloc (height * sizeof (png_bytep))) == NULL)
   {
     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
     free (png_pixels);
@@ -328,7 +336,7 @@
   }
 
   /* set the individual row_pointers to point at the correct offsets */
-  for (i = 0; i < (height); i++)
+  for (i = 0; i < ((int) height); i++)
     row_pointers[i] = png_pixels + i * row_bytes;
 
   /* now we can go ahead and just read the whole image */
@@ -371,9 +379,9 @@
   /* write data to PNM file */
   pix_ptr = png_pixels;
 
-  for (row = 0; row < height; row++)
+  for (row = 0; row < (int) height; row++)
   {
-    for (col = 0; col < width; col++)
+    for (col = 0; col < (int) width; col++)
     {
       for (i = 0; i < (channels - alpha_present); i++)
       {
@@ -381,7 +389,7 @@
           fputc ((int) *pix_ptr++ , pnm_file);
         else
           if (bit_depth == 16){
-	    dep_16 = (long) *pix_ptr++;
+            dep_16 = (long) *pix_ptr++;
             fprintf (pnm_file, "%ld ", (dep_16 << 8) + ((long) *pix_ptr++));
           }
           else
@@ -401,9 +409,9 @@
             fputc ((int) *pix_ptr++ , alpha_file);
           else
             if (bit_depth == 16){
-	      dep_16 = (long) *pix_ptr++;
+              dep_16 = (long) *pix_ptr++;
               fprintf (alpha_file, "%ld ", (dep_16 << 8) + (long) *pix_ptr++);
-	    }  
+            }
             else
               fprintf (alpha_file, "%ld ", (long) *pix_ptr++);
         }
diff --git a/contrib/pngminus/png2pnm.sh b/contrib/pngminus/png2pnm.sh
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pngminus.bat b/contrib/pngminus/pngminus.bat
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pngminus.sh b/contrib/pngminus/pngminus.sh
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pnm2png.bat b/contrib/pngminus/pnm2png.bat
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pnm2png.c b/contrib/pngminus/pnm2png.c
index 4cdfad8..8fa64cd 100644
--- a/contrib/pngminus/pnm2png.c
+++ b/contrib/pngminus/pnm2png.c
@@ -3,6 +3,7 @@
  *  copyright (C) 1999 by Willem van Schaik <willem@schaik.com>
  *
  *  version 1.0 - 1999.10.15 - First version.
+ *  version 1.1 - 2015.07.29 - Fixed leaks (Glenn Randers-Pehrson)
  *
  *  Permission to use, copy, modify, and distribute this software and
  *  its documentation for any purpose and without fee is hereby granted,
@@ -18,6 +19,7 @@
 #include <mem.h>
 #include <fcntl.h>
 #endif
+#include <zlib.h>
 
 #ifndef BOOL
 #define BOOL unsigned char
@@ -49,7 +51,8 @@
 
 int  main (int argc, char *argv[]);
 void usage ();
-BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, BOOL alpha);
+BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace,
+    BOOL alpha);
 void get_token(FILE *pnm_file, char *token);
 png_uint_32 get_data (FILE *pnm_file, int depth);
 png_uint_32 get_value (FILE *pnm_file, int depth);
@@ -175,7 +178,8 @@
   fprintf (stderr, "   or:  ... | pnm2png [options]\n");
   fprintf (stderr, "Options:\n");
   fprintf (stderr, "   -i[nterlace]   write png-file with interlacing on\n");
-  fprintf (stderr, "   -a[lpha] <file>.pgm read PNG alpha channel as pgm-file\n");
+  fprintf (stderr,
+      "   -a[lpha] <file>.pgm read PNG alpha channel as pgm-file\n");
   fprintf (stderr, "   -h | -?  print this help-information\n");
 }
 
@@ -183,29 +187,36 @@
  *  pnm2png
  */
 
-BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, BOOL alpha)
+BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace,
+     BOOL alpha)
 {
   png_struct    *png_ptr = NULL;
   png_info      *info_ptr = NULL;
   png_byte      *png_pixels = NULL;
   png_byte      **row_pointers = NULL;
   png_byte      *pix_ptr = NULL;
-  png_uint_32   row_bytes;
+  volatile png_uint_32   row_bytes;
 
   char          type_token[16];
   char          width_token[16];
   char          height_token[16];
   char          maxval_token[16];
-  int           color_type;
-  png_uint_32   width, alpha_width;
-  png_uint_32   height, alpha_height;
+  volatile int    color_type=1;
+  unsigned long   ul_width=0, ul_alpha_width=0;
+  unsigned long   ul_height=0, ul_alpha_height=0;
+  unsigned long   ul_maxval=0;
+  volatile png_uint_32   width=0, height=0;
+  volatile png_uint_32   alpha_width=0, alpha_height=0;
   png_uint_32   maxval;
-  int           bit_depth = 0;
-  int           channels;
+  volatile int           bit_depth = 0;
+  int           channels=0;
   int           alpha_depth = 0;
-  int           alpha_present;
+  int           alpha_present=0;
   int           row, col;
   BOOL          raw, alpha_raw = FALSE;
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+  BOOL          packed_bitmap = FALSE;
+#endif
   png_uint_32   tmp16;
   int           i;
 
@@ -218,20 +229,36 @@
   }
   else if ((type_token[1] == '1') || (type_token[1] == '4'))
   {
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
     raw = (type_token[1] == '4');
     color_type = PNG_COLOR_TYPE_GRAY;
+    get_token(pnm_file, width_token);
+    sscanf (width_token, "%lu", &ul_width);
+    width = (png_uint_32) ul_width;
+    get_token(pnm_file, height_token);
+    sscanf (height_token, "%lu", &ul_height);
+    height = (png_uint_32) ul_height;
     bit_depth = 1;
+    packed_bitmap = TRUE;
+#else
+    fprintf (stderr, "PNM2PNG built without PNG_WRITE_INVERT_SUPPORTED and \n");
+    fprintf (stderr, "PNG_WRITE_PACK_SUPPORTED can't read PBM (P1,P4) files\n");
+#endif
   }
   else if ((type_token[1] == '2') || (type_token[1] == '5'))
   {
     raw = (type_token[1] == '5');
     color_type = PNG_COLOR_TYPE_GRAY;
     get_token(pnm_file, width_token);
-    sscanf (width_token, "%lu", &width);
+    sscanf (width_token, "%lu", &ul_width);
+    width = (png_uint_32) ul_width;
     get_token(pnm_file, height_token);
-    sscanf (height_token, "%lu", &height);
+    sscanf (height_token, "%lu", &ul_height);
+    height = (png_uint_32) ul_height;
     get_token(pnm_file, maxval_token);
-    sscanf (maxval_token, "%lu", &maxval);
+    sscanf (maxval_token, "%lu", &ul_maxval);
+    maxval = (png_uint_32) ul_maxval;
+
     if (maxval <= 1)
       bit_depth = 1;
     else if (maxval <= 3)
@@ -248,11 +275,14 @@
     raw = (type_token[1] == '6');
     color_type = PNG_COLOR_TYPE_RGB;
     get_token(pnm_file, width_token);
-    sscanf (width_token, "%lu", &width);
+    sscanf (width_token, "%lu", &ul_width);
+    width = (png_uint_32) ul_width;
     get_token(pnm_file, height_token);
-    sscanf (height_token, "%lu", &height);
+    sscanf (height_token, "%lu", &ul_height);
+    height = (png_uint_32) ul_height;
     get_token(pnm_file, maxval_token);
-    sscanf (maxval_token, "%lu", &maxval);
+    sscanf (maxval_token, "%lu", &ul_maxval);
+    maxval = (png_uint_32) ul_maxval;
     if (maxval <= 1)
       bit_depth = 1;
     else if (maxval <= 3)
@@ -287,15 +317,18 @@
     {
       alpha_raw = (type_token[1] == '5');
       get_token(alpha_file, width_token);
-      sscanf (width_token, "%lu", &alpha_width);
+      sscanf (width_token, "%lu", &ul_alpha_width);
+      alpha_width=(png_uint_32) ul_alpha_width;
       if (alpha_width != width)
         return FALSE;
       get_token(alpha_file, height_token);
-      sscanf (height_token, "%lu", &alpha_height);
+      sscanf (height_token, "%lu", &ul_alpha_height);
+      alpha_height = (png_uint_32) ul_alpha_height;
       if (alpha_height != height)
         return FALSE;
       get_token(alpha_file, maxval_token);
-      sscanf (maxval_token, "%lu", &maxval);
+      sscanf (maxval_token, "%lu", &ul_maxval);
+      maxval = (png_uint_32) ul_maxval;
       if (maxval <= 1)
         alpha_depth = 1;
       else if (maxval <= 3)
@@ -324,76 +357,108 @@
     channels = 3;
   else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
     channels = 4;
+#if 0
   else
-    channels = 0; /* should not happen */
+    channels = 0; /* cannot happen */
+#endif
 
   alpha_present = (channels - 1) % 2;
 
-  /* row_bytes is the width x number of channels x (bit-depth / 8) */
-  row_bytes = width * channels * ((bit_depth <= 8) ? 1 : 2);
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+  if (packed_bitmap)
+    /* row data is as many bytes as can fit width x channels x bit_depth */
+    row_bytes = (width * channels * bit_depth + 7) / 8;
+  else
+#endif
+    /* row_bytes is the width x number of channels x (bit-depth / 8) */
+    row_bytes = width * channels * ((bit_depth <= 8) ? 1 : 2);
 
-  if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL)
+  if ((png_pixels = (png_byte *)
+     malloc (row_bytes * height * sizeof (png_byte))) == NULL)
     return FALSE;
 
   /* read data from PNM file */
   pix_ptr = png_pixels;
 
-  for (row = 0; row < height; row++)
+  for (row = 0; row < (int) height; row++)
   {
-    for (col = 0; col < width; col++)
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+    if (packed_bitmap) {
+      for (i = 0; i < (int) row_bytes; i++)
+        /* png supports this format natively so no conversion is needed */
+        *pix_ptr++ = get_data (pnm_file, 8);
+    } else
+#endif
     {
-      for (i = 0; i < (channels - alpha_present); i++)
+      for (col = 0; col < (int) width; col++)
       {
-        if (raw)
-          *pix_ptr++ = get_data (pnm_file, bit_depth);
-        else
-          if (bit_depth <= 8)
-            *pix_ptr++ = get_value (pnm_file, bit_depth);
+        for (i = 0; i < (channels - alpha_present); i++)
+        {
+          if (raw)
+            *pix_ptr++ = get_data (pnm_file, bit_depth);
           else
-          {
-            tmp16 = get_value (pnm_file, bit_depth);
-            *pix_ptr = (png_byte) ((tmp16 >> 8) & 0xFF);
-            pix_ptr++;
-            *pix_ptr = (png_byte) (tmp16 & 0xFF);
-            pix_ptr++;
-          }
-      }
+            if (bit_depth <= 8)
+              *pix_ptr++ = get_value (pnm_file, bit_depth);
+            else
+            {
+              tmp16 = get_value (pnm_file, bit_depth);
+              *pix_ptr = (png_byte) ((tmp16 >> 8) & 0xFF);
+              pix_ptr++;
+              *pix_ptr = (png_byte) (tmp16 & 0xFF);
+              pix_ptr++;
+            }
+        }
 
-      if (alpha) /* read alpha-channel from pgm file */
-      {
-        if (alpha_raw)
-          *pix_ptr++ = get_data (alpha_file, alpha_depth);
-        else
-          if (alpha_depth <= 8)
-            *pix_ptr++ = get_value (alpha_file, bit_depth);
+        if (alpha) /* read alpha-channel from pgm file */
+        {
+          if (alpha_raw)
+            *pix_ptr++ = get_data (alpha_file, alpha_depth);
           else
-          {
-            tmp16 = get_value (alpha_file, bit_depth);
-            *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF);
-            *pix_ptr++ = (png_byte) (tmp16 & 0xFF);
-          }
-      } /* if alpha */
-
+            if (alpha_depth <= 8)
+              *pix_ptr++ = get_value (alpha_file, bit_depth);
+            else
+            {
+              tmp16 = get_value (alpha_file, bit_depth);
+              *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF);
+              *pix_ptr++ = (png_byte) (tmp16 & 0xFF);
+            }
+        } /* if alpha */
+      } /* if packed_bitmap */
     } /* end for col */
   } /* end for row */
 
   /* prepare the standard PNG structures */
-  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  png_ptr = png_create_write_struct (png_get_libpng_ver(NULL), NULL, NULL,
+      NULL);
   if (!png_ptr)
   {
+    free (png_pixels);
+    png_pixels = NULL;
     return FALSE;
   }
   info_ptr = png_create_info_struct (png_ptr);
   if (!info_ptr)
   {
     png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+    free (png_pixels);
+    png_pixels = NULL;
     return FALSE;
   }
 
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+  if (packed_bitmap == TRUE)
+  {
+    png_set_packing (png_ptr);
+    png_set_invert_mono (png_ptr);
+  }
+#endif
+
   /* setjmp() must be called in every function that calls a PNG-reading libpng function */
   if (setjmp (png_jmpbuf(png_ptr)))
   {
-    png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    free (png_pixels);
+    png_pixels = NULL;
     return FALSE;
   }
 
@@ -411,25 +476,28 @@
   /* if needed we will allocate memory for an new array of row-pointers */
   if (row_pointers == (unsigned char**) NULL)
   {
-    if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL)
+    if ((row_pointers = (png_byte **)
+        malloc (height * sizeof (png_bytep))) == NULL)
     {
-      png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+      png_destroy_write_struct (&png_ptr, &info_ptr);
+      free (png_pixels);
+      png_pixels = NULL;
       return FALSE;
     }
   }
 
   /* set the individual row_pointers to point at the correct offsets */
-  for (i = 0; i < (height); i++)
+  for (i = 0; i < (int) height; i++)
     row_pointers[i] = png_pixels + i * row_bytes;
 
   /* write out the entire image data in one call */
   png_write_image (png_ptr, row_pointers);
 
-  /* write the additional chuncks to the PNG file (not really needed) */
+  /* write the additional chunks to the PNG file (not really needed) */
   png_write_end (png_ptr, info_ptr);
 
   /* clean up after the write, and free any memory allocated */
-  png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+  png_destroy_write_struct (&png_ptr, &info_ptr);
 
   if (row_pointers != (unsigned char**) NULL)
     free (row_pointers);
@@ -446,19 +514,32 @@
 void get_token(FILE *pnm_file, char *token)
 {
   int i = 0;
+  int ret;
 
-  /* remove white-space */
+  /* remove white-space and comment lines */
   do
   {
-    token[i] = (unsigned char) fgetc (pnm_file);
+    ret = fgetc(pnm_file);
+    if (ret == '#') {
+      /* the rest of this line is a comment */
+      do
+      {
+        ret = fgetc(pnm_file);
+      }
+      while ((ret != '\n') && (ret != '\r') && (ret != EOF));
+    }
+    if (ret == EOF) break;
+    token[i] = (unsigned char) ret;
   }
   while ((token[i] == '\n') || (token[i] == '\r') || (token[i] == ' '));
 
   /* read string */
   do
   {
+    ret = fgetc(pnm_file);
+    if (ret == EOF) break;
     i++;
-    token[i] = (unsigned char) fgetc (pnm_file);
+    token[i] = (unsigned char) ret;
   }
   while ((token[i] != '\n') && (token[i] != '\r') && (token[i] != ' '));
 
@@ -510,6 +591,7 @@
 {
   static png_uint_32 mask = 0;
   png_byte token[16];
+  unsigned long ul_ret_value;
   png_uint_32 ret_value;
   int i = 0;
 
@@ -518,7 +600,8 @@
       mask = (mask << 1) | 0x01;
 
   get_token (pnm_file, (char *) token);
-  sscanf ((const char *) token, "%lu", &ret_value);
+  sscanf ((const char *) token, "%lu", &ul_ret_value);
+  ret_value = (png_uint_32) ul_ret_value;
 
   ret_value &= mask;
 
diff --git a/contrib/pngminus/pnm2png.sh b/contrib/pngminus/pnm2png.sh
old mode 100644
new mode 100755
diff --git a/contrib/pngsuite/README b/contrib/pngsuite/README
index 714d12c..a7fde8f 100644
--- a/contrib/pngsuite/README
+++ b/contrib/pngsuite/README
@@ -1,41 +1,61 @@
 
 pngsuite
 --------
-(c) Willem van Schaik, 1999
+Copyright (c) Willem van Schaik, 1999, 2011, 2012
+Two images (ftbbn0g01.png and ftbbn0g02.png) are by Glenn Randers-Pehrson, 2012
 
-Permission to use, copy, and distribute these images for any purpose and
-without fee is hereby granted.
+Permission to use, copy, modify, and distribute these images for any
+purpose and without fee is hereby granted.
 
-These 15 images are part of the much larger PngSuite test-set of 
-images, available for developers of PNG supporting software. The 
-complete set, available at http:/www.schaik.com/pngsuite/, contains 
+The 15 "bas*.png" images are part of the much larger PngSuite test-set of
+images, available for developers of PNG supporting software. The
+complete set, available at http:/www.schaik.com/pngsuite/, contains
 a variety of images to test interlacing, gamma settings, ancillary
 chunks, etc.
 
+The "ft*.png" images are "free/libre" replacements for the transparent
+corresponding t*.png images in the PngSuite.
+
 The images in this directory represent the basic PNG color-types:
 grayscale (1-16 bit deep), full color (8 or 16 bit), paletted
 (1-8 bit) and grayscale or color images with alpha channel. You
 can use them to test the proper functioning of PNG software.
 
-    filename      depth type
+    filename       depth type
     ------------ ------ --------------
-    basn0g01.png  1-bit grayscale
-    basn0g02.png  2-bit grayscale
-    basn0g04.png  4-bit grayscale
-    basn0g08.png  8-bit grayscale
-    basn0g16.png 16-bit grayscale
-    basn2c08.png  8-bit truecolor
-    basn2c16.png 16-bit truecolor
-    basn3p01.png  1-bit paletted
-    basn3p02.png  2-bit paletted
-    basn3p04.png  4-bit paletted
-    basn3p08.png  8-bit paletted
-    basn4a08.png  8-bit gray with alpha
-    basn4a16.png 16-bit gray with alpha
-    basn6a08.png  8-bit RGBA
-    basn6a16.png 16-bit RGBA
+    basn0g01.png   1-bit grayscale
+    basn0g02.png   2-bit grayscale
+    basn0g04.png   4-bit grayscale
+    basn0g08.png   8-bit grayscale
+    basn0g16.png  16-bit grayscale
+    basn2c08.png   8-bit truecolor
+    basn2c16.png  16-bit truecolor
+    basn3p01.png   1-bit paletted
+    basn3p02.png   2-bit paletted
+    basn3p04.png   4-bit paletted
+    basn3p08.png   8-bit paletted
+    basn4a08.png   8-bit gray with alpha
+    basn4a16.png  16-bit gray with alpha
+    basn6a08.png   8-bit RGBA
+    basn6a16.png  16-bit RGBA
 
-Here is the correct result of typing "pngtest -m *.png" in
+    ftbbn0g01.png  1-bit grayscale, black bKGD
+    ftbbn0g02.png  2-bit grayscale, black bKGD
+    ftbbn0g04.png  4-bit grayscale, black bKGD
+    ftbbn2c16.png 16-bit truecolor, black bKGD
+    ftbbn3p08.png  8-bit paletted, black bKGD
+    ftbgn2c16.png 16-bit truecolor, gray bKGD
+    ftbgn3p08.png  8-bit paletted, gray bKGD
+    ftbrn2c08.png  8-bit truecolor, red bKGD
+    ftbwn0g16.png 16-bit gray, white bKGD
+    ftbwn3p08.png  8-bit paletted, white bKGD
+    ftbyn3p08.png  8-bit paletted, yellow bKGD
+    ftp0n0g08.png  8-bit grayscale, opaque
+    ftp0n2c08.png  8-bit truecolor, opaque
+    ftp0n3p08.png  8-bit paletted, opaque
+    ftp1n3p08.png  8-bit paletted, no bKGD
+
+Here is the correct result of typing "pngtest -m bas*.png" in
 this directory:
 
 Testing basn0g01.png: PASS (524 zero samples)
diff --git a/contrib/pngsuite/ftbbn0g01.png b/contrib/pngsuite/ftbbn0g01.png
new file mode 100644
index 0000000..ba746ff
--- /dev/null
+++ b/contrib/pngsuite/ftbbn0g01.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn0g02.png b/contrib/pngsuite/ftbbn0g02.png
new file mode 100644
index 0000000..3d83bd6
--- /dev/null
+++ b/contrib/pngsuite/ftbbn0g02.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn0g04.png b/contrib/pngsuite/ftbbn0g04.png
new file mode 100644
index 0000000..39a7050
--- /dev/null
+++ b/contrib/pngsuite/ftbbn0g04.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn2c16.png b/contrib/pngsuite/ftbbn2c16.png
new file mode 100644
index 0000000..dd3168e
--- /dev/null
+++ b/contrib/pngsuite/ftbbn2c16.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn3p08.png b/contrib/pngsuite/ftbbn3p08.png
new file mode 100644
index 0000000..0ede357
--- /dev/null
+++ b/contrib/pngsuite/ftbbn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbgn2c16.png b/contrib/pngsuite/ftbgn2c16.png
new file mode 100644
index 0000000..85cec39
--- /dev/null
+++ b/contrib/pngsuite/ftbgn2c16.png
Binary files differ
diff --git a/contrib/pngsuite/ftbgn3p08.png b/contrib/pngsuite/ftbgn3p08.png
new file mode 100644
index 0000000..8cf2e6f
--- /dev/null
+++ b/contrib/pngsuite/ftbgn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbrn2c08.png b/contrib/pngsuite/ftbrn2c08.png
new file mode 100644
index 0000000..5cca0d6
--- /dev/null
+++ b/contrib/pngsuite/ftbrn2c08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbwn0g16.png b/contrib/pngsuite/ftbwn0g16.png
new file mode 100644
index 0000000..99bdeed
--- /dev/null
+++ b/contrib/pngsuite/ftbwn0g16.png
Binary files differ
diff --git a/contrib/pngsuite/ftbwn3p08.png b/contrib/pngsuite/ftbwn3p08.png
new file mode 100644
index 0000000..eacab7a
--- /dev/null
+++ b/contrib/pngsuite/ftbwn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbyn3p08.png b/contrib/pngsuite/ftbyn3p08.png
new file mode 100644
index 0000000..656db09
--- /dev/null
+++ b/contrib/pngsuite/ftbyn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp0n0g08.png b/contrib/pngsuite/ftp0n0g08.png
new file mode 100644
index 0000000..333465f
--- /dev/null
+++ b/contrib/pngsuite/ftp0n0g08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp0n2c08.png b/contrib/pngsuite/ftp0n2c08.png
new file mode 100644
index 0000000..fc6e42c
--- /dev/null
+++ b/contrib/pngsuite/ftp0n2c08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp0n3p08.png b/contrib/pngsuite/ftp0n3p08.png
new file mode 100644
index 0000000..69a69e5
--- /dev/null
+++ b/contrib/pngsuite/ftp0n3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp1n3p08.png b/contrib/pngsuite/ftp1n3p08.png
new file mode 100644
index 0000000..a6c9f35
--- /dev/null
+++ b/contrib/pngsuite/ftp1n3p08.png
Binary files differ
diff --git a/contrib/tools/README.txt b/contrib/tools/README.txt
new file mode 100644
index 0000000..5ddae02
--- /dev/null
+++ b/contrib/tools/README.txt
@@ -0,0 +1,26 @@
+This directory (contrib/tools) contains tools used by the authors of libpng.
+
+Code and data placed in this directory is not required to build libpng,
+however the code in this directory has been used to generate data or code in
+the body of the libpng source.  The source code identifies where this has
+been done.  Code in this directory may not compile on all operating systems
+that libpng supports.
+
+NO COPYRIGHT RIGHTS ARE CLAIMED TO ANY OF THE FILES IN THIS DIRECTORY.
+
+To the extent possible under law, the authors have waived all copyright and
+related or neighboring rights to this work.  This work is published from:
+United States.
+
+The files may be used freely in any way.
+
+The source code and comments in this directory are the original work of the
+people named below.  No other person or organization has made contributions to
+the work in this directory.
+
+ORIGINAL AUTHORS
+    The following people have contributed to the code in this directory.  None
+    of the people below claim any rights with regard to the contents of this
+    directory.
+
+    John Bowler <jbowler@acm.org>
diff --git a/contrib/tools/checksum-icc.c b/contrib/tools/checksum-icc.c
new file mode 100644
index 0000000..581e708
--- /dev/null
+++ b/contrib/tools/checksum-icc.c
@@ -0,0 +1,102 @@
+/* checksum-icc.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Generate crc32 and adler32 checksums of the given input files, used to
+ * generate check-codes for use when matching ICC profiles within libpng.
+ */
+#include <stdio.h>
+
+#include <zlib.h>
+
+static int
+read_one_file(FILE *ip, const char *name)
+{
+   uLong length = 0;
+   uLong a32 = adler32(0, NULL, 0);
+   uLong c32 = crc32(0, NULL, 0);
+   Byte header[132];
+
+   for (;;)
+   {
+      int ch = getc(ip);
+      Byte b;
+
+      if (ch == EOF) break;
+
+      b = (Byte)ch;
+
+      if (length < sizeof header)
+         header[length] = b;
+
+      ++length;
+      a32 = adler32(a32, &b, 1);
+      c32 = crc32(c32, &b, 1);
+   }
+
+   if (ferror(ip))
+      return 0;
+
+   /* Success */
+   printf("PNG_ICC_CHECKSUM(0x%8.8lx, 0x%8.8lx,\n   PNG_MD5("
+      "0x%2.2x%2.2x%2.2x%2.2x, 0x%2.2x%2.2x%2.2x%2.2x, 0x%2.2x%2.2x%2.2x%2.2x,"
+      " 0x%2.2x%2.2x%2.2x%2.2x), %d,\n"
+      "   \"%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d\", %lu, \"%s\")\n",
+      (unsigned long)a32, (unsigned long)c32,
+      header[84], header[85], header[86], header[87],
+      header[88], header[89], header[90], header[91],
+      header[92], header[93], header[94], header[95],
+      header[96], header[97], header[98], header[99],
+#     define u16(x) (header[x] * 256 + header[x+1])
+#     define u32(x) (u16(x) * 65536 + u16(x+2))
+      u32(64), u16(24), u16(26), u16(28), u16(30), u16(32), u16(34),
+      (unsigned long)length, name);
+
+   return 1;
+}
+
+int main(int argc, char **argv)
+{
+   int err = 0;
+
+   printf("/* adler32, crc32, MD5[16], intent, date, length, file-name */\n");
+
+   if (argc > 1)
+   {
+      int i;
+
+      for (i=1; i<argc; ++i)
+      {
+         FILE *ip = fopen(argv[i], "rb");
+
+         if (ip == NULL || !read_one_file(ip, argv[i]))
+         {
+            err = 1;
+            perror(argv[i]);
+            fprintf(stderr, "%s: read error\n", argv[i]);
+            printf("/* ERROR: %s */\n", argv[i]);
+         }
+
+         (void)fclose(ip);
+      }
+   }
+
+   else
+   {
+      if (!read_one_file(stdin, "-"))
+      {
+         err = 1;
+         perror("stdin");
+         fprintf(stderr, "stdin: read error\n");
+         printf("/* ERROR: stdin */\n");
+      }
+   }
+
+   return err;
+}
diff --git a/contrib/tools/chkfmt b/contrib/tools/chkfmt
new file mode 100755
index 0000000..9da6475
--- /dev/null
+++ b/contrib/tools/chkfmt
@@ -0,0 +1,137 @@
+#!/bin/sh
+#
+# Check the format of the source files in the current directory - checks for a
+# line length of 80 characters max and no tab characters.
+#
+# Optionally arguments are files or directories to check.
+#
+# -v: output the long lines (makes fixing them easier)
+# -e: spawn an editor for each file that needs a change ($EDITOR must be
+#     defined).  When using -e the script MUST be run from an interactive
+#     command line.
+verbose=
+edit=
+vers=
+test "$1" = "-v" && {
+   shift
+   verbose=yes
+}
+test "$1" = "-e" && {
+   shift
+   if test -n "$EDITOR"
+   then
+      edit=yes
+
+      # Copy the standard streams for the editor
+      exec 3>&0 4>&1 5>&2
+   else
+      echo "chkfmt -e: EDITOR must be defined" >&2
+      exit 1
+   fi
+}
+
+# Function to edit a single file - if the file isn't changed ask the user
+# whether or not to continue.  This stuff only works if the script is run from
+# the command line (otherwise, don't specify -e or you will be sorry).
+doed(){
+   cp "$file" "$file".orig
+   "$EDITOR" "$file" 0>&3 1>&4 2>&5 3>&- 4>&- 5>&- || exit 1
+   if cmp -s "$file".orig "$file"
+   then
+      rm "$file".orig
+      echo -n "$file: file not changed, type anything to continue: " >&5
+      read ans 0>&3
+      test -n "$ans" || return 1
+   fi
+   return 0
+}
+
+# In beta versions the version string which appears in files can be a little
+# long and cause spuriously overlong lines.  To avoid this subtitute the version
+# string with a 'standard' version a.b.cc before checking for long lines.
+if test -r png.h
+then
+   vers="`sed -n -e \
+   's/^#define PNG_LIBPNG_VER_STRING .\([0-9]\.[0-9]\.[0-9][0-9a-z]*\).$/\1/p' \
+   png.h`"
+   echo "chkfmt: checking version $vers"
+fi
+if test -z "$vers"
+then
+   echo "chkfmt: png.h not found, ignoring version number" >&2
+fi
+
+test -n "$1" || set -- .
+find "$@" \( -type d \( -name '.git' -o -name '.libs' -o -name 'projects' \) \
+   -prune \) -o \( -type f \
+   ! -name '*.[oa]' ! -name '*.l[oa]' !  -name '*.png' ! -name '*.out' \
+   ! -name '*.jpg' ! -name '*.patch' ! -name '*.obj' ! -name '*.exe' \
+   ! -name '*.com' ! -name '*.tar.*' ! -name '*.zip' ! -name '*.ico' \
+   ! -name '*.res' ! -name '*.rc' ! -name '*.mms' ! -name '*.rej' \
+   ! -name '*.dsp' ! -name '*.orig' ! -name '*.dfn' ! -name '*.swp' \
+   ! -name '~*' ! -name '*.3' \
+   ! -name 'missing' ! -name 'mkinstalldirs' ! -name 'depcomp' \
+   ! -name 'aclocal.m4' ! -name 'install-sh' ! -name 'Makefile.in' \
+   ! -name 'ltmain.sh' ! -name 'config*' -print \) | {
+   st=0
+   while read file
+   do
+      case "$file" in
+      *.mak|*[Mm]akefile.*|*[Mm]akefile)
+         # Makefiles require tabs, dependency lines can be this long.
+         check_tabs=
+         line_length=100;;
+      *.awk)
+         # Includes literal tabs
+         check_tabs=
+         # The following is arbitrary
+         line_length=132;;
+      *contrib/*/*.[ch])
+         check_tabs=yes
+         line_length=96;;
+      *)
+         check_tabs=yes
+         line_length=80;;
+      esac
+
+      # Note that vers can only contain 0-9, . and a-z
+      if test -n "$vers"
+      then
+         sed -e "s/$vers/a.b.cc/g" "$file" >"$file".$$
+      else
+         cp "$file" "$file".$$
+      fi
+      splt="`fold -$line_length "$file".$$ | diff -c "$file".$$ -`"
+      rm "$file".$$
+
+      if test -n "$splt"
+      then
+         echo "$file: lines too long"
+         st=1
+         if test -n "$EDITOR" -a -n "$edit"
+         then
+            doed "$file" || exit 1
+         elif test -n "$verbose"
+         then
+            echo "$splt"
+         fi
+      fi
+      if test -n "$check_tabs"
+      then
+         tab="`tr -c -d '\t' <"$file"`"
+         if test -n "$tab"
+         then
+            echo "$file: file contains tab characters"
+            st=1
+            if test -n "$EDITOR" -a -n "$edit"
+            then
+               doed "$file" || exit 1
+            elif test -n "$verbose"
+            then
+               echo "$splt"
+            fi
+         fi
+      fi
+   done
+   exit $st
+}
diff --git a/contrib/tools/cvtcolor.c b/contrib/tools/cvtcolor.c
new file mode 100644
index 0000000..e6793c7
--- /dev/null
+++ b/contrib/tools/cvtcolor.c
@@ -0,0 +1,188 @@
+/*-
+ * convert.c
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2013.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Convert 8-bit sRGB or 16-bit linear values to another format.
+ */
+#define _ISOC99_SOURCE 1
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <fenv.h>
+
+#include "sRGB.h"
+
+static void
+usage(const char *prog)
+{
+   fprintf(stderr,
+      "%s: usage: %s [-linear|-sRGB] [-gray|-color] component{1,4}\n",
+      prog, prog);
+   exit(1);
+}
+
+unsigned long
+component(const char *prog, const char *arg, int issRGB)
+{
+   char *ep;
+   unsigned long c = strtoul(arg, &ep, 0);
+
+   if (ep <= arg || *ep || c > 65535 || (issRGB && c > 255))
+   {
+      fprintf(stderr, "%s: %s: invalid component value (%lu)\n", prog, arg, c);
+      usage(prog);
+   }
+
+   return c;
+}
+
+int
+main(int argc, const char **argv)
+{
+   const char *prog = *argv++;
+   int to_linear = 0, to_gray = 0, to_color = 0;
+   int channels = 0;
+   double c[4];
+
+   /* FE_TONEAREST is the IEEE754 round to nearest, preferring even, mode; i.e.
+    * everything rounds to the nearest value except that '.5' rounds to the
+    * nearest even value.
+    */
+   fesetround(FE_TONEAREST);
+
+   c[3] = c[2] = c[1] = c[0] = 0;
+
+   while (--argc > 0 && **argv == '-')
+   {
+      const char *arg = 1+*argv++;
+
+      if (strcmp(arg, "sRGB") == 0)
+         to_linear = 0;
+
+      else if (strcmp(arg, "linear") == 0)
+         to_linear = 1;
+
+      else if (strcmp(arg, "gray") == 0)
+         to_gray = 1, to_color = 0;
+
+      else if (strcmp(arg, "color") == 0)
+         to_gray = 0, to_color = 1;
+
+      else
+         usage(prog);
+   }
+
+   switch (argc)
+   {
+      default:
+         usage(prog);
+         break;
+
+      case 4:
+         c[3] = component(prog, argv[3], to_linear);
+         ++channels;
+      case 3:
+         c[2] = component(prog, argv[2], to_linear);
+         ++channels;
+      case 2:
+         c[1] = component(prog, argv[1], to_linear);
+         ++channels;
+      case 1:
+         c[0] = component(prog, argv[0], to_linear);
+         ++channels;
+         break;
+      }
+
+   if (to_linear)
+   {
+      int i;
+      int components = channels;
+
+      if ((components & 1) == 0)
+         --components;
+
+      for (i=0; i<components; ++i) c[i] = linear_from_sRGB(c[i] / 255);
+      if (components < channels)
+         c[components] = c[components] / 255;
+   }
+
+   else
+   {
+      int i;
+      for (i=0; i<4; ++i) c[i] /= 65535;
+
+      if ((channels & 1) == 0)
+      {
+         double alpha = c[channels-1];
+
+         if (alpha > 0)
+            for (i=0; i<channels-1; ++i) c[i] /= alpha;
+         else
+            for (i=0; i<channels-1; ++i) c[i] = 1;
+      }
+   }
+
+   if (to_gray)
+   {
+      if (channels < 3)
+      {
+         fprintf(stderr, "%s: too few channels (%d) for -gray\n",
+            prog, channels);
+         usage(prog);
+      }
+
+      c[0] = YfromRGB(c[0], c[1], c[2]);
+      channels -= 2;
+   }
+
+   if (to_color)
+   {
+      if (channels > 2)
+      {
+         fprintf(stderr, "%s: too many channels (%d) for -color\n",
+            prog, channels);
+         usage(prog);
+      }
+
+      c[3] = c[1]; /* alpha, if present */
+      c[2] = c[1] = c[0];
+   }
+
+   if (to_linear)
+   {
+      int i;
+      if ((channels & 1) == 0)
+      {
+         double alpha = c[channels-1];
+         for (i=0; i<channels-1; ++i) c[i] *= alpha;
+      }
+
+      for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 65535);
+   }
+
+   else /* to sRGB */
+   {
+      int i = (channels+1)&~1;
+      while (--i >= 0)
+         c[i] = sRGB_from_linear(c[i]);
+
+      for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 255);
+   }
+
+   {
+      int i;
+      for (i=0; i<channels; ++i) printf(" %g", c[i]);
+   }
+   printf("\n");
+
+   return 0;
+}
diff --git a/contrib/tools/genpng.c b/contrib/tools/genpng.c
new file mode 100644
index 0000000..ce43260
--- /dev/null
+++ b/contrib/tools/genpng.c
@@ -0,0 +1,867 @@
+/*- genpng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2015.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Generate a PNG with an alpha channel, correctly.
+ *
+ * This is a test case generator; the resultant PNG files are only of interest
+ * to those of us who care about whether the edges of circles are green, red,
+ * or yellow.
+ *
+ * The program generates an RGB+Alpha PNG of a given size containing the given
+ * shapes on a transparent background:
+ *
+ *  genpng width height { shape }
+ *    shape ::= color width shape x1 y1 x2 y2
+ *
+ * 'color' is:
+ *
+ *  black white red green yellow blue brown purple pink orange gray cyan
+ *
+ * The point is to have colors that are linguistically meaningful plus that old
+ * bugbear of the department store dress murders, Cyan, the only color we argue
+ * about.
+ *
+ * 'shape' is:
+ *
+ *  circle: an ellipse
+ *  square: a rectangle
+ *  line: a straight line
+ *
+ * Each shape is followed by four numbers, these are two points in the output
+ * coordinate space (as real numbers) which describe the circle, square, or
+ * line.  The shape is filled if it is preceded by 'filled' (not valid for
+ * 'line') or is drawn with a line, in which case the width of the line must
+ * precede the shape.
+ *
+ * The whole set of information can be repeated as many times as desired:
+ *
+ *    shape ::= color width shape x1 y1 x2 y2
+ *
+ *    color ::= black|white|red|green|yellow|blue
+ *    color ::= brown|purple|pink|orange|gray|cyan
+ *    width ::= filled
+ *    width ::= <number>
+ *    shape ::= circle|square|line
+ *    x1    ::= <number>
+ *    x2    ::= <number>
+ *    y1    ::= <number>
+ *    y2    ::= <number>
+ *
+ * The output PNG is generated by down-sampling a 4x supersampled image using
+ * a bi-cubic filter.  The bi-cubic has a 2 (output) pixel width, so an 8x8
+ * array of super-sampled points contribute to each output pixel.  The value of
+ * a super-sampled point is found using an unfiltered, aliased, infinite
+ * precision image: Each shape from the last to the first is checked to see if
+ * the point is in the drawn area and, if it is, the color of the point is the
+ * color of the shape and the alpha is 1, if not the previous shape is checked.
+ *
+ * This is an aliased algorithm because no filtering is done; a point is either
+ * inside or outside each shape and 'close' points do not contribute to the
+ * sample.  The down-sampling is relied on to correct the error of not using
+ * a filter.
+ *
+ * The line end-caps are 'flat'; they go through the points.  The square line
+ * joins are mitres; the outside of the lines are continued to the point of
+ * intersection.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+
+#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
+
+static const struct color
+{
+   const char *name;
+   double      red;
+   double      green;
+   double      blue;
+} colors[] =
+/* color ::= black|white|red|green|yellow|blue
+ * color ::= brown|purple|pink|orange|gray|cyan
+ */
+{
+   { "black",   0,    0,  0 },
+   { "white",   1,    1,  1 },
+   { "red",     1,    0,  0 },
+   { "green",   0,    1,  0 },
+   { "yellow",  1,    1,  0 },
+   { "blue",    0,    0,  1 },
+   { "brown",  .5, .125,  0 },
+   { "purple",  1,    0,  1 },
+   { "pink",    1,   .5, .5 },
+   { "orange",  1,   .5,  0 },
+   { "gray",    0,   .5, .5 },
+   { "cyan",    0,    1,  1 }
+};
+#define color_count ((sizeof colors)/(sizeof colors[0]))
+
+static const struct color *
+color_of(const char *arg)
+{
+   int icolor = color_count;
+
+   while (--icolor >= 0)
+   {
+      if (strcmp(colors[icolor].name, arg) == 0)
+         return colors+icolor;
+   }
+
+   fprintf(stderr, "genpng: invalid color %s\n", arg);
+   exit(1);
+}
+
+static double
+width_of(const char *arg)
+{
+   if (strcmp(arg, "filled") == 0)
+      return 0;
+
+   else
+   {
+      char *ep = NULL;
+      double w = strtod(arg, &ep);
+
+      if (ep != NULL && *ep == 0 && w > 0)
+         return w;
+   }
+
+   fprintf(stderr, "genpng: invalid line width %s\n", arg);
+   exit(1);
+}
+
+static double
+coordinate_of(const char *arg)
+{
+   char *ep = NULL;
+   double w = strtod(arg, &ep);
+
+   if (ep != NULL && *ep == 0)
+      return w;
+
+   fprintf(stderr, "genpng: invalid coordinate value %s\n", arg);
+   exit(1);
+}
+
+struct arg; /* forward declaration */
+
+typedef int (*shape_fn_ptr)(const struct arg *arg, double x, double y);
+   /* A function to determine if (x,y) is inside the shape.
+    *
+    * There are two implementations:
+    *
+    *    inside_fn: returns true if the point is inside
+    *    check_fn:  returns;
+    *       -1: the point is outside the shape by more than the filter width (2)
+    *        0: the point may be inside the shape
+    *       +1: the point is inside the shape by more than the filter width
+    */
+#define OUTSIDE (-1)
+#define INSIDE  (1)
+
+struct arg
+{
+   const struct color *color;
+   shape_fn_ptr        inside_fn;
+   shape_fn_ptr        check_fn;
+   double              width; /* line width, 0 for 'filled' */
+   double              x1, y1, x2, y2;
+};
+
+/* IMPLEMENTATION NOTE:
+ *
+ * We want the contribution of each shape to the sample corresponding to each
+ * pixel.  This could be obtained by super sampling the image to infinite
+ * dimensions, finding each point within the shape and assigning that a value
+ * '1' while leaving every point outside the shape with value '0' then
+ * downsampling to the image size with sinc; computationally very expensive.
+ *
+ * Approximations are as follows:
+ *
+ * 1) If the pixel coordinate is within the shape assume the sample has the
+ *    shape color and is opaque, else assume there is no contribution from
+ *    the shape.
+ *
+ *    This is the equivalent of aliased rendering or resampling an image with
+ *    a block filter.  The maximum error in the calculated alpha (which will
+ *    always be 0 or 1) is 0.5.
+ *
+ * 2) If the shape is within a square of size 1x1 centered on the pixel assume
+ *    that the shape obscures an amount of the pixel equal to its area within
+ *    that square.
+ *
+ *    This is the equivalent of 'pixel coverage' alpha calculation or resampling
+ *    an image with a bi-linear filter.  The maximum error is over 0.2, but the
+ *    results are often acceptable.
+ *
+ *    This can be approximated by applying (1) to a super-sampled image then
+ *    downsampling with a bi-linear filter.  The error in the super-sampled
+ *    image is 0.5 per sample, but the resampling reduces this.
+ *
+ * 3) Use a better filter with a super-sampled image; in the limit this is the
+ *    sinc() approach.
+ *
+ * 4) Do the geometric calculation; a bivariate definite integral across the
+ *    shape, unfortunately this means evaluating Si(x), the integral of sinc(x),
+ *    which is still a lot of math.
+ *
+ * This code uses approach (3) with a bi-cubic filter and 8x super-sampling
+ * and method (1) for the super-samples.  This means that the sample is either
+ * 0 or 1, depending on whether the sub-pixel is within or outside the shape.
+ * The bi-cubic weights are also fixed and the 16 required weights are
+ * pre-computed here (note that the 'scale' setting will need to be changed if
+ * 'super' is increased).
+ *
+ * The code also calculates a sum to the edge of the filter. This is not
+ * currently used by could be used to optimize the calculation.
+ */
+#if 0 /* bc code */
+scale=10
+super=8
+define bicubic(x) {
+   if (x <= 1) return (1.5*x - 2.5)*x*x + 1;
+   if (x <  2) return (((2.5 - 0.5*x)*x - 4)*x + 2);
+   return 0;
+}
+define sum(x) {
+   auto s;
+   s = 0;
+   while (x < 2*super) {
+      s = s + bicubic(x/super);
+      x = x + 1;
+   }
+   return s;
+}
+define results(x) {
+   auto b, s;
+   b = bicubic(x/super);
+   s = sum(x);
+
+   print "   /*", x, "*/ { ", b, ", ", s, " }";
+   return 1;
+}
+x=0
+while (x<2*super) {
+   x = x + results(x)
+   if (x < 2*super) print ","
+   print "\n"
+}
+quit
+#endif
+
+#define BICUBIC1(x) /*     |x| <= 1 */ ((1.5*(x)* - 2.5)*(x)*(x) + 1)
+#define BICUBIC2(x) /* 1 < |x| <  2 */ (((2.5 - 0.5*(x))*(x) - 4)*(x) + 2)
+#define FILTER_WEIGHT 9 /* Twice the first sum below */
+#define FILTER_WIDTH  2 /* Actually half the width; -2..+2 */
+#define FILTER_STEPS  8 /* steps per filter unit */
+static const double
+bicubic[16][2] =
+{
+   /* These numbers are exact; the weight for the filter is 1/9, but this
+    * would make the numbers inexact, so it is not included here.
+    */
+   /*          bicubic      sum        */
+   /* 0*/ { 1.0000000000, 4.5000000000 },
+   /* 1*/ {  .9638671875, 3.5000000000 },
+   /* 2*/ {  .8671875000, 2.5361328125 },
+   /* 3*/ {  .7275390625, 1.6689453125 },
+   /* 4*/ {  .5625000000,  .9414062500 },
+   /* 5*/ {  .3896484375,  .3789062500 },
+   /* 6*/ {  .2265625000, -.0107421875 },
+   /* 7*/ {  .0908203125, -.2373046875 },
+   /* 8*/ {            0, -.3281250000 },
+   /* 9*/ { -.0478515625, -.3281250000 },
+   /*10*/ { -.0703125000, -.2802734375 },
+   /*11*/ { -.0732421875, -.2099609375 },
+   /*12*/ { -.0625000000, -.1367187500 },
+   /*13*/ { -.0439453125, -.0742187500 },
+   /*14*/ { -.0234375000, -.0302734375 },
+   /*15*/ { -.0068359375, -.0068359375 }
+};
+
+static double
+alpha_calc(const struct arg *arg, double x, double y)
+{
+   /* For [x-2..x+2],[y-2,y+2] calculate the weighted bicubic given a function
+    * which tells us whether a point is inside or outside the shape.  First
+    * check if we need to do this at all:
+    */
+   switch (arg->check_fn(arg, x, y))
+   {
+      case OUTSIDE:
+         return 0; /* all samples outside the shape */
+
+      case INSIDE:
+         return 1; /* all samples inside the shape */
+
+      default:
+      {
+         int dy;
+         double alpha = 0;
+
+#        define FILTER_D (FILTER_WIDTH*FILTER_STEPS-1)
+         for (dy=-FILTER_D; dy<=FILTER_D; ++dy)
+         {
+            double wy = bicubic[abs(dy)][0];
+
+            if (wy != 0)
+            {
+               double alphay = 0;
+               int dx;
+
+               for (dx=-FILTER_D; dx<=FILTER_D; ++dx)
+               {
+                  double wx = bicubic[abs(dx)][0];
+
+                  if (wx != 0 && arg->inside_fn(arg, x+dx/16, y+dy/16))
+                     alphay += wx;
+               }
+
+               alpha += wy * alphay;
+            }
+         }
+
+         /* This needs to be weighted for each dimension: */
+         return alpha / (FILTER_WEIGHT*FILTER_WEIGHT);
+      }
+   }
+}
+
+/* These are the shape functions. */
+/* "square",
+ * { inside_square_filled, check_square_filled },
+ * { inside_square, check_square }
+ */
+static int
+square_check(double x, double y, double x1, double y1, double x2, double y2)
+   /* Is x,y inside the square (x1,y1)..(x2,y2)? */
+{
+   /* Do a modified Cohen-Sutherland on one point, bit patterns that indicate
+    * 'outside' are:
+    *
+    *   x<x1 | x<y1 | x<x2 | x<y2
+    *    0      x      0      x     To the right
+    *    1      x      1      x     To the left
+    *    x      0      x      0     Below
+    *    x      1      x      1     Above
+    *
+    * So 'inside' is (x<x1) != (x<x2) && (y<y1) != (y<y2);
+    */
+   return ((x<x1) ^ (x<x2)) & ((y<y1) ^ (y<y2));
+}
+
+static int
+inside_square_filled(const struct arg *arg, double x, double y)
+{
+   return square_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
+}
+
+static int
+square_check_line(const struct arg *arg, double x, double y, double w)
+   /* Check for a point being inside the boundaries implied by the given arg
+    * and assuming a width 2*w each side of the boundaries.  This returns the
+    * 'check' INSIDE/OUTSIDE/0 result but note the semantics:
+    *
+    *          +--------------+
+    *          |              |   OUTSIDE
+    *          |   INSIDE     |
+    *          |              |
+    *          +--------------+
+    *
+    * And '0' means within the line boundaries.
+    */
+{
+   double cx = (arg->x1+arg->x2)/2;
+   double wx = fabs(arg->x1-arg->x2)/2;
+   double cy = (arg->y1+arg->y2)/2;
+   double wy = fabs(arg->y1-arg->y2)/2;
+
+   if (square_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
+   {
+      /* Inside, but maybe too far; check for the redundant case where
+       * the lines overlap:
+       */
+      wx -= w;
+      wy -= w;
+      if (wx > 0 && wy > 0 && square_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
+         return INSIDE; /* between (inside) the boundary lines. */
+
+      return 0; /* inside the lines themselves. */
+   }
+
+   return OUTSIDE; /* outside the boundary lines. */
+}
+
+static int
+check_square_filled(const struct arg *arg, double x, double y)
+{
+   /* The filter extends +/-FILTER_WIDTH each side of each output point, so
+    * the check has to expand and contract the square by that amount; '0'
+    * means close enough to the edge of the square that the bicubic filter has
+    * to be run, OUTSIDE means alpha==0, INSIDE means alpha==1.
+    */
+   return square_check_line(arg, x, y, FILTER_WIDTH);
+}
+
+static int
+inside_square(const struct arg *arg, double x, double y)
+{
+   /* Return true if within the drawn lines, else false, no need to distinguish
+    * INSIDE vs OUTSIDE here:
+    */
+   return square_check_line(arg, x, y, arg->width/2) == 0;
+}
+
+static int
+check_square(const struct arg *arg, double x, double y)
+{
+   /* So for this function a result of 'INSIDE' means inside the actual lines.
+    */
+   double w = arg->width/2;
+
+   if (square_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
+   {
+      /* Somewhere close to the boundary lines. If far enough inside one of
+       * them then we can return INSIDE:
+       */
+      w -= FILTER_WIDTH;
+
+      if (w > 0 && square_check_line(arg, x, y, w) == 0)
+         return INSIDE;
+
+      /* Point is somewhere in the filter region: */
+      return 0;
+   }
+
+   else /* Inside or outside the square by more than w+FILTER_WIDTH. */
+      return OUTSIDE;
+}
+
+/* "circle",
+ * { inside_circle_filled, check_circle_filled },
+ * { inside_circle, check_circle }
+ *
+ * The functions here are analoguous to the square ones; however, they check
+ * the corresponding ellipse as opposed to the rectangle.
+ */
+static int
+circle_check(double x, double y, double x1, double y1, double x2, double y2)
+{
+   if (square_check(x, y, x1, y1, x2, y2))
+   {
+      /* Inside the square, so maybe inside the circle too: */
+      const double cx = (x1 + x2)/2;
+      const double cy = (y1 + y2)/2;
+      const double dx = x1 - x2;
+      const double dy = y1 - y2;
+
+      x = (x - cx)/dx;
+      y = (y - cy)/dy;
+
+      /* It is outside if the distance from the center is more than half the
+       * diameter:
+       */
+      return x*x+y*y < .25;
+   }
+
+   return 0; /* outside */
+}
+
+static int
+inside_circle_filled(const struct arg *arg, double x, double y)
+{
+   return circle_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
+}
+
+static int
+circle_check_line(const struct arg *arg, double x, double y, double w)
+   /* Check for a point being inside the boundaries implied by the given arg
+    * and assuming a width 2*w each side of the boundaries.  This function has
+    * the same semantic as square_check_line but tests the circle.
+    */
+{
+   double cx = (arg->x1+arg->x2)/2;
+   double wx = fabs(arg->x1-arg->x2)/2;
+   double cy = (arg->y1+arg->y2)/2;
+   double wy = fabs(arg->y1-arg->y2)/2;
+
+   if (circle_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
+   {
+      /* Inside, but maybe too far; check for the redundant case where
+       * the lines overlap:
+       */
+      wx -= w;
+      wy -= w;
+      if (wx > 0 && wy > 0 && circle_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
+         return INSIDE; /* between (inside) the boundary lines. */
+
+      return 0; /* inside the lines themselves. */
+   }
+
+   return OUTSIDE; /* outside the boundary lines. */
+}
+
+static int
+check_circle_filled(const struct arg *arg, double x, double y)
+{
+   return circle_check_line(arg, x, y, FILTER_WIDTH);
+}
+
+static int
+inside_circle(const struct arg *arg, double x, double y)
+{
+   return circle_check_line(arg, x, y, arg->width/2) == 0;
+}
+
+static int
+check_circle(const struct arg *arg, double x, double y)
+{
+   /* Exactly as the 'square' code.  */
+   double w = arg->width/2;
+
+   if (circle_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
+   {
+      w -= FILTER_WIDTH;
+
+      if (w > 0 && circle_check_line(arg, x, y, w) == 0)
+         return INSIDE;
+
+      /* Point is somewhere in the filter region: */
+      return 0;
+   }
+
+   else /* Inside or outside the square by more than w+FILTER_WIDTH. */
+      return OUTSIDE;
+}
+
+/* "line",
+ * { NULL, NULL },  There is no 'filled' line.
+ * { inside_line, check_line }
+ */
+static int
+line_check(double x, double y, double x1, double y1, double x2, double y2,
+   double w, double expand)
+{
+   /* Shift all the points to (arg->x1, arg->y1) */
+   double lx = x2 - x1;
+   double ly = y2 - y1;
+   double len2 = lx*lx + ly*ly;
+   double cross, dot;
+
+   x -= x1;
+   y -= y1;
+
+   /* The dot product is the distance down the line, the cross product is
+    * the distance away from the line:
+    *
+    *    distance = |cross| / sqrt(len2)
+    */
+   cross = x * ly - y * lx;
+
+   /* If 'distance' is more than w the point is definitely outside the line:
+    *
+    *     distance >= w
+    *     |cross|  >= w * sqrt(len2)
+    *     cross^2  >= w^2 * len2:
+    */
+   if (cross*cross >= (w+expand)*(w+expand)*len2)
+      return 0; /* outside */
+
+   /* Now find the distance *along* the line; this comes from the dot product
+    * lx.x+ly.y. The actual distance (in pixels) is:
+    *
+    *   distance = dot / sqrt(len2)
+    */
+   dot = lx * x + ly * y;
+
+   /* The test for 'outside' is:
+    *
+    *    distance < 0 || distance > sqrt(len2)
+    *                 -> dot / sqrt(len2) > sqrt(len2)
+    *                 -> dot > len2
+    *
+    * But 'expand' is used for the filter width and needs to be handled too:
+    */
+   return dot > -expand && dot < len2+expand;
+}
+
+static int
+inside_line(const struct arg *arg, double x, double y)
+{
+   return line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2, 0);
+}
+
+static int
+check_line(const struct arg *arg, double x, double y)
+{
+   /* The end caps of the line must be checked too; it's not enough just to
+    * widen the line by FILTER_WIDTH; 'expand' exists for this purpose:
+    */
+   if (line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
+       FILTER_WIDTH))
+   {
+      /* Inside the line+filter; far enough inside that the filter isn't
+       * required?
+       */
+      if (arg->width > 2*FILTER_WIDTH &&
+          line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
+             -FILTER_WIDTH))
+         return INSIDE;
+
+      return 0;
+   }
+
+   return OUTSIDE;
+}
+
+static const struct
+{
+   const char    *name;
+   shape_fn_ptr   function[2/*fill,line*/][2];
+#  define         FN_INSIDE 0
+#  define         FN_CHECK 1
+} shape_defs[] =
+{
+   {  "square",
+      {  { inside_square_filled, check_square_filled },
+         { inside_square, check_square } }
+   },
+   {  "circle",
+      {  { inside_circle_filled, check_circle_filled },
+         { inside_circle, check_circle } }
+   },
+   {  "line",
+      {  { NULL, NULL },
+         { inside_line, check_line } }
+   }
+};
+
+#define shape_count ((sizeof shape_defs)/(sizeof shape_defs[0]))
+
+static shape_fn_ptr
+shape_of(const char *arg, double width, int f)
+{
+   unsigned int i;
+
+   for (i=0; i<shape_count; ++i) if (strcmp(shape_defs[i].name, arg) == 0)
+   {
+      shape_fn_ptr fn = shape_defs[i].function[width != 0][f];
+
+      if (fn != NULL)
+         return fn;
+
+      fprintf(stderr, "genpng: %s %s not supported\n",
+         width == 0 ? "filled" : "unfilled", arg);
+      exit(1);
+   }
+
+   fprintf(stderr, "genpng: %s: not a valid shape name\n", arg);
+   exit(1);
+}
+
+static void
+parse_arg(struct arg *arg, const char **argv/*7 arguments*/)
+{
+   /* shape ::= color width shape x1 y1 x2 y2 */
+   arg->color = color_of(argv[0]);
+   arg->width = width_of(argv[1]);
+   arg->inside_fn = shape_of(argv[2], arg->width, FN_INSIDE);
+   arg->check_fn = shape_of(argv[2], arg->width, FN_CHECK);
+   arg->x1 = coordinate_of(argv[3]);
+   arg->y1 = coordinate_of(argv[4]);
+   arg->x2 = coordinate_of(argv[5]);
+   arg->y2 = coordinate_of(argv[6]);
+}
+
+static png_uint_32
+read_wh(const char *name, const char *str)
+   /* read a PNG width or height */
+{
+   char *ep = NULL;
+   unsigned long ul = strtoul(str, &ep, 10);
+
+   if (ep != NULL && *ep == 0 && ul > 0 && ul <= 0x7fffffff)
+      return (png_uint_32)/*SAFE*/ul;
+
+   fprintf(stderr, "genpng: %s: invalid number %s\n", name, str);
+   exit(1);
+}
+
+static void
+pixel(png_uint_16p p, struct arg *args, int nargs, double x, double y)
+{
+   /* Fill in the pixel by checking each shape (args[nargs]) for effects on
+    * the corresponding sample:
+    */
+   double r=0, g=0, b=0, a=0;
+
+   while (--nargs >= 0 && a != 1)
+   {
+      /* NOTE: alpha_calc can return a value outside the range 0..1 with the
+       * bicubic filter.
+       */
+      const double alpha = alpha_calc(args+nargs, x, y) * (1-a);
+
+      r += alpha * args[nargs].color->red;
+      g += alpha * args[nargs].color->green;
+      b += alpha * args[nargs].color->blue;
+      a += alpha;
+   }
+
+   /* 'a' may be negative or greater than 1; if it is, negative clamp the
+    * pixel to 0 if >1 clamp r/g/b:
+    */
+   if (a > 0)
+   {
+      if (a > 1)
+      {
+         if (r > 1) r = 1;
+         if (g > 1) g = 1;
+         if (b > 1) b = 1;
+         a = 1;
+      }
+
+      /* And fill in the pixel: */
+      p[0] = (png_uint_16)/*SAFE*/round(r * 65535);
+      p[1] = (png_uint_16)/*SAFE*/round(g * 65535);
+      p[2] = (png_uint_16)/*SAFE*/round(b * 65535);
+      p[3] = (png_uint_16)/*SAFE*/round(a * 65535);
+   }
+
+   else
+      p[3] = p[2] = p[1] = p[0] = 0;
+}
+
+int
+main(int argc, const char **argv)
+{
+   int convert_to_8bit = 0;
+
+   /* There is one option: --8bit: */
+   if (argc > 1 && strcmp(argv[1], "--8bit") == 0)
+      --argc, ++argv, convert_to_8bit = 1;
+
+   if (argc >= 3)
+   {
+      png_uint_16p buffer;
+      int nshapes;
+      png_image image;
+#     define max_shapes 256
+      struct arg arg_list[max_shapes];
+
+      /* The libpng Simplified API write code requires a fully initialized
+       * structure.
+       */
+      memset(&image, 0, sizeof image);
+      image.version = PNG_IMAGE_VERSION;
+      image.opaque = NULL;
+      image.width = read_wh("width", argv[1]);
+      image.height = read_wh("height", argv[2]);
+      image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+      image.flags = 0;
+      image.colormap_entries = 0;
+
+      /* Check the remainder of the arguments */
+      for (nshapes=0; 3+7*(nshapes+1) <= argc && nshapes < max_shapes;
+           ++nshapes)
+         parse_arg(arg_list+nshapes, argv+3+7*nshapes);
+
+      if (3+7*nshapes != argc)
+      {
+         fprintf(stderr, "genpng: %s: too many arguments\n", argv[3+7*nshapes]);
+         return 1;
+      }
+
+      /* Create the buffer: */
+      buffer = malloc(PNG_IMAGE_SIZE(image));
+
+      if (buffer != NULL)
+      {
+         png_uint_32 y;
+
+         /* Write each row... */
+         for (y=0; y<image.height; ++y)
+         {
+            png_uint_32 x;
+
+            /* Each pixel in each row: */
+            for (x=0; x<image.width; ++x)
+               pixel(buffer + 4*(x + y*image.width), arg_list, nshapes, x, y);
+         }
+
+         /* Write the result (to stdout) */
+         if (png_image_write_to_stdio(&image, stdout, convert_to_8bit,
+             buffer, 0/*row_stride*/, NULL/*colormap*/))
+         {
+            free(buffer);
+            return 0; /* success */
+         }
+
+         else
+            fprintf(stderr, "genpng: write stdout: %s\n", image.message);
+
+         free(buffer);
+      }
+
+      else
+         fprintf(stderr, "genpng: out of memory: %lu bytes\n",
+               (unsigned long)PNG_IMAGE_SIZE(image));
+   }
+
+   else
+   {
+      /* Wrong number of arguments */
+      fprintf(stderr, "genpng: usage: genpng [--8bit] width height {shape}\n"
+         " Generate a transparent PNG in RGBA (truecolor+alpha) format\n"
+         " containing the given shape or shapes.  Shapes are defined:\n"
+         "\n"
+         "  shape ::= color width shape x1 y1 x2 y2\n"
+         "  color ::= black|white|red|green|yellow|blue\n"
+         "  color ::= brown|purple|pink|orange|gray|cyan\n"
+         "  width ::= filled|<number>\n"
+         "  shape ::= circle|square|line\n"
+         "  x1,x2 ::= <number>\n"
+         "  y1,y2 ::= <number>\n"
+         "\n"
+         " Numbers are floating point numbers describing points relative to\n"
+         " the top left of the output PNG as pixel coordinates.  The 'width'\n"
+         " parameter is either the width of the line (in output pixels) used\n"
+         " to draw the shape or 'filled' to indicate that the shape should\n"
+         " be filled with the color.\n"
+         "\n"
+         " Colors are interpreted loosely to give access to the eight full\n"
+         " intensity RGB values:\n"
+         "\n"
+         "  black, red, green, blue, yellow, cyan, purple, white,\n"
+         "\n"
+         " Cyan is full intensity blue+green; RGB(0,1,1), plus the following\n"
+         " lower intensity values:\n"
+         "\n"
+         "  brown:  red+orange:  RGB(0.5, 0.125, 0) (dark red+orange)\n"
+         "  pink:   red+white:   RGB(1.0, 0.5,   0.5)\n"
+         "  orange: red+yellow:  RGB(1.0, 0.5,   0)\n"
+         "  gray:   black+white: RGB(0.5, 0.5,   0.5)\n"
+         "\n"
+         " The RGB values are selected to make detection of aliasing errors\n"
+         " easy. The names are selected to make the description of errors\n"
+         " easy.\n"
+         "\n"
+         " The PNG is written to stdout, if --8bit is given a 32bpp RGBA sRGB\n"
+         " file is produced, otherwise a 64bpp RGBA linear encoded file is\n"
+         " written.\n");
+   }
+
+   return 1;
+}
+#endif /* SIMPLIFIED_WRITE && STDIO */
diff --git a/contrib/tools/intgamma.sh b/contrib/tools/intgamma.sh
new file mode 100755
index 0000000..41c5d6d
--- /dev/null
+++ b/contrib/tools/intgamma.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+#
+# intgamma.sh
+#
+# Last changed in libpng 1.6.0 [February 14, 2013]
+#
+# COPYRIGHT: Written by John Cunningham Bowler, 2013.
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work.  This work is published from:
+# United States.
+#
+# Shell script to generate png.c 8-bit and 16-bit log tables (see the code in
+# png.c for details).
+#
+# This script uses the "bc" arbitrary precision calculator to calculate 32-bit
+# fixed point values of logarithms appropriate to finding the log of an 8-bit
+# (0..255) value and a similar table for the exponent calculation.
+#
+# "bc" must be on the path when the script is executed, and the math library
+# (-lm) must be available
+#
+# function to print out a list of numbers as integers; the function truncates
+# the integers which must be one-per-line
+function print(){
+   awk 'BEGIN{
+      str = ""
+   }
+   {
+      sub("\\.[0-9]*$", "")
+      if ($0 == "")
+         $0 = "0"
+
+      if (str == "")
+         t = "   " $0 "U"
+      else
+         t = str ", " $0 "U"
+
+      if (length(t) >= 80) {
+         print str ","
+         str = "   " $0 "U"
+      } else
+         str = t
+   }
+   END{
+      print str
+   }'
+}
+#
+# The logarithm table.
+cat <<END
+/* 8-bit log table: png_8bit_l2[128]
+ * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
+ * 255, so it's the base 2 logarithm of a normalized 8-bit floating point
+ * mantissa.  The numbers are 32-bit fractions.
+ */
+static const png_uint_32
+png_8bit_l2[128] =
+{
+END
+#
+bc -lqws <<END | print
+f=65536*65536/l(2)
+for (i=128;i<256;++i) { .5 - l(i/255)*f; }
+END
+echo '};'
+echo
+#
+# The exponent table.
+cat <<END
+/* The 'exp()' case must invert the above, taking a 20-bit fixed point
+ * logarithmic value and returning a 16 or 8-bit number as appropriate.  In
+ * each case only the low 16 bits are relevant - the fraction - since the
+ * integer bits (the top 4) simply determine a shift.
+ *
+ * The worst case is the 16-bit distinction between 65535 and 65534; this
+ * requires perhaps spurious accuracy in the decoding of the logarithm to
+ * distinguish log2(65535/65534.5) - 10^-5 or 17 bits.  There is little chance
+ * of getting this accuracy in practice.
+ *
+ * To deal with this the following exp() function works out the exponent of the
+ * frational part of the logarithm by using an accurate 32-bit value from the
+ * top four fractional bits then multiplying in the remaining bits.
+ */
+static const png_uint_32
+png_32bit_exp[16] =
+{
+END
+#
+bc -lqws <<END | print
+f=l(2)/16
+for (i=0;i<16;++i) {
+   x = .5 + e(-i*f)*2^32;
+   if (x >= 2^32) x = 2^32-1;
+   x;
+}
+END
+echo '};'
+echo
+#
+# And the table of adjustment values.
+cat <<END
+/* Adjustment table; provided to explain the numbers in the code below. */
+#if 0
+END
+bc -lqws <<END | awk '{ printf "%5d %s\n", 12-NR, $0 }'
+for (i=11;i>=0;--i){
+   (1 - e(-(2^i)/65536*l(2))) * 2^(32-i)
+}
+END
+echo '#endif'
diff --git a/contrib/tools/makesRGB.c b/contrib/tools/makesRGB.c
new file mode 100644
index 0000000..e66c9f1
--- /dev/null
+++ b/contrib/tools/makesRGB.c
@@ -0,0 +1,430 @@
+/* makesRGB.c -- build sRGB-to-linear and linear-to-sRGB conversion tables
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2013.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Make a table to convert 8-bit sRGB encoding values into the closest 16-bit
+ * linear value.
+ *
+ * Make two tables to take a linear value scaled to 255*65535 and return an
+ * approximation to the 8-bit sRGB encoded value.  Calculate the error in these
+ * tables and display it.
+ */
+#define _C99_SOURCE 1
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+/* pngpriv.h includes the definition of 'PNG_sRGB_FROM_LINEAR' which is required
+ * to verify the actual code.
+ */
+#include "../../pngpriv.h"
+
+#include "sRGB.h"
+
+/* The tables are declared 'const' in pngpriv.h, so this redefines the tables to
+ * be used.
+ */
+#define png_sRGB_table sRGB_table
+#define png_sRGB_base sRGB_base
+#define png_sRGB_delta sRGB_delta
+
+static png_uint_16 png_sRGB_table[256];
+static png_uint_16 png_sRGB_base[512];
+static png_byte png_sRGB_delta[512];
+
+static const unsigned int max_input = 255*65535;
+
+double
+fsRGB(double l)
+{
+   return sRGB_from_linear(l/max_input);
+}
+
+double
+sRGB(unsigned int i)
+{
+   return fsRGB(i);
+}
+
+double
+finvsRGB(unsigned int i)
+{
+   return 65535 * linear_from_sRGB(i/255.);
+}
+
+png_uint_16
+invsRGB(unsigned int i)
+{
+   unsigned int x = nearbyint(finvsRGB(i));
+
+   if (x > 65535)
+   {
+      fprintf(stderr, "invsRGB(%u) overflows to %u\n", i, x);
+      exit(1);
+   }
+
+   return (png_uint_16)x;
+}
+
+int
+main(int argc, char **argv)
+{
+   unsigned int i, i16, ibase;
+   double min_error = 0;
+   double max_error = 0;
+   double min_error16 = 0;
+   double max_error16 = 0;
+   double adjust;
+   double adjust_lo = 0.4, adjust_hi = 0.6, adjust_mid = 0.5;
+   unsigned int ec_lo = 0, ec_hi = 0, ec_mid = 0;
+   unsigned int error_count = 0;
+   unsigned int error_count16 = 0;
+   int test_only = 0;
+
+   if (argc > 1)
+      test_only = strcmp("--test", argv[1]) == 0;
+
+   /* Initialize the encoding table first. */
+   for (i=0; i<256; ++i)
+   {
+      png_sRGB_table[i] = invsRGB(i);
+   }
+
+   /* Now work out the decoding tables (this is where the error comes in because
+    * there are 512 set points and 512 straight lines between them.)
+    */
+   for (;;)
+   {
+      if (ec_lo == 0)
+         adjust = adjust_lo;
+
+      else if (ec_hi == 0)
+         adjust = adjust_hi;
+
+      else if (ec_mid == 0)
+         adjust = adjust_mid;
+
+      else if (ec_mid < ec_hi)
+         adjust = (adjust_mid + adjust_hi)/2;
+
+      else if (ec_mid < ec_lo)
+         adjust = (adjust_mid + adjust_lo)/2;
+
+      else
+      {
+         fprintf(stderr, "not reached: %u .. %u .. %u\n", ec_lo, ec_mid, ec_hi);
+         exit(1);
+      }
+
+      /* Calculate the table using the current 'adjust' */
+      for (i=0; i<=511; ++i)
+      {
+         double lo = 255 * sRGB(i << 15);
+         double hi = 255 * sRGB((i+1) << 15);
+         unsigned int calc;
+
+         calc = nearbyint((lo+adjust) * 256);
+         if (calc > 65535)
+         {
+            fprintf(stderr, "table[%d][0]: overflow %08x (%d)\n", i, calc,
+               calc);
+            exit(1);
+         }
+         png_sRGB_base[i] = calc;
+
+         calc = nearbyint((hi-lo) * 32);
+         if (calc > 255)
+         {
+            fprintf(stderr, "table[%d][1]: overflow %08x (%d)\n", i, calc,
+               calc);
+            exit(1);
+         }
+         png_sRGB_delta[i] = calc;
+      }
+
+      /* Check the 16-bit linear values alone: */
+      error_count16 = 0;
+      for (i16=0; i16 <= 65535; ++i16)
+      {
+         unsigned int i = 255*i16;
+         unsigned int iexact = nearbyint(255*sRGB(i));
+         unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+         if (icalc != iexact)
+            ++error_count16;
+      }
+
+      /* Now try changing the adjustment. */
+      if (ec_lo == 0)
+         ec_lo = error_count16;
+
+      else if (ec_hi == 0)
+         ec_hi = error_count16;
+
+      else if (ec_mid == 0)
+      {
+         ec_mid = error_count16;
+         printf("/* initial error counts: %u .. %u .. %u */\n", ec_lo, ec_mid,
+            ec_hi);
+      }
+
+      else if (error_count16 < ec_mid)
+      {
+         printf("/* adjust (mid ): %f: %u -> %u */\n", adjust, ec_mid,
+            error_count16);
+         ec_mid = error_count16;
+         adjust_mid = adjust;
+      }
+
+      else if (adjust < adjust_mid && error_count16 < ec_lo)
+      {
+         printf("/* adjust (low ): %f: %u -> %u */\n", adjust, ec_lo,
+            error_count16);
+         ec_lo = error_count16;
+         adjust_lo = adjust;
+      }
+
+      else if (adjust > adjust_mid && error_count16 < ec_hi)
+      {
+         printf("/* adjust (high): %f: %u -> %u */\n", adjust, ec_hi,
+            error_count16);
+         ec_hi = error_count16;
+         adjust_hi = adjust;
+      }
+
+      else
+      {
+         adjust = adjust_mid;
+         printf("/* adjust: %f: %u */\n", adjust, ec_mid);
+         break;
+      }
+   }
+
+   /* For each entry in the table try to adjust it to minimize the error count
+    * in that entry.  Each entry corresponds to 128 input values.
+    */
+   for (ibase=0; ibase<65536; ibase+=128)
+   {
+      png_uint_16 base = png_sRGB_base[ibase >> 7], trybase = base, ob=base;
+      png_byte delta = png_sRGB_delta[ibase >> 7], trydelta = delta, od=delta;
+      unsigned int ecbase = 0, eco;
+
+      for (;;)
+      {
+         png_sRGB_base[ibase >> 7] = trybase;
+         png_sRGB_delta[ibase >> 7] = trydelta;
+
+         /* Check the 16-bit linear values alone: */
+         error_count16 = 0;
+         for (i16=ibase; i16 < ibase+128; ++i16)
+         {
+            unsigned int i = 255*i16;
+            unsigned int iexact = nearbyint(255*sRGB(i));
+            unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+            if (icalc != iexact)
+               ++error_count16;
+         }
+
+         if (error_count16 == 0)
+            break;
+
+         if (ecbase == 0)
+         {
+            eco = ecbase = error_count16;
+            ++trybase; /* First test */
+         }
+
+         else if (error_count16 < ecbase)
+         {
+            if (trybase > base)
+            {
+               base = trybase;
+               ++trybase;
+            }
+            else if (trybase < base)
+            {
+               base = trybase;
+               --trybase;
+            }
+            else if (trydelta > delta)
+            {
+               delta = trydelta;
+               ++trydelta;
+            }
+            else if (trydelta < delta)
+            {
+               delta = trydelta;
+               --trydelta;
+            }
+            else
+            {
+               fprintf(stderr, "makesRGB: impossible\n");
+               exit(1);
+            }
+            ecbase = error_count16;
+         }
+
+         else
+         {
+            if (trybase > base)
+               trybase = base-1;
+            else if (trybase < base)
+            {
+               trybase = base;
+               ++trydelta;
+            }
+            else if (trydelta > delta)
+               trydelta = delta-1;
+            else if (trydelta < delta)
+               break; /* end of tests */
+         }
+      }
+
+      png_sRGB_base[ibase >> 7] = base;
+      png_sRGB_delta[ibase >> 7] = delta;
+      if (base != ob || delta != od)
+      {
+         printf("/* table[%u]={%u,%u} -> {%u,%u} %u -> %u errors */\n",
+            ibase>>7, ob, od, base, delta, eco, ecbase);
+      }
+      else if (0)
+         printf("/* table[%u]={%u,%u} %u errors */\n", ibase>>7, ob, od,
+            ecbase);
+   }
+
+   /* Only do the full (slow) test at the end: */
+   min_error = -.4999;
+   max_error = .4999;
+   error_count = 0;
+
+   for (i=0; i <= max_input; ++i)
+   {
+      unsigned int iexact = nearbyint(255*sRGB(i));
+      unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+      if (icalc != iexact)
+      {
+         double err = 255*sRGB(i) - icalc;
+
+         if (err > (max_error+.001) || err < (min_error-.001))
+         {
+            printf(
+               "/* 0x%08x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n",
+               i, iexact, icalc, png_sRGB_base[i>>15],
+               png_sRGB_delta[i>>15], err);
+         }
+
+         ++error_count;
+         if (err > max_error)
+            max_error = err;
+         else if (err < min_error)
+            min_error = err;
+      }
+   }
+
+   /* Re-check the 16-bit cases too, including the warning if there is an error
+    * bigger than 1.
+    */
+   error_count16 = 0;
+   max_error16 = 0;
+   min_error16 = 0;
+   for (i16=0; i16 <= 65535; ++i16)
+   {
+      unsigned int i = 255*i16;
+      unsigned int iexact = nearbyint(255*sRGB(i));
+      unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+      if (icalc != iexact)
+      {
+         double err = 255*sRGB(i) - icalc;
+
+         ++error_count16;
+         if (err > max_error16)
+            max_error16 = err;
+         else if (err < min_error16)
+            min_error16 = err;
+
+         if (abs(icalc - iexact) > 1)
+            printf(
+               "/* 0x%04x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n",
+               i16, iexact, icalc, png_sRGB_base[i>>15],
+               png_sRGB_delta[i>>15], err);
+      }
+   }
+
+   /* Check the round trip for each 8-bit sRGB value. */
+   for (i16=0; i16 <= 255; ++i16)
+   {
+      unsigned int i = 255 * png_sRGB_table[i16];
+      unsigned int iexact = nearbyint(255*sRGB(i));
+      unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+      if (i16 != iexact)
+      {
+         fprintf(stderr, "8-bit rounding error: %d -> %d\n", i16, iexact);
+         exit(1);
+      }
+
+      if (icalc != i16)
+      {
+         double finv = finvsRGB(i16);
+
+         printf("/* 8-bit roundtrip error: %d -> %f -> %d(%f) */\n",
+            i16, finv, icalc, fsRGB(255*finv));
+      }
+   }
+
+
+   printf("/* error: %g - %g, %u (%g%%) of readings inexact */\n",
+      min_error, max_error, error_count, (100.*error_count)/max_input);
+   printf("/* 16-bit error: %g - %g, %u (%g%%) of readings inexact */\n",
+      min_error16, max_error16, error_count16, (100.*error_count16)/65535);
+
+   if (!test_only)
+   {
+      printf("PNG_CONST png_uint_16 png_sRGB_table[256] =\n{\n   ");
+      for (i=0; i<255; )
+      {
+         do
+         {
+            printf("%d,", png_sRGB_table[i++]);
+         }
+         while ((i & 0x7) != 0 && i<255);
+         if (i<255) printf("\n   ");
+      }
+      printf("%d\n};\n\n", png_sRGB_table[i]);
+
+
+      printf("PNG_CONST png_uint_16 png_sRGB_base[512] =\n{\n   ");
+      for (i=0; i<511; )
+      {
+         do
+         {
+            printf("%d,", png_sRGB_base[i++]);
+         }
+         while ((i & 0x7) != 0 && i<511);
+         if (i<511) printf("\n   ");
+      }
+      printf("%d\n};\n\n", png_sRGB_base[i]);
+
+      printf("PNG_CONST png_byte png_sRGB_delta[512] =\n{\n   ");
+      for (i=0; i<511; )
+      {
+         do
+         {
+            printf("%d,", png_sRGB_delta[i++]);
+         }
+         while ((i & 0xf) != 0 && i<511);
+         if (i<511) printf("\n   ");
+      }
+      printf("%d\n};\n\n", png_sRGB_delta[i]);
+   }
+
+   return 0;
+}
diff --git a/contrib/tools/png-fix-itxt.c b/contrib/tools/png-fix-itxt.c
new file mode 100644
index 0000000..c7654c1
--- /dev/null
+++ b/contrib/tools/png-fix-itxt.c
@@ -0,0 +1,164 @@
+
+/* png-fix-itxt version 1.0.0
+ *
+ * Copyright 2015 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Usage:
+ *
+ *     png-fix-itxt.exe < bad.png > good.png
+ *
+ * Fixes a PNG file written with libpng-1.6.0 or 1.6.1 that has one or more
+ * uncompressed iTXt chunks.  Assumes that the actual length is greater
+ * than or equal to the value in the length byte, and that the CRC is
+ * correct for the actual length.  This program hunts for the CRC and
+ * adjusts the length byte accordingly.  It is not an error to process a
+ * PNG file that has no iTXt chunks or one that has valid iTXt chunks;
+ * such files will simply be copied.
+ *
+ * Requires zlib (for crc32 and Z_NULL); build with
+ *
+ *     gcc -O -o png-fix-itxt png-fix-itxt.c -lz
+ *
+ * If you need to handle iTXt chunks larger than 500000 kbytes you must
+ * rebuild png-fix-itxt with a larger values of MAX_LENGTH (or a smaller value
+ * if you know you will never encounter such huge iTXt chunks).
+ */
+
+#include <stdio.h>
+#include <zlib.h>
+
+#define MAX_LENGTH 500000
+
+/* Read one character (inchar), also return octet (c), break if EOF */
+#define GETBREAK inchar=getchar(); \
+                 c=(inchar & 0xffU);\
+                 if (inchar != c) break
+int
+main(void)
+{
+   unsigned int i;
+   unsigned char buf[MAX_LENGTH];
+   unsigned long crc;
+   unsigned char c;
+   int inchar;
+
+/* Skip 8-byte signature */
+   for (i=8; i; i--)
+   {
+      GETBREAK;
+      putchar(c);
+   }
+
+if (inchar == c) /* !EOF */
+for (;;)
+ {
+   /* Read the length */
+   unsigned long length; /* must be 32 bits! */
+   GETBREAK; buf[0] = c; length  = c; length <<= 8;
+   GETBREAK; buf[1] = c; length += c; length <<= 8;
+   GETBREAK; buf[2] = c; length += c; length <<= 8;
+   GETBREAK; buf[3] = c; length += c;
+
+   /* Read the chunkname */
+   GETBREAK; buf[4] = c;
+   GETBREAK; buf[5] = c;
+   GETBREAK; buf[6] = c;
+   GETBREAK; buf[7] = c;
+
+
+   /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */
+   if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116)
+   {
+      if (length >= MAX_LENGTH-12)
+         break;  /* To do: handle this more gracefully */
+
+      /* Initialize the CRC */
+      crc = crc32(0, Z_NULL, 0);
+
+      /* Copy the data bytes */
+      for (i=8; i < length + 12; i++)
+      {
+         GETBREAK; buf[i] = c;
+      }
+
+      if (inchar != c) /* EOF */
+         break;
+
+      /* Calculate the CRC */
+      crc = crc32(crc, buf+4, (uInt)length+4);
+
+      for (;;)
+      {
+        /* Check the CRC */
+        if (((crc >> 24) & 0xffU) == buf[length+8] &&
+            ((crc >> 16) & 0xffU) == buf[length+9] &&
+            ((crc >>  8) & 0xffU) == buf[length+10] &&
+            ((crc      ) & 0xffU) == buf[length+11])
+           break;
+
+        length++;
+
+        if (length >= MAX_LENGTH-12)
+           break;
+
+        GETBREAK;
+        buf[length+11] = c;
+
+        /* Update the CRC */
+        crc = crc32(crc, buf+7+length, 1);
+      }
+
+      if (inchar != c) /* EOF */
+         break;
+
+      /* Update length bytes */
+      buf[0] = (unsigned char)((length >> 24) & 0xffU);
+      buf[1] = (unsigned char)((length >> 16) & 0xffU);
+      buf[2] = (unsigned char)((length >>  8) & 0xffU);
+      buf[3] = (unsigned char)((length      ) & 0xffU);
+
+      /* Write the fixed iTXt chunk (length, name, data, crc) */
+      for (i=0; i<length+12; i++)
+         putchar(buf[i]);
+   }
+
+   else
+   {
+      if (inchar != c) /* EOF */
+         break;
+
+      /* Copy bytes that were already read (length and chunk name) */
+      for (i=0; i<8; i++)
+         putchar(buf[i]);
+
+      /* Copy data bytes and CRC */
+      for (i=8; i< length+12; i++)
+      {
+         GETBREAK;
+         putchar(c);
+      }
+
+      if (inchar != c) /* EOF */
+      {
+         break;
+      }
+
+   /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */
+      if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
+         break;
+   }
+
+   if (inchar != c) /* EOF */
+      break;
+
+   if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
+     break;
+ }
+
+ return 0;
+}
diff --git a/contrib/tools/pngfix.c b/contrib/tools/pngfix.c
new file mode 100644
index 0000000..7fde31c
--- /dev/null
+++ b/contrib/tools/pngfix.c
@@ -0,0 +1,4049 @@
+/* pngfix.c
+ *
+ * Copyright (c) 2014-2015 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.20 [December 3, 2015]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Tool to check and fix the zlib inflate 'too far back' problem.
+ * See the usage message for more information.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#define implies(x,y) assert(!(x) || (y))
+
+#ifdef __GNUC__
+   /* This is used to fix the error:
+    *
+    * pngfix.c:
+    * In function 'zlib_advance':
+    * pngfix.c:181:13: error: assuming signed overflow does not
+    *   occur when simplifying conditional to constant [-Werror=strict-overflow]
+    */
+#  define FIX_GCC volatile
+#else
+#  define FIX_GCC
+#endif
+
+#define PROGRAM_NAME "pngfix"
+
+/* Define the following to use this program against your installed libpng,
+ * rather than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+#  include <png.h>
+#else
+#  include "../../png.h"
+#endif
+
+#if PNG_LIBPNG_VER < 10603 /* 1.6.3 */
+#  error "pngfix will not work with libpng prior to 1.6.3"
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#include <setjmp.h>
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_EASY_ACCESS_SUPPORTED) &&\
+   (defined(PNG_READ_DEINTERLACE_SUPPORTED) ||\
+    defined(PNG_READ_INTERLACING_SUPPORTED))
+
+/* zlib.h defines the structure z_stream, an instance of which is included
+ * in this structure and is required for decompressing the LZ compressed
+ * data in PNG files.
+ */
+#ifndef ZLIB_CONST
+   /* We must ensure that zlib uses 'const' in declarations. */
+#  define ZLIB_CONST
+#endif
+#include <zlib.h>
+#ifdef const
+   /* zlib.h sometimes #defines const to nothing, undo this. */
+#  undef const
+#endif
+
+/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
+ * with older builds.
+ */
+#if ZLIB_VERNUM < 0x1260
+#  define PNGZ_MSG_CAST(s) constcast(char*,s)
+#  define PNGZ_INPUT_CAST(b) constcast(png_bytep,b)
+#else
+#  define PNGZ_MSG_CAST(s) (s)
+#  define PNGZ_INPUT_CAST(b) (b)
+#endif
+
+#ifndef PNG_MAXIMUM_INFLATE_WINDOW
+#  error "pngfix not supported in this libpng version"
+#endif
+
+#if ZLIB_VERNUM >= 0x1240
+
+/* Copied from pngpriv.h */
+#ifdef __cplusplus
+#  define voidcast(type, value) static_cast<type>(value)
+#  define constcast(type, value) const_cast<type>(value)
+#  define aligncast(type, value) \
+   static_cast<type>(static_cast<void*>(value))
+#  define aligncastconst(type, value) \
+   static_cast<type>(static_cast<const void*>(value))
+#else
+#  define voidcast(type, value) (value)
+#  define constcast(type, value) ((type)(value))
+#  define aligncast(type, value) ((void*)(value))
+#  define aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+#if PNG_LIBPNG_VER < 10700
+/* Chunk tags (copied from pngpriv.h) */
+#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
+#define PNG_U32(b1,b2,b3,b4) \
+   (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
+
+/* Constants for known chunk types. */
+#define png_IDAT PNG_U32( 73,  68,  65,  84)
+#define png_IEND PNG_U32( 73,  69,  78,  68)
+#define png_IHDR PNG_U32( 73,  72,  68,  82)
+#define png_PLTE PNG_U32( 80,  76,  84,  69)
+#define png_bKGD PNG_U32( 98,  75,  71,  68)
+#define png_cHRM PNG_U32( 99,  72,  82,  77)
+#define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
+#define png_gAMA PNG_U32(103,  65,  77,  65)
+#define png_gIFg PNG_U32(103,  73,  70, 103)
+#define png_gIFt PNG_U32(103,  73,  70, 116) /* deprecated */
+#define png_gIFx PNG_U32(103,  73,  70, 120)
+#define png_hIST PNG_U32(104,  73,  83,  84)
+#define png_iCCP PNG_U32(105,  67,  67,  80)
+#define png_iTXt PNG_U32(105,  84,  88, 116)
+#define png_oFFs PNG_U32(111,  70,  70, 115)
+#define png_pCAL PNG_U32(112,  67,  65,  76)
+#define png_pHYs PNG_U32(112,  72,  89, 115)
+#define png_sBIT PNG_U32(115,  66,  73,  84)
+#define png_sCAL PNG_U32(115,  67,  65,  76)
+#define png_sPLT PNG_U32(115,  80,  76,  84)
+#define png_sRGB PNG_U32(115,  82,  71,  66)
+#define png_sTER PNG_U32(115,  84,  69,  82)
+#define png_tEXt PNG_U32(116,  69,  88, 116)
+#define png_tIME PNG_U32(116,  73,  77,  69)
+#define png_tRNS PNG_U32(116,  82,  78,  83)
+#define png_zTXt PNG_U32(122,  84,  88, 116)
+#endif
+
+/* The 8-byte signature as a pair of 32-bit quantities */
+#define sig1 PNG_U32(137,  80,  78,  71)
+#define sig2 PNG_U32( 13,  10,  26,  10)
+
+/* Is the chunk critical? */
+#define CRITICAL(chunk) (((chunk) & PNG_U32(32,0,0,0)) == 0)
+
+/* Is it safe to copy? */
+#define SAFE_TO_COPY(chunk) (((chunk) & PNG_U32(0,0,0,32)) != 0)
+
+/* Fix ups for builds with limited read support */
+#ifndef PNG_ERROR_TEXT_SUPPORTED
+#  define png_error(a,b) png_err(a)
+#endif
+
+/********************************* UTILITIES **********************************/
+/* UNREACHED is a value to cause an assert to fail. Because of the way the
+ * assert macro is written the string "UNREACHED" is produced in the error
+ * message.
+ */
+#define UNREACHED 0
+
+/* 80-bit number handling - a PNG image can be up to (2^31-1)x(2^31-1) 8-byte
+ * (16-bit RGBA) pixels in size; that's less than 2^65 bytes or 2^68 bits, so
+ * arithmetic of 80-bit numbers is sufficient.  This representation uses an
+ * arbitrary length array of png_uint_16 digits (0..65535).  The representation
+ * is little endian.
+ *
+ * The arithmetic functions take zero to two uarb values together with the
+ * number of digits in those values and write the result to the given uarb
+ * (always the first argument) returning the number of digits in the result.
+ * If the result is negative the return value is also negative (this would
+ * normally be an error).
+ */
+typedef png_uint_16  udigit; /* A 'unum' is an array of these */
+typedef png_uint_16p uarb;
+typedef png_const_uint_16p uarbc;
+
+#define UDIGITS(unum) ((sizeof unum)/(sizeof (udigit))
+   /* IMPORTANT: only apply this to an array, applied to a pointer the result
+    * will typically be '2', which is not useful.
+    */
+
+static int
+uarb_set(uarb result, png_alloc_size_t val)
+   /* Set (initialize) 'result' to 'val'.  The size required for 'result' must
+    * be determined by the caller from a knowledge of the maximum for 'val'.
+    */
+{
+   int ndigits = 0;
+
+   while (val > 0)
+   {
+      result[ndigits++] = (png_uint_16)(val & 0xffff);
+      val >>= 16;
+   }
+
+   return ndigits;
+}
+
+static int
+uarb_copy(uarb to, uarb from, int idigits)
+   /* Copy a uarb, may reduce the digit count */
+{
+   int d, odigits;
+
+   for (d=odigits=0; d<idigits; ++d)
+      if ((to[d] = from[d]) != 0)
+         odigits = d+1;
+
+   return odigits;
+}
+
+static int
+uarb_inc(uarb num, int in_digits, png_int_32 add)
+   /* This is a signed 32-bit add, except that to avoid overflow the value added
+    * or subtracted must be no more than 2^31-65536.  A negative result
+    * indicates a negative number (which is an error below).  The size of
+    * 'num' should be max(in_digits+1,2) for arbitrary 'add' but can be just
+    * in_digits+1 if add is known to be in the range -65535..65535.
+    */
+{
+   FIX_GCC int out_digits = 0;
+
+   while (out_digits < in_digits)
+   {
+      add += num[out_digits];
+      num[out_digits++] = (png_uint_16)(add & 0xffff);
+      add >>= 16;
+   }
+
+   while (add != 0 && add != (-1))
+   {
+      num[out_digits++] = (png_uint_16)(add & 0xffff);
+      add >>= 16;
+   }
+
+   if (add == 0)
+   {
+      while (out_digits > 0 && num[out_digits-1] == 0)
+         --out_digits;
+      return out_digits; /* may be 0 */
+   }
+
+   else /* negative result */
+   {
+      while (out_digits > 1 && num[out_digits-1] == 0xffff)
+         --out_digits;
+
+      return -out_digits;
+   }
+}
+
+static int
+uarb_add32(uarb num, int in_digits, png_uint_32 add)
+   /* As above but this works with any 32-bit value and only does 'add' */
+{
+   if (in_digits > 0)
+   {
+      in_digits = uarb_inc(num, in_digits, add & 0xffff);
+      return uarb_inc(num+1, in_digits-1, add >> 16)+1;
+   }
+
+   return uarb_set(num, add);
+}
+
+static int
+uarb_mult_digit(uarb acc, int a_digits, uarb num, FIX_GCC int n_digits,
+   png_uint_16 val)
+   /* Primitive one-digit multiply - 'val' must be 0..65535. Note that this
+    * primitive is a multiply and accumulate - the result of *num * val is added
+    * to *acc.
+    *
+    * This is a one-digit multiply, so the product may be up to one digit longer
+    * than 'num', however the add to 'acc' means that the caller must ensure
+    * that 'acc' is at least one digit longer than this *and* at least one digit
+    * longer than the current length of 'acc'.  (Or the caller must otherwise
+    * ensure 'adigits' is adequate from knowledge of the values.)
+    */
+{
+   /* The digits in *acc, *num and val are in the range 0..65535, so the
+    * result below is at most (65535*65535)+2*65635 = 65535*(65535+2), which is
+    * exactly 0xffffffff.
+    */
+   if (val > 0 && n_digits > 0) /* Else the product is 0 */
+   {
+      png_uint_32 carry = 0;
+      int out_digits = 0;
+
+      while (out_digits < n_digits || carry > 0)
+      {
+         if (out_digits < a_digits)
+            carry += acc[out_digits];
+
+         if (out_digits < n_digits)
+            carry += (png_uint_32)num[out_digits] * val;
+
+         acc[out_digits++] = (png_uint_16)(carry & 0xffff);
+         carry >>= 16;
+      }
+
+      /* So carry is 0 and all the input digits have been consumed. This means
+       * that it is possible to skip any remaining digits in acc.
+       */
+      if (out_digits > a_digits)
+         return out_digits;
+   }
+
+   return a_digits;
+}
+
+static int
+uarb_mult32(uarb acc, int a_digits, uarb num, int n_digits, png_uint_32 val)
+   /* calculate acc += num * val, 'val' may be any 32-bit value, 'acc' and 'num'
+    * may be any value, returns the number of digits in 'acc'.
+    */
+{
+   if (n_digits > 0 && val > 0)
+   {
+      a_digits = uarb_mult_digit(acc, a_digits, num, n_digits,
+         (png_uint_16)(val & 0xffff));
+
+      /* Because n_digits and val are >0 the following must be true: */
+      assert(a_digits > 0);
+
+      val >>= 16;
+      if (val > 0)
+         a_digits = uarb_mult_digit(acc+1, a_digits-1, num, n_digits,
+            (png_uint_16)val) + 1;
+   }
+
+   return a_digits;
+}
+
+static int
+uarb_shift(uarb inout, int ndigits, unsigned int right_shift)
+   /* Shift inout right by right_shift bits, right_shift must be in the range
+    * 1..15
+    */
+{
+   FIX_GCC int i = ndigits;
+   png_uint_16 carry = 0;
+
+   assert(right_shift >= 1 && right_shift <= 15);
+
+   while (--i >= 0)
+   {
+      png_uint_16 temp = (png_uint_16)(carry | (inout[i] >> right_shift));
+
+      /* Bottom bits to top bits of carry */
+      carry = (png_uint_16)((inout[i] << (16-right_shift)) & 0xffff);
+
+      inout[i] = temp;
+
+      /* The shift may reduce ndigits */
+      if (i == ndigits-1 && temp == 0)
+         ndigits = i;
+   }
+
+   return ndigits;
+}
+
+static int
+uarb_cmp(uarb a, int adigits, uarb b, int bdigits)
+   /* Return -1/0/+1 according as a<b/a==b/a>b */
+{
+   if (adigits < bdigits)
+      return -1;
+
+   if (adigits > bdigits)
+      return 1;
+
+   while (adigits-- > 0)
+      if (a[adigits] < b[adigits])
+         return -1;
+
+      else if (a[adigits] > b[adigits])
+         return 1;
+
+   return 0;
+}
+
+#if 0 /*UNUSED*/
+static int
+uarb_eq32(uarb num, int digits, png_uint_32 val)
+   /* Return true if the uarb is equal to 'val' */
+{
+   switch (digits)
+   {
+      case 0:  return val == 0;
+      case 1:  return val == num[0];
+      case 2:  return (val & 0xffff) == num[0] && (val >> 16) == num[1];
+      default: return 0;
+   }
+}
+#endif
+
+static void
+uarb_printx(uarb num, int digits, FILE *out)
+   /* Print 'num' as a hexadecimal number (easier than decimal!) */
+{
+   while (digits > 0)
+      if (num[--digits] > 0)
+      {
+         fprintf(out, "0x%x", num[digits]);
+
+         while (digits > 0)
+            fprintf(out, "%.4x", num[--digits]);
+      }
+
+      else if (digits == 0) /* the number is 0 */
+         fputs("0x0", out);
+}
+
+static void
+uarb_print(uarb num, int digits, FILE *out)
+   /* Prints 'num' as a decimal if it will fit in an unsigned long, else as a
+    * hexadecimal number.  Notice that the results vary for images over 4GByte
+    * in a system dependent way, and the hexadecimal form doesn't work very well
+    * in awk script input.
+    *
+    *
+    * TODO: write uarb_div10
+    */
+{
+   if (digits * sizeof (udigit) > sizeof (unsigned long))
+      uarb_printx(num, digits, out);
+
+   else
+   {
+      unsigned long n = 0;
+
+      while (digits > 0)
+         n = (n << 16) + num[--digits];
+
+      fprintf(out, "%lu", n);
+   }
+}
+
+/* Generate random bytes.  This uses a boring repeatable algorithm and it
+ * is implemented here so that it gives the same set of numbers on every
+ * architecture.  It's a linear congruential generator (Knuth or Sedgewick
+ * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
+ * Hill, "The Art of Electronics" (Pseudo-Random Bit Sequences and Noise
+ * Generation.)
+ *
+ * (Copied from contrib/libtests/pngvalid.c)
+ */
+static void
+make_random_bytes(png_uint_32* seed, void* pv, size_t size)
+{
+   png_uint_32 u0 = seed[0], u1 = seed[1];
+   png_bytep bytes = voidcast(png_bytep, pv);
+
+   /* There are thirty-three bits; the next bit in the sequence is bit-33 XOR
+    * bit-20.  The top 1 bit is in u1, the bottom 32 are in u0.
+    */
+   size_t i;
+   for (i=0; i<size; ++i)
+   {
+      /* First generate 8 new bits then shift them in at the end. */
+      png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
+      u1 <<= 8;
+      u1 |= u0 >> 24;
+      u0 <<= 8;
+      u0 |= u;
+      *bytes++ = (png_byte)u;
+   }
+
+   seed[0] = u0;
+   seed[1] = u1;
+}
+
+/* Clear an object to a random value. */
+static void
+clear(void *pv, size_t size)
+{
+   static png_uint_32 clear_seed[2] = { 0x12345678, 0x9abcdef0 };
+   make_random_bytes(clear_seed, pv, size);
+}
+
+#define CLEAR(object) clear(&(object), sizeof (object))
+
+/* Copied from unreleased 1.7 code.
+ *
+ * CRC checking uses a local pre-built implementation of the Ethernet CRC32.
+ * This is to avoid a function call to the zlib DLL and to optimize the
+ * byte-by-byte case.
+ */
+static png_uint_32 crc_table[256] =
+{
+   0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+   0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+   0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+   0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+   0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+   0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+   0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+   0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+   0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+   0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+   0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+   0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+   0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+   0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+   0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+   0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+   0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+   0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+   0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+   0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+   0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+   0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+   0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+   0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+   0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+   0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+   0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+   0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+   0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+   0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+   0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+   0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+   0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+   0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+   0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+   0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+   0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+   0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+   0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+   0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+   0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+   0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+   0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+   0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+   0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+   0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+   0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+   0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+   0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+   0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+   0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+   0x2d02ef8d
+};
+
+/* The CRC calculated here *IS* conditioned, the corresponding value used by
+ * zlib and the result value is obtained by XORing with CRC_INIT, which is also
+ * the first value that must be passed in (for the first byte) to crc_one_byte.
+ */
+#define CRC_INIT 0xffffffff
+
+static png_uint_32
+crc_one_byte(png_uint_32 crc, int b)
+{
+   return crc_table[(crc ^ b) & 0xff] ^ (crc >> 8);
+}
+
+static png_uint_32
+crc_init_4(png_uint_32 value)
+{
+   /* This is an alternative to the algorithm used in zlib, which requires four
+    * separate tables to parallelize the four byte operations, it only works for
+    * a CRC of the first four bytes of the stream, but this is what happens in
+    * the parser below where length+chunk-name is read and chunk-name used to
+    * initialize the CRC.  Notice that the calculation here avoids repeated
+    * conditioning (xor with 0xffffffff) by storing the conditioned value.
+    */
+   png_uint_32 crc = crc_table[(~value >> 24)] ^ 0xffffff;
+
+   crc = crc_table[(crc ^ (value >> 16)) & 0xff] ^ (crc >> 8);
+   crc = crc_table[(crc ^ (value >> 8)) & 0xff] ^ (crc >> 8);
+   return crc_table[(crc ^ value) & 0xff] ^ (crc >> 8);
+}
+
+static int
+chunk_type_valid(png_uint_32 c)
+   /* Bit whacking approach to chunk name validation that is intended to avoid
+    * branches.  The cost is that it uses a lot of 32-bit constants, which might
+    * be bad on some architectures.
+    */
+{
+   png_uint_32 t;
+
+   /* Remove bit 5 from all but the reserved byte; this means every
+    * 8-bit unit must be in the range 65-90 to be valid.  So bit 5
+    * must be zero, bit 6 must be set and bit 7 zero.
+    */
+   c &= ~PNG_U32(32,32,0,32);
+   t = (c & ~0x1f1f1f1f) ^ 0x40404040;
+
+   /* Subtract 65 for each 8-bit quantity, this must not overflow
+    * and each byte must then be in the range 0-25.
+    */
+   c -= PNG_U32(65,65,65,65);
+   t |=c ;
+
+   /* Subtract 26, handling the overflow which should set the top
+    * three bits of each byte.
+    */
+   c -= PNG_U32(25,25,25,26);
+   t |= ~c;
+
+   return (t & 0xe0e0e0e0) == 0;
+}
+
+/**************************** CONTROL INFORMATION *****************************/
+
+/* Information about a sequence of IDAT chunks, the chunks have been re-synced
+ * using sync_stream below and the new lengths are recorded here.  Because the
+ * number of chunks is unlimited this is handled using a linked list of these
+ * structures.
+ */
+struct IDAT_list
+{
+   struct IDAT_list *next;     /* Linked list */
+   unsigned int      length;   /* Actual length of the array below */
+   unsigned int      count;    /* Number of entries that are valid */
+#     define IDAT_INIT_LENGTH 16
+   png_uint_32       lengths[IDAT_INIT_LENGTH];
+};
+
+static void
+IDAT_list_init(struct IDAT_list *list)
+{
+   CLEAR(*list);
+
+   list->next = NULL;
+   list->length = IDAT_INIT_LENGTH;
+}
+
+static size_t
+IDAT_list_size(struct IDAT_list *list, unsigned int length)
+   /* Return the size in bytes of an IDAT_list of the given length. */
+{
+   if (list != NULL)
+      length = list->length;
+
+   return sizeof *list - sizeof list->lengths +
+      length * sizeof list->lengths[0];
+}
+
+static void
+IDAT_list_end(struct IDAT_list *IDAT_list)
+{
+   struct IDAT_list *list = IDAT_list->next;
+
+   CLEAR(*IDAT_list);
+
+   while (list != NULL)
+   {
+      struct IDAT_list *next = list->next;
+
+      clear(list, IDAT_list_size(list, 0));
+      free(list);
+      list = next;
+   }
+}
+
+static struct IDAT_list *
+IDAT_list_extend(struct IDAT_list *tail)
+{
+   /* Use the previous cached value if available. */
+   struct IDAT_list *next = tail->next;
+
+   if (next == NULL)
+   {
+      /* Insert a new, malloc'ed, block of IDAT information buffers, this
+       * one twice as large as the previous one:
+       */
+      unsigned int length = 2 * tail->length;
+
+      if (length < tail->length) /* arithmetic overflow */
+         length = tail->length;
+
+      next = voidcast(IDAT_list*, malloc(IDAT_list_size(NULL, length)));
+      CLEAR(*next);
+
+      /* The caller must handle this: */
+      if (next == NULL)
+         return NULL;
+
+      next->next = NULL;
+      next->length = length;
+      tail->next = next;
+   }
+
+   return next;
+}
+
+/* GLOBAL CONTROL STRUCTURE */
+struct global
+{
+   /* PUBLIC GLOBAL VARIABLES: OWNER INITIALIZE */
+   unsigned int   errors        :1; /* print file errors to stderr */
+   unsigned int   warnings      :1; /* print libpng warnings to stderr */
+   unsigned int   optimize_zlib :1; /* Run optimization search */
+   unsigned int   quiet         :2; /* don't output summaries */
+   unsigned int   verbose       :3; /* various internal tracking */
+   unsigned int   skip          :3; /* Non-critical chunks to skip */
+#     define SKIP_NONE      0
+#     define SKIP_BAD_CRC   1    /* Chunks with a bad CRC */
+#     define SKIP_UNSAFE    2    /* Chunks not safe to copy */
+#     define SKIP_UNUSED    3    /* Chunks not used by libpng */
+#     define SKIP_TRANSFORM 4    /* Chunks only used in transforms */
+#     define SKIP_COLOR     5    /* Everything but tRNS, sBIT, gAMA and sRGB */
+#     define SKIP_ALL       6    /* Everything but tRNS and sBIT */
+
+   png_uint_32    idat_max;         /* 0 to perform no re-chunking */
+
+   int            status_code;      /* Accumulated status code */
+#     define TOO_FAR_BACK   0x01 /* found a too-far-back error */
+#     define CRC_ERROR      0x02 /* fixed an invalid CRC */
+#     define STREAM_ERROR   0x04 /* damaged PNG stream (may be fixable) */
+#     define TRUNCATED      0x08 /* truncated but still readable */
+#     define FILE_ERROR     0x10 /* could not read the file */
+#     define WRITE_ERROR    0x20 /* write error (this terminates the read) */
+#     define INTERNAL_ERROR 0x40 /* internal limits/errors encountered */
+
+   /* PUBLIC GLOBAL VARIABLES: USED INTERNALLY BY IDAT READ CODE */
+   struct IDAT_list idat_cache;  /* Cache of file IDAT information buffers */
+      /* The structure is shared across all uses of this global control
+       * structure to avoid reallocation between IDAT streams.
+       */
+};
+
+static int
+global_end(struct global *global)
+{
+
+   int rc;
+
+   IDAT_list_end(&global->idat_cache);
+   rc = global->status_code;
+   CLEAR(*global);
+   return rc;
+}
+
+static void
+global_init(struct global *global)
+   /* Call this once (and only once) to initialize the control */
+{
+   CLEAR(*global);
+
+   /* Globals */
+   global->errors        = 0;
+   global->warnings      = 0;
+   global->quiet         = 0;
+   global->verbose       = 0;
+   global->idat_max      = 0;         /* no re-chunking of IDAT */
+   global->optimize_zlib = 0;
+   global->skip          = SKIP_NONE;
+   global->status_code   = 0;
+
+   IDAT_list_init(&global->idat_cache);
+}
+
+static int
+skip_chunk_type(const struct global *global, png_uint_32 type)
+   /* Return true if this chunk is to be skipped according to the --strip
+    * option.  This code needs to recognize all known ancillary chunks in order
+    * to handle the --strip=unsafe option.
+    */
+{
+   /* Never strip critical chunks: */
+   if (CRITICAL(type))
+      return 0;
+
+   switch (type)
+   {
+      /* Chunks that are treated as, effectively, critical because they affect
+       * correct interpretation of the pixel values:
+       */
+      case png_tRNS: case png_sBIT:
+         return 0;
+
+      /* Chunks that specify gamma encoding which should therefore only be
+       * removed the the user insists:
+       */
+      case png_gAMA: case png_sRGB:
+         if (global->skip >= SKIP_ALL)
+            return 1;
+         return 0;
+
+      /* Chunks that affect color interpretation - not used by libpng and rarely
+       * used by applications, but technically still required for correct
+       * interpretation of the image data:
+       */
+      case png_cHRM: case png_iCCP:
+         if (global->skip >= SKIP_COLOR)
+            return 1;
+         return 0;
+
+      /* Other chunks that are used by libpng in image transformations (as
+       * opposed to known chunks that have get/set APIs but are not otherwise
+       * used.)
+       */
+      case png_bKGD:
+         if (global->skip >= SKIP_TRANSFORM)
+            return 1;
+         return 0;
+
+      /* All other chunks that libpng knows about and affect neither image
+       * interpretation nor libpng transforms - chunks that are effectively
+       * unused by libpng even though libpng might recognize and store them.
+       */
+      case png_fRAc: case png_gIFg: case png_gIFt: case png_gIFx: case png_hIST:
+      case png_iTXt: case png_oFFs: case png_pCAL: case png_pHYs: case png_sCAL:
+      case png_sPLT: case png_sTER: case png_tEXt: case png_tIME: case png_zTXt:
+         if (global->skip >= SKIP_UNUSED)
+            return 1;
+         return 0;
+
+      /* Chunks that libpng does not know about (notice that this depends on the
+       * list above including all known chunks!)  The decision here depends on
+       * whether the safe-to-copy bit is set in the chunk type.
+       */
+      default:
+         if (SAFE_TO_COPY(type))
+         {
+            if (global->skip >= SKIP_UNUSED) /* as above */
+               return 1;
+         }
+
+         else if (global->skip >= SKIP_UNSAFE)
+            return 1;
+
+         return 0;
+   }
+}
+
+/* PER-FILE CONTROL STRUCTURE */
+struct chunk;
+struct IDAT;
+struct file
+{
+   /* ANCESTORS */
+   struct global *global;
+
+   /* PUBLIC PER-FILE VARIABLES: CALLER INITIALIZE */
+   const char *   file_name;
+   const char *   out_name;      /* Name of output file (if required) */
+
+   /* PUBLIC PER-FILE VARIABLES: SET BY PNG READ CODE */
+   /* File specific result codes */
+   int            status_code;   /* Set to a bit mask of the following: */
+   int            read_errno;    /* Records a read error errno */
+   int            write_errno;   /* Records a write error errno */
+
+   /* IHDR information */
+   png_uint_32    width;
+   png_uint_32    height;
+   png_byte       bit_depth;
+   png_byte       color_type;
+   png_byte       compression_method;
+   png_byte       filter_method;
+   png_byte       interlace_method;
+
+   udigit         image_bytes[5];
+   int            image_digits;
+
+   /* PROTECTED PER-FILE VARIABLES: USED BY THE READ CODE */
+   FILE *         file;          /* Original PNG file */
+   FILE *         out;           /* If a new one is being written */
+   jmp_buf        jmpbuf;        /* Set while reading a PNG */
+
+   /* PROTECTED CHUNK SPECIFIC VARIABLES: USED BY CHUNK CODE */
+   /* The following variables are used during reading to record the length, type
+    * and data position of the *next* chunk or, right at the start, the
+    * signature (in length,type).
+    *
+    * When a chunk control structure is instantiated these values are copied
+    * into the structure and can then be overritten with the data for the next
+    * chunk.
+    */
+   fpos_t         data_pos;      /* Position of first byte of chunk data */
+   png_uint_32    length;        /* First word (length or signature start) */
+   png_uint_32    type;          /* Second word (type or signature end) */
+   png_uint_32    crc;           /* Running chunk CRC (used by read_chunk) */
+
+   /* These counts are maintained by the read and write routines below and are
+    * reset by the chunk handling code.  They record the total number of bytes
+    * read or written for the chunk, including the header (length,type) bytes.
+    */
+   png_uint_32    read_count;    /* Count of bytes read (in the chunk) */
+   png_uint_32    write_count;   /* Count of bytes written (in the chunk) */
+   int            state;         /* As defined here: */
+#     define STATE_SIGNATURE  0  /* The signature is being written */
+#     define STATE_CHUNKS     1  /* Non-IDAT chunks are being written */
+#     define STATE_IDAT       2  /* An IDAT stream is being written */
+
+   /* Two pointers used to enable clean-up in the event of fatal errors and to
+    * hold state about the parser process (only one of each at present.)
+    */
+   struct chunk * chunk;
+   struct IDAT *  idat;
+
+   /* Interface to allocate a new chunk or IDAT control structure.  The result
+    * is returned by setting one or other of the above variables.  Note that the
+    * relevant initializer is called by the allocator function.  The alloc_ptr
+    * is used only by the implementation of the allocate function.
+    */
+   void *         alloc_ptr;
+   void         (*alloc)(struct file*,int idat);
+                                  /* idat: allocate IDAT not chunk */
+};
+
+/* Valid longjmp (stop) codes are: */
+#define LIBPNG_WARNING_CODE   1 /* generic png_error */
+#define LIBPNG_ERROR_CODE     2 /* generic png_error */
+#define ZLIB_ERROR_CODE       3 /* generic zlib error */
+#define INVALID_ERROR_CODE    4 /* detected an invalid PNG */
+#define READ_ERROR_CODE       5 /* read failed */
+#define WRITE_ERROR_CODE      6 /* error in write */
+#define UNEXPECTED_ERROR_CODE 7 /* unexpected (internal?) error */
+
+static void
+emit_string(const char *str, FILE *out)
+   /* Print a string with spaces replaced by '_' and non-printing characters by
+    * an octal escape.
+    */
+{
+   for (; *str; ++str)
+      if (isgraph(UCHAR_MAX & *str))
+         putc(*str, out);
+
+      else if (isspace(UCHAR_MAX & *str))
+         putc('_', out);
+
+      else
+         fprintf(out, "\\%.3o", *str);
+}
+
+static const char *
+strcode(int code)
+{
+   switch (code)
+   {
+      case LIBPNG_WARNING_CODE:   return "warning";
+      case LIBPNG_ERROR_CODE:     return "libpng";
+      case ZLIB_ERROR_CODE:       return "zlib";
+      case INVALID_ERROR_CODE:    return "invalid";
+      case READ_ERROR_CODE:       return "read";
+      case WRITE_ERROR_CODE:      return "write";
+      case UNEXPECTED_ERROR_CODE: return "unexpected";
+      default:                    return "INVALID";
+   }
+}
+
+static void
+emit_error(struct file *file, int code, const char *what)
+   /* Generic error message routine, takes a 'stop' code but can be used
+    * elsewhere.  Always outputs a message.
+    */
+{
+   const char *reason;
+   int err = 0;
+
+   switch (code)
+   {
+      case LIBPNG_WARNING_CODE:   reason = "libpng warning:"; break;
+      case LIBPNG_ERROR_CODE:     reason = "libpng error:"; break;
+      case ZLIB_ERROR_CODE:       reason = "zlib error:"; break;
+      case INVALID_ERROR_CODE:    reason = "invalid"; break;
+      case READ_ERROR_CODE:       reason = "read failure:";
+                                  err = file->read_errno;
+                                  break;
+      case WRITE_ERROR_CODE:      reason = "write error";
+                                  err = file->write_errno;
+                                  break;
+      case UNEXPECTED_ERROR_CODE: reason = "unexpected error:";
+                                  err = file->read_errno;
+                                  if (err == 0)
+                                     err = file->write_errno;
+                                  break;
+      default:                    reason = "INVALID (internal error):"; break;
+   }
+
+   if (err != 0)
+      fprintf(stderr, "%s: %s %s [%s]\n", file->file_name, reason, what,
+         strerror(err));
+
+   else
+      fprintf(stderr, "%s: %s %s\n", file->file_name, reason, what);
+}
+
+static void chunk_end(struct chunk **);
+static void IDAT_end(struct IDAT **);
+
+static int
+file_end(struct file *file)
+{
+   int rc;
+
+   /* If either of the chunk pointers are set end them here, the IDAT structure
+    * must be deallocated first as it may deallocate the chunk structure.
+    */
+   if (file->idat != NULL)
+      IDAT_end(&file->idat);
+
+   if (file->chunk != NULL)
+      chunk_end(&file->chunk);
+
+   rc = file->status_code;
+
+   if (file->file != NULL)
+      (void)fclose(file->file);
+
+   if (file->out != NULL)
+   {
+      /* NOTE: this is bitwise |, all the following functions must execute and
+       * must succeed.
+       */
+      if (ferror(file->out) | fflush(file->out) | fclose(file->out))
+      {
+         perror(file->out_name);
+         emit_error(file, READ_ERROR_CODE, "output write error");
+         rc |= WRITE_ERROR;
+      }
+   }
+
+   /* Accumulate the result codes */
+   file->global->status_code |= rc;
+
+   CLEAR(*file);
+
+   return rc; /* status code: non-zero on read or write error */
+}
+
+static int
+file_init(struct file *file, struct global *global, const char *file_name,
+   const char *out_name, void *alloc_ptr, void (*alloc)(struct file*,int))
+   /* Initialize a file control structure.  This will open the given files as
+    * well.  The status code returned is 0 on success, non zero (using the flags
+    * above) on a file open error.
+    */
+{
+   CLEAR(*file);
+   file->global = global;
+
+   file->file_name = file_name;
+   file->out_name = out_name;
+   file->status_code = 0;
+   file->read_errno = 0;
+   file->write_errno = 0;
+
+   file->file = NULL;
+   file->out = NULL;
+   /* jmpbuf is garbage: must be set by read_png */
+
+   file->read_count = 0;
+   file->state = STATE_SIGNATURE;
+
+   file->chunk = NULL;
+   file->idat = NULL;
+
+   file->alloc_ptr = alloc_ptr;
+   file->alloc = alloc;
+
+   /* Open the files: */
+   assert(file_name != NULL);
+   file->file = fopen(file_name, "rb");
+
+   if (file->file == NULL)
+   {
+      file->read_errno = errno;
+      file->status_code |= FILE_ERROR;
+      /* Always output: please give a readable file! */
+      perror(file_name);
+      return FILE_ERROR;
+   }
+
+   if (out_name != NULL)
+   {
+      file->out = fopen(out_name, "wb");
+
+      if (file->out == NULL)
+      {
+         file->write_errno = errno;
+         file->status_code |= WRITE_ERROR;
+         perror(out_name);
+         return WRITE_ERROR;
+      }
+   }
+
+   return 0;
+}
+
+static void
+log_error(struct file *file, int code, const char *what)
+   /* Like emit_error but checks the global 'errors' flag */
+{
+   if (file->global->errors)
+      emit_error(file, code, what);
+}
+
+static char
+type_char(png_uint_32 v)
+{
+   /* In fact because chunk::chunk_type is validated prior to any call to this
+    * function it will always return a-zA-Z, but the extra codes are just there
+    * to help in finding internal (programming) errors.  Note that the code only
+    * ever considers the low 7 bits of the value (so it is not necessary for the
+    * type_name function to mask of the byte.)
+    */
+   if (v & 32)
+      return "!abcdefghijklmnopqrstuvwxyz56789"[(v-96)&31];
+
+   else
+      return "@ABCDEFGHIJKLMNOPQRSTUVWXYZ01234"[(v-64)&31];
+}
+
+static void
+type_name(png_uint_32 type, FILE *out)
+{
+   putc(type_char(type >> 24), out);
+   putc(type_char(type >> 16), out);
+   putc(type_char(type >>  8), out);
+   putc(type_char(type      ), out);
+}
+
+static void
+type_sep(FILE *out)
+{
+   putc(':', out);
+   putc(' ', out);
+}
+
+static png_uint_32 current_type(struct file *file, int code);
+
+PNG_NORETURN static void
+stop(struct file *file, int code, const char *what)
+   /* Return control when a PNG file cannot be read. This outputs an 'ERR'
+    * summary line too.
+    */
+{
+   log_error(file, code, what);
+
+   /* The chunk being read is typically identified by file->chunk or, if this is
+    * NULL, by file->type.  This may be wrong if libpng reads ahead, but this
+    * only happens with IDAT where libpng reads the header then jumps around
+    * finding errors in the previous chunks.  We know that is happening because
+    * we are at the start of the IDAT (i.e. no IDAT data has yet been written.)
+    *
+    * SUMMARY FORMAT (stop):
+    *
+    * IDAT ERR status code read-errno write-errno message file
+    *
+    * 'uncompressed' will be 0 if there was a problem in the IHDR.  The errno
+    * values are emit_string(strerror(errno)).
+    */
+   if (file->global->quiet < 2) /* need two quiets to stop this. */
+   {
+      png_uint_32 type;
+
+      if (file->chunk != NULL)
+         type = current_type(file, code); /* Gropes in struct chunk and IDAT */
+
+      else
+         type = file->type;
+
+      if (type)
+         type_name(type, stdout);
+
+      else /* magic: an IDAT header, produces bogons for too many IDATs */
+         fputs("HEAD", stdout); /* not a registered chunk! */
+
+      printf(" ERR %.2x %s ", file->status_code, strcode(code));
+      /* This only works one strerror at a time, because of the way strerror is
+       * implemented.
+       */
+      emit_string(strerror(file->read_errno), stdout);
+      putc(' ', stdout);
+      emit_string(strerror(file->write_errno), stdout);
+      putc(' ', stdout);
+      emit_string(what, stdout);
+      putc(' ', stdout);
+      fputs(file->file_name, stdout);
+      putc('\n', stdout);
+   }
+
+   file->status_code |= FILE_ERROR;
+   longjmp(file->jmpbuf, code);
+}
+
+PNG_NORETURN static void
+stop_invalid(struct file *file, const char *what)
+{
+   stop(file, INVALID_ERROR_CODE, what);
+}
+
+static void
+type_message(struct file *file, png_uint_32 type, const char *what)
+   /* Error message for a chunk; the chunk name comes from 'type' */
+{
+   if (file->global->errors)
+   {
+      fputs(file->file_name, stderr);
+      type_sep(stderr);
+      type_name(type, stderr);
+      type_sep(stderr);
+      fputs(what, stderr);
+      putc('\n', stderr);
+   }
+}
+
+/* Input file positioning - we jump around in the input file while reading
+ * stuff, these wrappers deal with the error handling.
+ */
+static void
+file_getpos(struct file *file, fpos_t *pos)
+{
+   if (fgetpos(file->file, pos))
+   {
+      /* This is unexpected, so perror it */
+      perror(file->file_name);
+      stop(file, READ_ERROR_CODE, "fgetpos");
+   }
+}
+
+static void
+file_setpos(struct file *file, const fpos_t *pos)
+{
+   if (fsetpos(file->file, pos))
+   {
+      perror(file->file_name);
+      stop(file, READ_ERROR_CODE, "fsetpos");
+   }
+}
+
+static void
+getpos(struct file *file)
+   /* Get the current position and store it in 'data_pos'.  The corresponding
+    * setpos() function is chunk specific because it uses the copy of the
+    * position for the specific chunk.
+    */
+{
+   file_getpos(file, &file->data_pos);
+}
+
+
+/* Read utility - read a single byte, returns a value in the range 0..255 or EOF
+ * on a read error.  In the latter case status_code and read_errno are updated
+ * appropriately.
+ */
+static int
+read_byte(struct file *file)
+{
+   int ch = getc(file->file);
+
+   if (ch >= 0 && ch <= 255)
+   {
+      ++(file->read_count);
+      return ch;
+   }
+
+   else if (ch != EOF)
+   {
+      file->status_code |= INTERNAL_ERROR;
+      file->read_errno = ERANGE; /* out of range character */
+
+      /* This is very unexpected; an error message is always output: */
+      emit_error(file, UNEXPECTED_ERROR_CODE, "file read");
+   }
+
+#  ifdef EINTR
+      else if (errno == EINTR) /* Interrupted, try again */
+      {
+         errno = 0;
+         return read_byte(file);
+      }
+#  endif
+
+   else
+   {
+      /* An error, it doesn't really matter what the error is but it gets
+       * recorded anyway.
+       */
+      if (ferror(file->file))
+         file->read_errno = errno;
+
+      else if (feof(file->file))
+         file->read_errno = 0; /* I.e. a regular EOF, no error */
+
+      else /* unexpected */
+         file->read_errno = EDOM;
+   }
+
+   /* 'TRUNCATED' is used for all cases of failure to read a byte, because of
+    * the way libpng works a byte read is never attempted unless the byte is
+    * expected to be there, so EOF should not occur.
+    */
+   file->status_code |= TRUNCATED;
+   return EOF;
+}
+
+static png_byte
+reread_byte(struct file *file)
+   /* Read a byte when an error is not expected to happen because the byte has
+    * been read before without error.
+    */
+{
+   int ch = getc(file->file);
+
+   if (errno != 0)
+      file->read_errno = errno;
+
+   if (ch < 0 || ch > 255)
+      stop(file, UNEXPECTED_ERROR_CODE, "reread");
+
+   return (png_byte)ch;
+}
+
+static png_uint_32
+reread_4(struct file *file)
+   /* The same but for a four byte quantity */
+{
+   png_uint_32 result = 0;
+   int i = 0;
+
+   while (++i <= 4)
+      result = (result << 8) + reread_byte(file);
+
+   return result;
+}
+
+static void
+skip_12(struct file *file)
+   /* Skip exactly 12 bytes in the input stream - used to skip a CRC and chunk
+    * header that has been read before.
+    */
+{
+   /* Since the chunks were read before this shouldn't fail: */
+   if (fseek(file->file, 12, SEEK_CUR) != 0)
+   {
+      if (errno != 0)
+         file->read_errno = errno;
+
+      stop(file, UNEXPECTED_ERROR_CODE, "reskip");
+   }
+}
+
+static void
+write_byte(struct file *file, int b)
+   /* Write one byte to the output - this causes a fatal error if the write
+    * fails and the read of this PNG file immediately terminates.  Just
+    * increments the write count if there is no output file.
+    */
+{
+   if (file->out != NULL)
+   {
+      if (putc(b, file->out) != b)
+      {
+         file->write_errno = errno;
+         file->status_code |= WRITE_ERROR;
+         stop(file, WRITE_ERROR_CODE, "write byte");
+      }
+   }
+
+   ++(file->write_count);
+}
+
+/* Derivatives of the read/write functions. */
+static unsigned int
+read_4(struct file *file, png_uint_32 *pu)
+   /* Read four bytes, returns the number of bytes read successfully and, if all
+    * four bytes are read, assigns the result to *pu.
+    */
+{
+   unsigned int i = 0;
+   png_uint_32 val = 0;
+
+   do
+   {
+      int ch = read_byte(file);
+
+      if (ch == EOF)
+         return i;
+
+      val = (val << 8) + ch;
+   } while (++i < 4);
+
+   *pu = val;
+   return i;
+}
+
+/* CRC handling - read but calculate the CRC while doing so. */
+static int
+crc_read_many(struct file *file, png_uint_32 length)
+   /* Reads 'length' bytes and updates the CRC, returns true on success, false
+    * if the input is truncated.
+    */
+{
+   if (length > 0)
+   {
+      png_uint_32 crc = file->crc;
+
+      do
+      {
+         int ch = read_byte(file);
+
+         if (ch == EOF)
+            return 0; /* Truncated */
+
+         crc = crc_one_byte(crc, ch);
+      }
+      while (--length > 0);
+
+      file->crc = crc;
+   }
+
+   return 1; /* OK */
+}
+
+static int
+calc_image_size(struct file *file)
+   /* Fill in the image_bytes field given the IHDR information, calls stop on
+    * error.
+    */
+{
+   png_uint_16 pd = file->bit_depth;
+
+   switch (file->color_type)
+   {
+      default:
+         stop_invalid(file, "IHDR: colour type");
+
+      invalid_bit_depth:
+         stop_invalid(file, "IHDR: bit depth");
+
+      case 0: /* g */
+         if (pd != 1 && pd != 2 && pd != 4 && pd != 8 && pd != 16)
+            goto invalid_bit_depth;
+         break;
+
+      case 3:
+         if (pd != 1 && pd != 2 && pd != 4 && pd != 8)
+            goto invalid_bit_depth;
+         break;
+
+      case 2: /* rgb */
+         if (pd != 8 && pd != 16)
+            goto invalid_bit_depth;
+
+         pd = (png_uint_16)(pd * 3);
+         break;
+
+      case 4: /* ga */
+         if (pd != 8 && pd != 16)
+            goto invalid_bit_depth;
+
+         pd = (png_uint_16)(pd * 2);
+         break;
+
+      case 6: /* rgba */
+         if (pd != 8 && pd != 16)
+            goto invalid_bit_depth;
+
+         pd = (png_uint_16)(pd * 4);
+         break;
+   }
+
+   if (file->width < 1 || file->width > 0x7fffffff)
+      stop_invalid(file, "IHDR: width");
+
+   else if (file->height < 1 || file->height > 0x7fffffff)
+      stop_invalid(file, "IHDR: height");
+
+   else if (file->compression_method != 0)
+      stop_invalid(file, "IHDR: compression method");
+
+   else if (file->filter_method != 0)
+      stop_invalid(file, "IHDR: filter method");
+
+   else switch (file->interlace_method)
+   {
+      case PNG_INTERLACE_ADAM7:
+         /* Interlacing makes the image larger because of the replication of
+          * both the filter byte and the padding to a byte boundary.
+          */
+         {
+            int pass;
+            int image_digits = 0;
+            udigit row_width[2], row_bytes[3];
+
+            for (pass=0; pass<=6; ++pass)
+            {
+               png_uint_32 pw = PNG_PASS_COLS(file->width, pass);
+
+               if (pw > 0)
+               {
+                  int  digits;
+
+                  /* calculate 1+((pw*pd+7)>>3) in row_bytes */
+                  digits = uarb_mult_digit(row_bytes, uarb_set(row_bytes, 7),
+                     row_width, uarb_set(row_width, pw), pd);
+                  digits = uarb_shift(row_bytes, digits, 3);
+                  digits = uarb_inc(row_bytes, digits, 1);
+
+                  /* Add row_bytes * pass-height to the file image_bytes field
+                   */
+                  image_digits = uarb_mult32(file->image_bytes, image_digits,
+                     row_bytes, digits,
+                     PNG_PASS_ROWS(file->height, pass));
+               }
+            }
+
+            file->image_digits = image_digits;
+         }
+         break;
+
+      case PNG_INTERLACE_NONE:
+         {
+            int  digits;
+            udigit row_width[2], row_bytes[3];
+
+            /* As above, but use image_width in place of the pass width: */
+            digits = uarb_mult_digit(row_bytes, uarb_set(row_bytes, 7),
+               row_width, uarb_set(row_width, file->width), pd);
+            digits = uarb_shift(row_bytes, digits, 3);
+            digits = uarb_inc(row_bytes, digits, 1);
+
+            /* Set row_bytes * image-height to the file image_bytes field */
+            file->image_digits = uarb_mult32(file->image_bytes, 0,
+               row_bytes, digits, file->height);
+         }
+         break;
+
+      default:
+         stop_invalid(file, "IHDR: interlace method");
+   }
+
+   assert(file->image_digits >= 1 && file->image_digits <= 5);
+   return 1;
+}
+
+/* PER-CHUNK CONTROL STRUCTURE
+ * This structure is instantiated for each chunk, except for the IDAT chunks
+ * where one chunk control structure is used for the whole of a single stream of
+ * IDAT chunks (see the IDAT control structure below).
+ */
+struct chunk
+{
+   /* ANCESTORS */
+   struct file *         file;
+   struct global *       global;
+
+   /* PUBLIC IDAT INFORMATION: SET BY THE ZLIB CODE */
+   udigit         uncompressed_bytes[5];
+   int            uncompressed_digits;
+   udigit         compressed_bytes[5];
+   int            compressed_digits;
+
+   /* PUBLIC PER-CHUNK INFORMATION: USED BY CHUNK READ CODE */
+   /* This information is filled in by chunk_init from the data in the file
+    * control structure, but chunk_length may be changed later.
+    */
+   fpos_t         chunk_data_pos;    /* Position of first byte of chunk data */
+   png_uint_32    chunk_length;      /* From header (or modified below) */
+   png_uint_32    chunk_type;        /* From header */
+
+   /* PUBLIC PER-CHUNK INFORMATION: FOR THE CHUNK WRITE CODE */
+   png_uint_32    write_crc;         /* Output CRC (may differ from read_crc) */
+   png_uint_32    rewrite_offset;    /* Count of bytes before rewrite. */
+   int            rewrite_length;    /* Number of bytes left to change */
+   png_byte       rewrite_buffer[2]; /* Buffer of new byte values */
+};
+
+static void
+chunk_message(struct chunk *chunk, const char *message)
+{
+   type_message(chunk->file, chunk->chunk_type, message);
+}
+
+static void
+chunk_end(struct chunk **chunk_var)
+{
+   struct chunk *chunk = *chunk_var;
+
+   *chunk_var = NULL;
+   CLEAR(*chunk);
+}
+
+static void
+chunk_init(struct chunk * const chunk, struct file * const file)
+   /* When a chunk is initialized the file length/type/pos are copied into the
+    * corresponding chunk fields and the new chunk is registered in the file
+    * structure.  There can only be one chunk at a time.
+    *
+    * NOTE: this routine must onely be called from the file alloc routine!
+    */
+{
+   assert(file->chunk == NULL);
+
+   CLEAR(*chunk);
+
+   chunk->file = file;
+   chunk->global = file->global;
+
+   chunk->chunk_data_pos = file->data_pos;
+   chunk->chunk_length = file->length;
+   chunk->chunk_type = file->type;
+
+   /* Compresssed/uncompressed size information (from the zlib control structure
+    * that is used to check the compressed data in a chunk.)
+    */
+   chunk->uncompressed_digits = 0;
+   chunk->compressed_digits = 0;
+
+   file->chunk = chunk;
+}
+
+static png_uint_32
+current_type(struct file *file, int code)
+   /* Guess the actual chunk type that causes a stop() */
+{
+   /* This may return png_IDAT for errors detected (late) in the header; that
+    * includes any inter-chunk consistency check that libpng performs.  Assume
+    * that if the chunk_type is png_IDAT and the file write count is 8 this is
+    * what is happening.
+    */
+   if (file->chunk != NULL)
+   {
+      png_uint_32 type = file->chunk->chunk_type;
+
+      /* This is probably wrong for the excess IDATs case, because then libpng
+       * whines about too many of them (apparently in some cases erroneously)
+       * when the header is read.
+       */
+      if (code <= LIBPNG_ERROR_CODE && type == png_IDAT &&
+         file->write_count == 8)
+         type = 0; /* magic */
+
+      return type;
+   }
+
+   else
+      return file->type;
+}
+
+static void
+setpos(struct chunk *chunk)
+   /* Reset the position to 'chunk_data_pos' - the start of the data for this
+    * chunk.  As a side effect the read_count in the file is reset to 8, just
+    * after the length/type header.
+    */
+{
+   chunk->file->read_count = 8;
+   file_setpos(chunk->file, &chunk->chunk_data_pos);
+}
+
+/* Specific chunk handling - called for each chunk header, all special chunk
+ * processing is initiated in these functions.
+ */
+/* The next functions handle special processing for those chunks with LZ data,
+ * the data is identified and checked for validity.  If there are problems which
+ * cannot be corrected the routines return false, otherwise true (although
+ * modification to the zlib header may be required.)
+ *
+ * The compressed data is in zlib format (RFC1950) and consequently has a
+ * minimum length of 7 bytes.
+ */
+static int zlib_check(struct file *file, png_uint_32 offset);
+
+static int
+process_zTXt_iCCP(struct file *file)
+   /* zTXt and iCCP have exactly the same form - keyword, null, compression
+    * method then compressed data.
+    */
+{
+   struct chunk *chunk = file->chunk;
+   png_uint_32 length;
+   png_uint_32 index = 0;
+
+   assert(chunk != NULL && file->idat == NULL);
+   length = chunk->chunk_length;
+   setpos(chunk);
+
+   while (length >= 9)
+   {
+      --length;
+      ++index;
+      if (reread_byte(file) == 0) /* keyword null terminator */
+      {
+         --length;
+         ++index;
+         (void)reread_byte(file); /* compression method */
+         return zlib_check(file, index);
+      }
+   }
+
+   chunk_message(chunk, "too short");
+   return 0; /* skip */
+}
+
+static int
+process_iTXt(struct file *file)
+{
+   /* Like zTXt but more fields. */
+   struct chunk *chunk = file->chunk;
+   png_uint_32 length;
+   png_uint_32 index = 0;
+
+   assert(chunk != NULL && file->idat == NULL);
+   length = chunk->chunk_length;
+   setpos(chunk);
+
+   while (length >= 5)
+   {
+      --length;
+      ++index;
+      if (reread_byte(file) == 0) /* keyword null terminator */
+      {
+         --length;
+         ++index;
+         if (reread_byte(file) == 0) /* uncompressed text */
+            return 1; /* nothing to check */
+
+         --length;
+         ++index;
+         (void)reread_byte(file); /* compression method */
+
+         /* Skip the language tag (null terminated). */
+         while (length >= 9)
+         {
+            --length;
+            ++index;
+            if (reread_byte(file) == 0) /* terminator */
+            {
+               /* Skip the translated keyword */
+               while (length >= 8)
+               {
+                  --length;
+                  ++index;
+                  if (reread_byte(file) == 0) /* terminator */
+                     return zlib_check(file, index);
+               }
+            }
+         }
+
+         /* Ran out of bytes in the compressed case. */
+         break;
+      }
+   }
+
+   log_error(file, INVALID_ERROR_CODE, "iTXt chunk length");
+
+   return 0; /* skip */
+}
+
+/* IDAT READ/WRITE CONTROL STRUCTURE */
+struct IDAT
+{
+   /* ANCESTORS */
+   struct file *         file;
+   struct global *       global;
+
+   /* PROTECTED IDAT INFORMATION: SET BY THE IDAT READ CODE */
+   struct IDAT_list *idat_list_head; /* START of the list of IDAT information */
+   struct IDAT_list *idat_list_tail; /* *END* of the list of IDAT information */
+
+   /* PROTECTED IDAT INFORMATION: USED BY THE IDAT WRITE CODE */
+   struct IDAT_list *idat_cur;       /* Current list entry */
+   unsigned int      idat_count;     /* And the *current* index into the list */
+   png_uint_32       idat_index;     /* Index of *next* input byte to write */
+   png_uint_32       idat_length;    /* Cache of current chunk length */
+};
+
+/* NOTE: there is currently no IDAT_reset, so a stream cannot contain more than
+ * one IDAT sequence (i.e. MNG is not supported).
+ */
+
+static void
+IDAT_end(struct IDAT **idat_var)
+{
+   struct IDAT *idat = *idat_var;
+   struct file *file = idat->file;
+
+   *idat_var = NULL;
+
+   CLEAR(*idat);
+
+   assert(file->chunk != NULL);
+   chunk_end(&file->chunk);
+
+   /* Regardless of why the IDAT was killed set the state back to CHUNKS (it may
+    * already be CHUNKS because the state isn't changed until process_IDAT
+    * returns; a stop will cause IDAT_end to be entered in state CHUNKS!)
+    */
+   file->state = STATE_CHUNKS;
+}
+
+static void
+IDAT_init(struct IDAT * const idat, struct file * const file)
+   /* When the chunk is png_IDAT instantiate an IDAT control structure in place
+    * of a chunk control structure.  The IDAT will instantiate a chunk control
+    * structure using the file alloc routine.
+    *
+    * NOTE: this routine must only be called from the file alloc routine!
+    */
+{
+   assert(file->chunk == NULL);
+   assert(file->idat == NULL);
+
+   CLEAR(*idat);
+
+   idat->file = file;
+   idat->global = file->global;
+
+   /* Initialize the tail to the pre-allocated buffer and set the count to 0
+    * (empty.)
+    */
+   idat->global->idat_cache.count = 0;
+   idat->idat_list_head = idat->idat_list_tail = &idat->global->idat_cache;
+
+   /* Now the chunk.  The allocator calls the initializer of the new chunk and
+    * stores the result in file->chunk:
+    */
+   file->alloc(file, 0/*chunk*/);
+   assert(file->chunk != NULL);
+
+   /* And store this for cleanup (and to check for double alloc or failure to
+    * free.)
+    */
+   file->idat = idat;
+}
+
+static png_uint_32
+rechunk_length(struct IDAT *idat)
+   /* Return the length for the next IDAT chunk, taking into account
+    * rechunking.
+    */
+{
+   png_uint_32 len = idat->global->idat_max;
+
+   if (len == 0) /* use original chunk lengths */
+   {
+      const struct IDAT_list *cur;
+      unsigned int count;
+
+      if (idat->idat_index == 0) /* at the new chunk (first time) */
+         return idat->idat_length; /* use the cache */
+
+      /* Otherwise rechunk_length is called at the end of a chunk for the length
+       * of the next one.
+       */
+      cur = idat->idat_cur;
+      count = idat->idat_count;
+
+      assert(idat->idat_index == idat->idat_length &&
+         idat->idat_length == cur->lengths[count]);
+
+      /* Return length of the *next* chunk */
+      if (++count < cur->count)
+         return cur->lengths[count];
+
+      /* End of this list */
+      assert(cur != idat->idat_list_tail);
+      cur = cur->next;
+      assert(cur != NULL && cur->count > 0);
+      return cur->lengths[0];
+   }
+
+   else /* rechunking */
+   {
+      /* The chunk size is the lesser of file->idat_max and the number
+       * of remaining bytes.
+       */
+      png_uint_32 have = idat->idat_length - idat->idat_index;
+
+      if (len > have)
+      {
+         struct IDAT_list *cur = idat->idat_cur;
+         unsigned int j = idat->idat_count+1; /* the next IDAT in the list */
+
+         do
+         {
+            /* Add up the remaining bytes.  This can't overflow because the
+             * individual lengths are always <= 0x7fffffff, so when we add two
+             * of them overflow is not possible.
+             */
+            assert(cur != NULL);
+
+            for (;;)
+            {
+               /* NOTE: IDAT_list::count here, not IDAT_list::length */
+               for (; j < cur->count; ++j)
+               {
+                  have += cur->lengths[j];
+                  if (len <= have)
+                     return len;
+               }
+
+               /* If this was the end return the count of the available bytes */
+               if (cur == idat->idat_list_tail)
+                  return have;
+
+               cur = cur->next;
+               j = 0;
+            }
+         }
+         while (len > have);
+      }
+
+      return len;
+   }
+}
+
+static int
+process_IDAT(struct file *file)
+   /* Process the IDAT stream, this is the more complex than the preceding
+    * cases because the compressed data is spread across multiple IDAT chunks
+    * (typically).  Rechunking of the data is not handled here; all this
+    * function does is establish whether the zlib header needs to be modified.
+    *
+    * Initially the function returns false, indicating that the chunk should not
+    * be written.  It does this until the last IDAT chunk is passed in, then it
+    * checks the zlib data and returns true.
+    *
+    * It does not return false on a fatal error; it calls stop instead.
+    *
+    * The caller must have an instantiated (IDAT) control structure and it must
+    * have extent over the whole read of the IDAT stream.  For a PNG this means
+    * the whole PNG read, for MNG it could have lesser extent.
+    */
+{
+   struct IDAT_list *list;
+
+   assert(file->idat != NULL && file->chunk != NULL);
+
+   /* We need to first check the entire sequence of IDAT chunks to ensure the
+    * stream is in sync.  Do this by building a list of all the chunks and
+    * recording the length of each because the length may have been fixed up by
+    * sync_stream below.
+    *
+    * At the end of the list of chunks, where the type of the next chunk is not
+    * png_IDAT, process the whole stream using the list data to check validity
+    * then return control to the start and rewrite everything.
+    */
+   list = file->idat->idat_list_tail;
+
+   if (list->count == list->length)
+   {
+      list = IDAT_list_extend(list);
+
+      if (list == NULL)
+         stop(file, READ_ERROR_CODE, "out of memory");
+
+      /* Move to the next block */
+      list->count = 0;
+      file->idat->idat_list_tail = list;
+   }
+
+   /* And fill in the next IDAT information buffer. */
+   list->lengths[(list->count)++] = file->chunk->chunk_length;
+
+   /* The type of the next chunk was recorded in the file control structure by
+    * the caller, if this is png_IDAT return 'skip' to the caller.
+    */
+   if (file->type == png_IDAT)
+      return 0; /* skip this for the moment */
+
+   /* This is the final IDAT chunk, so run the tests to check for the too far
+    * back error and possibly optimize the window bits.  This means going back
+    * to the start of the first chunk data, which is stored in the original
+    * chunk allocation.
+    */
+   setpos(file->chunk);
+
+   if (zlib_check(file, 0))
+   {
+      struct IDAT *idat;
+      int cmp;
+
+      /* The IDAT stream was successfully uncompressed; see whether it
+       * contained the correct number of bytes of image data.
+       */
+      cmp = uarb_cmp(file->image_bytes, file->image_digits,
+         file->chunk->uncompressed_bytes, file->chunk->uncompressed_digits);
+
+      if (cmp < 0)
+         type_message(file, png_IDAT, "extra uncompressed data");
+
+      else if (cmp > 0)
+         stop(file, LIBPNG_ERROR_CODE, "IDAT: uncompressed data too small");
+
+      /* Return the stream to the start of the first IDAT chunk; the length
+       * is set in the write case below but the input chunk variables must be
+       * set (once) here:
+       */
+      setpos(file->chunk);
+
+      idat = file->idat;
+      idat->idat_cur = idat->idat_list_head;
+      idat->idat_length = idat->idat_cur->lengths[0];
+      idat->idat_count = 0; /* Count of chunks read in current list */
+      idat->idat_index = 0; /* Index into chunk data */
+
+      /* Update the chunk length to the correct value for the IDAT chunk: */
+      file->chunk->chunk_length = rechunk_length(idat);
+
+      /* Change the state to writing IDAT chunks */
+      file->state = STATE_IDAT;
+
+      return 1;
+   }
+
+   else /* Failure to decompress the IDAT stream; give up. */
+      stop(file, ZLIB_ERROR_CODE, "could not uncompress IDAT");
+}
+
+/* ZLIB CONTROL STRUCTURE */
+struct zlib
+{
+   /* ANCESTORS */
+   struct IDAT *  idat;          /* NOTE: May be NULL */
+   struct chunk * chunk;
+   struct file *  file;
+   struct global *global;
+
+   /* GLOBAL ZLIB INFORMATION: SET BY THE CALLER */
+   png_uint_32    rewrite_offset;
+
+   /* GLOBAL ZLIB INFORMATION: SET BY THE ZLIB READ CODE */
+   udigit         compressed_bytes[5];
+   int            compressed_digits;
+   udigit         uncompressed_bytes[5];
+   int            uncompressed_digits;
+   int            file_bits;             /* window bits from the file */
+   int            ok_bits;               /* Set <16 on a successful read */
+   int            cksum;                 /* Set on a checksum error */
+
+   /* PROTECTED ZLIB INFORMATION: USED BY THE ZLIB ROUTINES */
+   z_stream       z;
+   png_uint_32    extra_bytes;   /* Count of extra compressed bytes */
+   int            state;
+   int            rc;            /* Last return code */
+   int            window_bits;   /* 0 if no change */
+   png_byte       header[2];
+};
+
+static const char *
+zlib_flevel(struct zlib *zlib)
+{
+   switch (zlib->header[1] >> 6)
+   {
+      case 0:  return "supfast";
+      case 1:  return "stdfast";
+      case 2:  return "default";
+      case 3:  return "maximum";
+      default: assert(UNREACHED);
+   }
+
+   return "COMPILER BUG";
+}
+
+static const char *
+zlib_rc(struct zlib *zlib)
+   /* Return a string for the zlib return code */
+{
+   switch (zlib->rc)
+   {
+      case Z_OK:              return "Z_OK";
+      case Z_STREAM_END:      return "Z_STREAM_END";
+      case Z_NEED_DICT:       return "Z_NEED_DICT";
+      case Z_ERRNO:           return "Z_ERRNO";
+      case Z_STREAM_ERROR:    return "Z_STREAM_ERROR";
+      case Z_DATA_ERROR:      return "Z_DATA_ERROR";
+      case Z_MEM_ERROR:       return "Z_MEM_ERROR";
+      case Z_BUF_ERROR:       return "Z_BUF_ERROR";
+      case Z_VERSION_ERROR:   return "Z_VERSION_ERROR";
+      default:                return "Z_*INVALID_RC*";
+   }
+}
+
+static void
+zlib_message(struct zlib *zlib, int unexpected)
+   /* Output a message given a zlib rc */
+{
+   if (zlib->global->errors)
+   {
+      const char *reason = zlib->z.msg;
+
+      if (reason == NULL)
+         reason = "[no message]";
+
+      fputs(zlib->file->file_name, stderr);
+      type_sep(stderr);
+      type_name(zlib->chunk->chunk_type, stderr);
+      fprintf(stderr, ": %szlib error: %d (%s) (%s)\n",
+         unexpected ? "unexpected " : "", zlib->rc, zlib_rc(zlib), reason);
+   }
+}
+
+static void
+zlib_end(struct zlib *zlib)
+{
+   /* Output the summary line now; this ensures a summary line always gets
+    * output regardless of the manner of exit.
+    */
+   if (!zlib->global->quiet)
+   {
+      if (zlib->ok_bits < 16) /* stream was read ok */
+      {
+         const char *reason;
+
+         if (zlib->cksum)
+            reason = "CHK"; /* checksum error */
+
+         else if (zlib->ok_bits > zlib->file_bits)
+            reason = "TFB"; /* fixing a too-far-back error */
+
+         else if (zlib->ok_bits == zlib->file_bits)
+            reason = "OK ";
+
+         else
+            reason = "OPT"; /* optimizing window bits */
+
+         /* SUMMARY FORMAT (for a successful zlib inflate):
+          *
+          * IDAT reason flevel file-bits ok-bits compressed uncompressed file
+          */
+         type_name(zlib->chunk->chunk_type, stdout);
+         printf(" %s %s %d %d ", reason, zlib_flevel(zlib), zlib->file_bits,
+            zlib->ok_bits);
+         uarb_print(zlib->compressed_bytes, zlib->compressed_digits, stdout);
+         putc(' ', stdout);
+         uarb_print(zlib->uncompressed_bytes, zlib->uncompressed_digits,
+            stdout);
+         putc(' ', stdout);
+         fputs(zlib->file->file_name, stdout);
+         putc('\n', stdout);
+      }
+
+      else
+      {
+         /* This is a zlib read error; the chunk will be skipped.  For an IDAT
+          * stream this will also cause a fatal read error (via stop()).
+          *
+          * SUMMARY FORMAT:
+          *
+          * IDAT SKP flevel file-bits z-rc compressed message file
+          *
+          * z-rc is the zlib failure code; message is the error message with
+          * spaces replaced by '-'.  The compressed byte count indicates where
+          * in the zlib stream the error occurred.
+          */
+         type_name(zlib->chunk->chunk_type, stdout);
+         printf(" SKP %s %d %s ", zlib_flevel(zlib), zlib->file_bits,
+            zlib_rc(zlib));
+         uarb_print(zlib->compressed_bytes, zlib->compressed_digits, stdout);
+         putc(' ', stdout);
+         emit_string(zlib->z.msg ? zlib->z.msg : "[no_message]", stdout);
+         putc(' ', stdout);
+         fputs(zlib->file->file_name, stdout);
+         putc('\n', stdout);
+      }
+   }
+
+   if (zlib->state >= 0)
+   {
+      zlib->rc = inflateEnd(&zlib->z);
+
+      if (zlib->rc != Z_OK)
+         zlib_message(zlib, 1/*unexpected*/);
+   }
+
+   CLEAR(*zlib);
+}
+
+static int
+zlib_reset(struct zlib *zlib, int window_bits)
+   /* Reinitializes a zlib with a different window_bits */
+{
+   assert(zlib->state >= 0); /* initialized by zlib_init */
+
+   zlib->z.next_in = Z_NULL;
+   zlib->z.avail_in = 0;
+   zlib->z.next_out = Z_NULL;
+   zlib->z.avail_out = 0;
+
+   zlib->window_bits = window_bits;
+   zlib->compressed_digits = 0;
+   zlib->uncompressed_digits = 0;
+
+   zlib->state = 0; /* initialized, once */
+   zlib->rc = inflateReset2(&zlib->z, 0);
+   if (zlib->rc != Z_OK)
+   {
+      zlib_message(zlib, 1/*unexpected*/);
+      return 0;
+   }
+
+   return 1;
+}
+
+static int
+zlib_init(struct zlib *zlib, struct IDAT *idat, struct chunk *chunk,
+   int window_bits, png_uint_32 offset)
+   /* Initialize a zlib_control; the result is true/false */
+{
+   CLEAR(*zlib);
+
+   zlib->idat = idat;
+   zlib->chunk = chunk;
+   zlib->file = chunk->file;
+   zlib->global = chunk->global;
+   zlib->rewrite_offset = offset; /* never changed for this zlib */
+
+   /* *_out does not need to be set: */
+   zlib->z.next_in = Z_NULL;
+   zlib->z.avail_in = 0;
+   zlib->z.zalloc = Z_NULL;
+   zlib->z.zfree = Z_NULL;
+   zlib->z.opaque = Z_NULL;
+
+   zlib->state = -1;
+   zlib->window_bits = window_bits;
+
+   zlib->compressed_digits = 0;
+   zlib->uncompressed_digits = 0;
+
+   /* These values are sticky across reset (in addition to the stuff in the
+    * first block, which is actually constant.)
+    */
+   zlib->file_bits = 24;
+   zlib->ok_bits = 16; /* unset */
+   zlib->cksum = 0; /* set when a checksum error is detected */
+
+   /* '0' means use the header; inflateInit2 should always succeed because it
+    * does nothing apart from allocating the internal zstate.
+    */
+   zlib->rc = inflateInit2(&zlib->z, 0);
+   if (zlib->rc != Z_OK)
+   {
+      zlib_message(zlib, 1/*unexpected*/);
+      return 0;
+   }
+
+   else
+   {
+      zlib->state = 0; /* initialized */
+      return 1;
+   }
+}
+
+static int
+max_window_bits(uarbc size, int ndigits)
+   /* Return the zlib stream window bits required for data of the given size. */
+{
+   png_uint_16 cb;
+
+   if (ndigits > 1)
+      return 15;
+
+   cb = size[0];
+
+   if (cb > 16384) return 15;
+   if (cb >  8192) return 14;
+   if (cb >  4096) return 13;
+   if (cb >  2048) return 12;
+   if (cb >  1024) return 11;
+   if (cb >   512) return 10;
+   if (cb >   256) return  9;
+   return 8;
+}
+
+static int
+zlib_advance(struct zlib *zlib, png_uint_32 nbytes)
+   /* Read nbytes compressed bytes; the stream will be initialized if required.
+    * Bytes are always being reread and errors are fatal.  The return code is as
+    * follows:
+    *
+    *    -1: saw the "too far back" error
+    *     0: ok, keep going
+    *     1: saw Z_STREAM_END (zlib->extra_bytes indicates too much data)
+    *     2: a zlib error that cannot be corrected (error message already
+    *        output if required.)
+    */
+#  define ZLIB_TOO_FAR_BACK (-1)
+#  define ZLIB_OK           0
+#  define ZLIB_STREAM_END   1
+#  define ZLIB_FATAL        2
+{
+   int state = zlib->state;
+   int endrc = ZLIB_OK;
+   png_uint_32 in_bytes = 0;
+   struct file *file = zlib->file;
+
+   assert(state >= 0);
+
+   while (in_bytes < nbytes && endrc == ZLIB_OK)
+   {
+      png_uint_32 out_bytes;
+      int flush;
+      png_byte bIn = reread_byte(file);
+      png_byte bOut;
+
+      switch (state)
+      {
+         case 0: /* first header byte */
+            {
+               int file_bits = 8+(bIn >> 4);
+               int new_bits = zlib->window_bits;
+
+               zlib->file_bits = file_bits;
+
+               /* Check against the existing value - it may not need to be
+                * changed.  Note that a bogus file_bits is allowed through once,
+                * to see if it works, but the window_bits value is set to 15,
+                * the maximum.
+                */
+               if (new_bits == 0) /* no change */
+                  zlib->window_bits = ((file_bits > 15) ? 15 : file_bits);
+
+               else if (new_bits != file_bits) /* rewrite required */
+                  bIn = (png_byte)((bIn & 0xf) + ((new_bits-8) << 4));
+            }
+
+            zlib->header[0] = bIn;
+            zlib->state = state = 1;
+            break;
+
+         case 1: /* second header byte */
+            {
+               int b2 = bIn & 0xe0; /* top 3 bits */
+
+               /* The checksum calculation, on the first 11 bits: */
+               b2 += 0x1f - ((zlib->header[0] << 8) + b2) % 0x1f;
+
+               /* Update the checksum byte if required: */
+               if (bIn != b2)
+               {
+                  /* If the first byte wasn't changed this indicates an error in
+                   * the checksum calculation; signal this by setting 'cksum'.
+                   */
+                  if (zlib->file_bits == zlib->window_bits)
+                     zlib->cksum = 1;
+
+                  bIn = (png_byte)b2;
+               }
+            }
+
+            zlib->header[1] = bIn;
+            zlib->state = state = 2;
+            break;
+
+         default: /* After the header bytes */
+            break;
+      }
+
+      /* For some streams, perhaps only those compressed with 'superfast
+       * compression' (which results in a lot of copying) Z_BUF_ERROR can happen
+       * immediately after all output has been flushed on the next input byte.
+       * This is handled below when Z_BUF_ERROR is detected by adding an output
+       * byte.
+       */
+      zlib->z.next_in = &bIn;
+      zlib->z.avail_in = 1;
+      zlib->z.next_out = &bOut;
+      zlib->z.avail_out = 0;     /* Initially */
+
+      /* Initially use Z_NO_FLUSH in an attempt to persuade zlib to look at this
+       * byte without confusing what is going on with output.
+       */
+      flush = Z_NO_FLUSH;
+      out_bytes = 0;
+
+      /* NOTE: expression 3 is only evaluted on 'continue', because of the
+       * 'break' at the end of this loop below.
+       */
+      for (;endrc == ZLIB_OK;
+         flush = Z_SYNC_FLUSH,
+         zlib->z.next_out = &bOut,
+         zlib->z.avail_out = 1,
+         ++out_bytes)
+      {
+         zlib->rc = inflate(&zlib->z, flush);
+         out_bytes -= zlib->z.avail_out;
+
+         switch (zlib->rc)
+         {
+            case Z_BUF_ERROR:
+               if (zlib->z.avail_out == 0)
+                  continue; /* Try another output byte. */
+
+               if (zlib->z.avail_in == 0)
+                  break; /* Try another input byte */
+
+               /* Both avail_out and avail_in are 1 yet zlib returned a code
+                * indicating no progress was possible.  This is unexpected.
+                */
+               zlib_message(zlib, 1/*unexpected*/);
+               endrc = ZLIB_FATAL; /* stop processing */
+               break;
+
+            case Z_OK:
+               /* Zlib is supposed to have made progress: */
+               assert(zlib->z.avail_out == 0 || zlib->z.avail_in == 0);
+               continue;
+
+            case Z_STREAM_END:
+               /* This is the successful end. */
+               zlib->state = 3; /* end of stream */
+               endrc = ZLIB_STREAM_END;
+               break;
+
+            case Z_NEED_DICT:
+               zlib_message(zlib, 0/*stream error*/);
+               endrc = ZLIB_FATAL;
+               break;
+
+            case Z_DATA_ERROR:
+               /* The too far back error can be corrected, others cannot: */
+               if (zlib->z.msg != NULL &&
+                  strcmp(zlib->z.msg, "invalid distance too far back") == 0)
+               {
+                  endrc = ZLIB_TOO_FAR_BACK;
+                  break;
+               }
+               /* FALL THROUGH */
+
+            default:
+               zlib_message(zlib, 0/*stream error*/);
+               endrc = ZLIB_FATAL;
+               break;
+         } /* switch (inflate rc) */
+
+         /* Control gets here when further output is not possible; endrc may
+          * still be ZLIB_OK if more input is required.
+          */
+         break;
+      } /* for (output bytes) */
+
+      /* Keep a running count of output byte produced: */
+      zlib->uncompressed_digits = uarb_add32(zlib->uncompressed_bytes,
+         zlib->uncompressed_digits, out_bytes);
+
+      /* Keep going, the loop will terminate when endrc is no longer set to
+       * ZLIB_OK or all the input bytes have been consumed; meanwhile keep
+       * adding input bytes.
+       */
+      assert(zlib->z.avail_in == 0 || endrc != ZLIB_OK);
+
+      in_bytes += 1 - zlib->z.avail_in;
+   } /* while (input bytes) */
+
+   assert(in_bytes == nbytes || endrc != ZLIB_OK);
+
+   /* Update the running total of input bytes consumed */
+   zlib->compressed_digits = uarb_add32(zlib->compressed_bytes,
+      zlib->compressed_digits, in_bytes - zlib->z.avail_in);
+
+   /* At the end of the stream update the chunk with the accumulated
+    * information if it is an improvement:
+    */
+   if (endrc == ZLIB_STREAM_END && zlib->window_bits < zlib->ok_bits)
+   {
+      struct chunk *chunk = zlib->chunk;
+
+      chunk->uncompressed_digits = uarb_copy(chunk->uncompressed_bytes,
+         zlib->uncompressed_bytes, zlib->uncompressed_digits);
+      chunk->compressed_digits = uarb_copy(chunk->compressed_bytes,
+         zlib->compressed_bytes, zlib->compressed_digits);
+      chunk->rewrite_buffer[0] = zlib->header[0];
+      chunk->rewrite_buffer[1] = zlib->header[1];
+
+      if (zlib->window_bits != zlib->file_bits || zlib->cksum)
+      {
+         /* A rewrite is required */
+         chunk->rewrite_offset = zlib->rewrite_offset;
+         chunk->rewrite_length = 2;
+      }
+
+      else
+      {
+         chunk->rewrite_offset = 0;
+         chunk->rewrite_length = 0;
+      }
+
+      if (in_bytes < nbytes)
+         chunk_message(chunk, "extra compressed data");
+
+      zlib->extra_bytes = nbytes - in_bytes;
+      zlib->ok_bits = zlib->window_bits;
+   }
+
+   return endrc;
+}
+
+static int
+zlib_run(struct zlib *zlib)
+   /* Like zlib_advance but also handles a stream of IDAT chunks. */
+{
+   /* The 'extra_bytes' field is set by zlib_advance if there is extra
+    * compressed data in the chunk it handles (if it sees Z_STREAM_END before
+    * all the input data has been used.)  This function uses the value to update
+    * the correct chunk length, so the problem should only ever be detected once
+    * for each chunk.  zlib_advance outputs the error message, though see the
+    * IDAT specific check below.
+    */
+   zlib->extra_bytes = 0;
+
+   if (zlib->idat != NULL)
+   {
+      struct IDAT_list *list = zlib->idat->idat_list_head;
+      struct IDAT_list *last = zlib->idat->idat_list_tail;
+      int        skip = 0;
+
+      /* 'rewrite_offset' is the offset of the LZ data within the chunk, for
+       * IDAT it should be 0:
+       */
+      assert(zlib->rewrite_offset == 0);
+
+      /* Process each IDAT_list in turn; the caller has left the stream
+       * positioned at the start of the first IDAT chunk data.
+       */
+      for (;;)
+      {
+         const unsigned int count = list->count;
+         unsigned int i;
+
+         for (i = 0; i<count; ++i)
+         {
+            int rc;
+
+            if (skip > 0) /* Skip CRC and next IDAT header */
+               skip_12(zlib->file);
+
+            skip = 12; /* for the next time */
+
+            rc = zlib_advance(zlib, list->lengths[i]);
+
+            switch (rc)
+            {
+               case ZLIB_OK: /* keep going */
+                  break;
+
+               case ZLIB_STREAM_END: /* stop */
+                  /* There may be extra chunks; if there are and one of them is
+                   * not zero length output the 'extra data' message.  Only do
+                   * this check if errors are being output.
+                   */
+                  if (zlib->global->errors && zlib->extra_bytes == 0)
+                  {
+                     struct IDAT_list *check = list;
+                     int j = i+1, jcount = count;
+
+                     for (;;)
+                     {
+                        for (; j<jcount; ++j)
+                           if (check->lengths[j] > 0)
+                           {
+                              chunk_message(zlib->chunk,
+                                 "extra compressed data");
+                              goto end_check;
+                           }
+
+                        if (check == last)
+                           break;
+
+                        check = check->next;
+                        jcount = check->count;
+                        j = 0;
+                     }
+                  }
+
+               end_check:
+                  /* Terminate the list at the current position, reducing the
+                   * length of the last IDAT too if required.
+                   */
+                  list->lengths[i] -= zlib->extra_bytes;
+                  list->count = i+1;
+                  zlib->idat->idat_list_tail = list;
+                  /* FALL THROUGH */
+
+               default:
+                  return rc;
+            }
+         }
+
+         /* At the end of the compressed data and Z_STREAM_END was not seen. */
+         if (list == last)
+            return ZLIB_OK;
+
+         list = list->next;
+      }
+   }
+
+   else
+   {
+      struct chunk *chunk = zlib->chunk;
+      int rc;
+
+      assert(zlib->rewrite_offset < chunk->chunk_length);
+
+      rc = zlib_advance(zlib, chunk->chunk_length - zlib->rewrite_offset);
+
+      /* The extra bytes in the chunk are handled now by adjusting the chunk
+       * length to exclude them; the zlib data is always stored at the end of
+       * the PNG chunk (although clearly this is not necessary.)  zlib_advance
+       * has already output a warning message.
+       */
+      chunk->chunk_length -= zlib->extra_bytes;
+      return rc;
+   }
+}
+
+static int /* global function; not a member function */
+zlib_check(struct file *file, png_uint_32 offset)
+   /* Check the stream of zlib compressed data in either idat (if given) or (if
+    * not) chunk.  In fact it is zlib_run that handles the difference in reading
+    * a single chunk and a list of IDAT chunks.
+    *
+    * In either case the input file must be positioned at the first byte of zlib
+    * compressed data (the first header byte).
+    *
+    * The return value is true on success, including the case where the zlib
+    * header may need to be rewritten, and false on an unrecoverable error.
+    *
+    * In the case of IDAT chunks 'offset' should be 0.
+    */
+{
+   fpos_t start_pos;
+   struct zlib zlib;
+
+   /* Record the start of the LZ data to allow a re-read. */
+   file_getpos(file, &start_pos);
+
+   /* First test the existing (file) window bits: */
+   if (zlib_init(&zlib, file->idat, file->chunk, 0/*window bits*/, offset))
+   {
+      int min_bits, max_bits, rc;
+
+      /* The first run using the existing window bits. */
+      rc = zlib_run(&zlib);
+
+      switch (rc)
+      {
+         case ZLIB_TOO_FAR_BACK:
+            /* too far back error */
+            file->status_code |= TOO_FAR_BACK;
+            min_bits = zlib.window_bits + 1;
+            max_bits = 15;
+            break;
+
+         case ZLIB_STREAM_END:
+            if (!zlib.global->optimize_zlib &&
+               zlib.window_bits == zlib.file_bits && !zlib.cksum)
+            {
+               /* The trivial case where the stream is ok and optimization was
+                * not requested.
+                */
+               zlib_end(&zlib);
+               return 1;
+            }
+
+            max_bits = max_window_bits(zlib.uncompressed_bytes,
+               zlib.uncompressed_digits);
+            if (zlib.ok_bits < max_bits)
+               max_bits = zlib.ok_bits;
+            min_bits = 8;
+
+            /* cksum is set if there is an error in the zlib header checksum
+             * calculation in the original file (and this may be the only reason
+             * a rewrite is required).  We can't rely on the file window bits in
+             * this case, so do the optimization anyway.
+             */
+            if (zlib.cksum)
+               chunk_message(zlib.chunk, "zlib checkum");
+            break;
+
+
+         case ZLIB_OK:
+            /* Truncated stream; unrecoverable, gets converted to ZLIB_FATAL */
+            zlib.z.msg = PNGZ_MSG_CAST("[truncated]");
+            zlib_message(&zlib, 0/*expected*/);
+            /* FALL THROUGH */
+
+         default:
+            /* Unrecoverable error; skip the chunk; a zlib_message has already
+             * been output.
+             */
+            zlib_end(&zlib);
+            return 0;
+      }
+
+      /* Optimize window bits or fix a too-far-back error.  min_bits and
+       * max_bits have been set appropriately, ok_bits records the bit value
+       * known to work.
+       */
+      while (min_bits < max_bits || max_bits < zlib.ok_bits/*if 16*/)
+      {
+         int test_bits = (min_bits + max_bits) >> 1;
+
+         if (zlib_reset(&zlib, test_bits))
+         {
+            file_setpos(file, &start_pos);
+            rc = zlib_run(&zlib);
+
+            switch (rc)
+            {
+               case ZLIB_TOO_FAR_BACK:
+                  min_bits = test_bits+1;
+                  if (min_bits > max_bits)
+                  {
+                     /* This happens when the stream really is damaged and it
+                      * contains a distance code that addresses bytes before
+                      * the start of the uncompressed data.
+                      */
+                     assert(test_bits == 15);
+
+                     /* Output the error that wasn't output before: */
+                     if (zlib.z.msg == NULL)
+                        zlib.z.msg = PNGZ_MSG_CAST(
+                           "invalid distance too far back");
+                     zlib_message(&zlib, 0/*stream error*/);
+                     zlib_end(&zlib);
+                     return 0;
+                  }
+                  break;
+
+               case ZLIB_STREAM_END: /* success */
+                  max_bits = test_bits;
+                  break;
+
+               default:
+                  /* A fatal error; this happens if a too-far-back error was
+                   * hiding a more serious error, zlib_advance has already
+                   * output a zlib_message.
+                   */
+                  zlib_end(&zlib);
+                  return 0;
+            }
+         }
+
+         else /* inflateReset2 failed */
+         {
+            zlib_end(&zlib);
+            return 0;
+         }
+      }
+
+      /* The loop guarantees this */
+      assert(zlib.ok_bits == max_bits);
+      zlib_end(&zlib);
+      return 1;
+   }
+
+   else /* zlib initialization failed - skip the chunk */
+   {
+      zlib_end(&zlib);
+      return 0;
+   }
+}
+
+/***************************** LIBPNG CALLBACKS *******************************/
+/* The strategy here is to run a regular libpng PNG file read but examine the
+ * input data (from the file) before passing it to libpng so as to be aware of
+ * the state we expect libpng to be in.  Warning and error callbacks are also
+ * intercepted so that they can be quieted and interpreted.  Interpretation
+ * depends on a somewhat risky string match for known error messages; let us
+ * hope that this can be fixed in the next version of libpng.
+ *
+ * The control structure is pointed to by the libpng error pointer.  It contains
+ * that set of structures which must persist across multiple read callbacks,
+ * which is pretty much everything except the 'zlib' control structure.
+ *
+ * The file structure is instantiated in the caller of the per-file routine, but
+ * the per-file routine contains the chunk and IDAT control structures.
+ */
+/* The three routines read_chunk, process_chunk and sync_stream can only be
+ * called via a call to read_chunk and only exit at a return from process_chunk.
+ * These routines could have been written as one confusing large routine,
+ * instead this code relies on the compiler to do tail call elimination.  The
+ * possible calls are as follows:
+ *
+ * read_chunk
+ *    -> sync_stream
+ *       -> process_chunk
+ *    -> process_chunk
+ *       -> read_chunk
+ *       returns
+ */
+static void read_chunk(struct file *file);
+static void
+process_chunk(struct file *file, png_uint_32 file_crc, png_uint_32 next_length,
+   png_uint_32 next_type)
+   /* Called when the chunk data has been read, next_length and next_type
+    * will be set for the next chunk (or 0 if this is IEND).
+    *
+    * When this routine returns, chunk_length and chunk_type will be set for the
+    * next chunk to write because if a chunk is skipped this return calls back
+    * to read_chunk.
+    */
+{
+   const png_uint_32 type = file->type;
+
+   if (file->global->verbose > 1)
+   {
+      fputs("  ", stderr);
+      type_name(file->type, stderr);
+      fprintf(stderr, " %lu 0x%.8x 0x%.8x\n", (unsigned long)file->length,
+         file->crc ^ 0xffffffff, file_crc);
+   }
+
+   /* The basic structure seems correct but the CRC may not match, in this
+    * case assume that it is simply a bad CRC, either wrongly calculated or
+    * because of damaged stream data.
+    */
+   if ((file->crc ^ 0xffffffff) != file_crc)
+   {
+      /* The behavior is set by the 'skip' setting; if it is anything other
+       * than SKIP_BAD_CRC ignore the bad CRC and return the chunk, with a
+       * corrected CRC and possibly processed, to libpng.  Otherwise skip the
+       * chunk, which will result in a fatal error if the chunk is critical.
+       */
+      file->status_code |= CRC_ERROR;
+
+      /* Ignore the bad CRC  */
+      if (file->global->skip != SKIP_BAD_CRC)
+         type_message(file, type, "bad CRC");
+
+      /* This will cause an IEND with a bad CRC to stop */
+      else if (CRITICAL(type))
+         stop(file, READ_ERROR_CODE, "bad CRC in critical chunk");
+
+      else
+      {
+         type_message(file, type, "skipped: bad CRC");
+
+         /* NOTE: this cannot be reached for IEND because it is critical. */
+         goto skip_chunk;
+      }
+   }
+
+   /* Check for other 'skip' cases and handle these; these only apply to
+    * ancillary chunks (and not tRNS, which should probably have been a critical
+    * chunk.)
+    */
+   if (skip_chunk_type(file->global, type))
+      goto skip_chunk;
+
+   /* The chunk may still be skipped if problems are detected in the LZ data,
+    * however the LZ data check requires a chunk.  Handle this by instantiating
+    * a chunk unless an IDAT is already instantiated (IDAT control structures
+    * instantiate their own chunk.)
+    */
+   if (type != png_IDAT)
+      file->alloc(file, 0/*chunk*/);
+
+   else if (file->idat == NULL)
+      file->alloc(file, 1/*IDAT*/);
+
+   else
+   {
+      /* The chunk length must be updated for process_IDAT */
+      assert(file->chunk != NULL);
+      assert(file->chunk->chunk_type == png_IDAT);
+      file->chunk->chunk_length = file->length;
+   }
+
+   /* Record the 'next' information too, now that the original values for
+    * this chunk have been copied.  Notice that the IDAT chunks only make a
+    * copy of the position of the first chunk, this is fine - process_IDAT does
+    * not need the position of this chunk.
+    */
+   file->length = next_length;
+   file->type = next_type;
+   getpos(file);
+
+   /* Do per-type processing, note that if this code does not return from the
+    * function the chunk will be skipped.  The rewrite is cancelled here so that
+    * it can be set in the per-chunk processing.
+    */
+   file->chunk->rewrite_length = 0;
+   file->chunk->rewrite_offset = 0;
+   switch (type)
+   {
+      default:
+         return;
+
+      case png_IHDR:
+         /* Read this now and update the control structure with the information
+          * it contains.  The header is validated completely to ensure this is a
+          * PNG.
+          */
+         {
+            struct chunk *chunk = file->chunk;
+
+            if (chunk->chunk_length != 13)
+               stop_invalid(file, "IHDR length");
+
+            /* Read all the IHDR information and validate it. */
+            setpos(chunk);
+            file->width = reread_4(file);
+            file->height = reread_4(file);
+            file->bit_depth = reread_byte(file);
+            file->color_type = reread_byte(file);
+            file->compression_method = reread_byte(file);
+            file->filter_method = reread_byte(file);
+            file->interlace_method = reread_byte(file);
+
+            /* This validates all the fields, and calls stop_invalid if
+             * there is a problem.
+             */
+            calc_image_size(file);
+         }
+         return;
+
+         /* Ancillary chunks that require further processing: */
+      case png_zTXt: case png_iCCP:
+         if (process_zTXt_iCCP(file))
+            return;
+         chunk_end(&file->chunk);
+         file_setpos(file, &file->data_pos);
+         break;
+
+      case png_iTXt:
+         if (process_iTXt(file))
+            return;
+         chunk_end(&file->chunk);
+         file_setpos(file, &file->data_pos);
+         break;
+
+      case png_IDAT:
+         if (process_IDAT(file))
+            return;
+         /* First pass: */
+         assert(next_type == png_IDAT);
+         break;
+   }
+
+   /* Control reaches this point if the chunk must be skipped.  For chunks other
+    * than IDAT this means that the zlib compressed data is fatally damanged and
+    * the chunk will not be passed to libpng.  For IDAT it means that the end of
+    * the IDAT stream has not yet been reached and we must handle the next
+    * (IDAT) chunk.  If the LZ data in an IDAT stream cannot be read 'stop' must
+    * be used to halt parsing of the PNG.
+    */
+   read_chunk(file);
+   return;
+
+   /* This is the generic code to skip the current chunk; simply jump to the
+    * next one.
+    */
+skip_chunk:
+   file->length = next_length;
+   file->type = next_type;
+   getpos(file);
+   read_chunk(file);
+}
+
+static png_uint_32
+get32(png_bytep buffer, int offset)
+   /* Read a 32-bit value from an 8-byte circular buffer (used only below).
+    */
+{
+   return
+      (buffer[ offset    & 7] << 24) +
+      (buffer[(offset+1) & 7] << 16) +
+      (buffer[(offset+2) & 7] <<  8) +
+      (buffer[(offset+3) & 7]      );
+}
+
+static void
+sync_stream(struct file *file)
+   /* The stream seems to be messed up, attempt to resync from the current chunk
+    * header.  Executes stop on a fatal error, otherwise calls process_chunk.
+    */
+{
+   png_uint_32 file_crc;
+
+   file->status_code |= STREAM_ERROR;
+
+   if (file->global->verbose)
+   {
+      fputs(" SYNC ", stderr);
+      type_name(file->type, stderr);
+      putc('\n', stderr);
+   }
+
+   /* Return to the start of the chunk data */
+   file_setpos(file, &file->data_pos);
+   file->read_count = 8;
+
+   if (read_4(file, &file_crc) == 4) /* else completely truncated */
+   {
+      /* Ignore the recorded chunk length, proceed through the data looking for
+       * a leading sequence of bytes that match the CRC in the following four
+       * bytes.  Each time a match is found check the next 8 bytes for a valid
+       * length, chunk-type pair.
+       */
+      png_uint_32 length;
+      png_uint_32 type = file->type;
+      png_uint_32 crc = crc_init_4(type);
+      png_byte buffer[8];
+      unsigned int nread = 0, nused = 0;
+
+      for (length=0; length <= 0x7fffffff; ++length)
+      {
+         int ch;
+
+         if ((crc ^ 0xffffffff) == file_crc)
+         {
+            /* A match on the CRC; for IEND this is sufficient, but for anything
+             * else expect a following chunk header.
+             */
+            if (type == png_IEND)
+            {
+               file->length = length;
+               process_chunk(file, file_crc, 0, 0);
+               return;
+            }
+
+            else
+            {
+               /* Need 8 bytes */
+               while (nread < 8+nused)
+               {
+                  ch = read_byte(file);
+                  if (ch == EOF)
+                     goto truncated;
+                  buffer[(nread++) & 7] = (png_byte)ch;
+               }
+
+               /* Prevent overflow */
+               nread -= nused & ~7;
+               nused -= nused & ~7; /* or, nused &= 7 ;-) */
+
+               /* Examine the 8 bytes for a valid chunk header. */
+               {
+                  png_uint_32 next_length = get32(buffer, nused);
+
+                  if (next_length < 0x7fffffff)
+                  {
+                     png_uint_32 next_type = get32(buffer, nused+4);
+
+                     if (chunk_type_valid(next_type))
+                     {
+                        file->read_count -= 8;
+                        process_chunk(file, file_crc, next_length, next_type);
+                        return;
+                     }
+                  }
+
+                  /* Not valid, keep going. */
+               }
+            }
+         }
+
+         /* This catches up with the circular buffer which gets filled above
+          * while checking a chunk header.  This code is slightly tricky - if
+          * the chunk_type is IEND the buffer will never be used, if it is not
+          * the code will always read ahead exactly 8 bytes and pass this on to
+          * process_chunk.  So the invariant that IEND leaves the file position
+          * after the IEND CRC and other chunk leave it after the *next* chunk
+          * header is not broken.
+          */
+         if (nread <= nused)
+         {
+            ch = read_byte(file);
+
+            if (ch == EOF)
+               goto truncated;
+         }
+
+         else
+            ch = buffer[(++nused) & 7];
+
+         crc = crc_one_byte(crc, file_crc >> 24);
+         file_crc = (file_crc << 8) + ch;
+      }
+
+      /* Control gets to here if when 0x7fffffff bytes (plus 8) have been read,
+       * ok, treat this as a damaged stream too:
+       */
+   }
+
+truncated:
+   stop(file, READ_ERROR_CODE, "damaged PNG stream");
+}
+
+static void
+read_chunk(struct file *file)
+   /* On entry file::data_pos must be set to the position of the first byte
+    * of the chunk data *and* the input file must be at this position.  This
+    * routine (via process_chunk) instantiates a chunk or IDAT control structure
+    * based on file::length and file::type and also resets these fields and
+    * file::data_pos for the chunk after this one.  For an IDAT chunk the whole
+    * stream of IDATs will be read, until something other than an IDAT is
+    * encountered, and the file fields will be set for the chunk after the end
+    * of the stream of IDATs.
+    *
+    * For IEND the file::type field will be set to 0, and nothing beyond the end
+    * of the IEND chunk will have been read.
+    */
+{
+   png_uint_32 length = file->length;
+   png_uint_32 type = file->type;
+
+   /* After IEND file::type is set to 0, if libpng attempts to read
+    * more data at this point this is a bug in libpng.
+    */
+   if (type == 0)
+      stop(file, UNEXPECTED_ERROR_CODE, "read beyond IEND");
+
+   if (file->global->verbose > 2)
+   {
+      fputs("   ", stderr);
+      type_name(type, stderr);
+      fprintf(stderr, " %lu\n", (unsigned long)length);
+   }
+
+   /* Start the read_crc calculation with the chunk type, then read to the end
+    * of the chunk data (without processing it in any way) to check that it is
+    * all there and calculate the CRC.
+    */
+   file->crc = crc_init_4(type);
+   if (crc_read_many(file, length)) /* else it was truncated */
+   {
+      png_uint_32 file_crc; /* CRC read from file */
+      unsigned int nread = read_4(file, &file_crc);
+
+      if (nread == 4)
+      {
+         if (type != png_IEND) /* do not read beyond IEND */
+         {
+            png_uint_32 next_length;
+
+            nread += read_4(file, &next_length);
+            if (nread == 8 && next_length <= 0x7fffffff)
+            {
+               png_uint_32 next_type;
+
+               nread += read_4(file, &next_type);
+
+               if (nread == 12 && chunk_type_valid(next_type))
+               {
+                  /* Adjust the read count back to the correct value for this
+                   * chunk.
+                   */
+                  file->read_count -= 8;
+                  process_chunk(file, file_crc, next_length, next_type);
+                  return;
+               }
+            }
+         }
+
+         else /* IEND */
+         {
+            process_chunk(file, file_crc, 0, 0);
+            return;
+         }
+      }
+   }
+
+   /* Control gets to here if the the stream seems invalid or damaged in some
+    * way.  Either there was a problem reading all the expected data (this
+    * chunk's data, its CRC and the length and type of the next chunk) or the
+    * next chunk length/type are invalid.  Notice that the cases that end up
+    * here all correspond to cases that would otherwise terminate the read of
+    * the PNG file.
+    */
+   sync_stream(file);
+}
+
+/* This returns a file* from a png_struct in an implementation specific way. */
+static struct file *get_control(png_const_structrp png_ptr);
+
+static void PNGCBAPI
+error_handler(png_structp png_ptr, png_const_charp message)
+{
+   stop(get_control(png_ptr),  LIBPNG_ERROR_CODE, message);
+}
+
+static void PNGCBAPI
+warning_handler(png_structp png_ptr, png_const_charp message)
+{
+   struct file *file = get_control(png_ptr);
+
+   if (file->global->warnings)
+      emit_error(file, LIBPNG_WARNING_CODE, message);
+}
+
+/* Read callback - this is where the work gets done to check the stream before
+ * passing it to libpng
+ */
+static void PNGCBAPI
+read_callback(png_structp png_ptr, png_bytep buffer, size_t count)
+   /* Return 'count' bytes to libpng in 'buffer' */
+{
+   struct file *file = get_control(png_ptr);
+   png_uint_32 type, length; /* For the chunk be *WRITTEN* */
+   struct chunk *chunk;
+
+   /* libpng should always ask for at least one byte */
+   if (count == 0)
+      stop(file, UNEXPECTED_ERROR_CODE, "read callback for 0 bytes");
+
+   /* The callback always reads ahead by 8 bytes - the signature or chunk header
+    * - these bytes are stored in chunk_length and chunk_type.  This block is
+    * executed once for the signature and once for the first chunk right at the
+    * start.
+    */
+   if (file->read_count < 8)
+   {
+      assert(file->read_count == 0);
+      assert((file->status_code & TRUNCATED) == 0);
+
+      (void)read_4(file, &file->length);
+
+      if (file->read_count == 4)
+         (void)read_4(file, &file->type);
+
+      if (file->read_count < 8)
+      {
+         assert((file->status_code & TRUNCATED) != 0);
+         stop(file, READ_ERROR_CODE, "not a PNG (too short)");
+      }
+
+      if (file->state == STATE_SIGNATURE)
+      {
+         if (file->length != sig1 || file->type != sig2)
+            stop(file, LIBPNG_ERROR_CODE, "not a PNG (signature)");
+
+         /* Else write it (this is the initialization of write_count, prior to
+          * this it contains CLEAR garbage.)
+          */
+         file->write_count = 0;
+      }
+
+      else
+      {
+         assert(file->state == STATE_CHUNKS);
+
+         /* The first chunk must be a well formed IHDR (this could be relaxed to
+          * use the checks in process_chunk, but that seems unnecessary.)
+          */
+         if (file->length != 13 || file->type != png_IHDR)
+            stop(file, LIBPNG_ERROR_CODE, "not a PNG (IHDR)");
+
+         /* The position of the data must be stored too */
+         getpos(file);
+      }
+   }
+
+   /* Retrieve previous state (because the read callbacks are made pretty much
+    * byte-by-byte in the sequential reader prior to 1.7).
+    */
+   chunk = file->chunk;
+
+   if (chunk != NULL)
+   {
+      length = chunk->chunk_length;
+      type = chunk->chunk_type;
+   }
+
+   else
+   {
+      /* This is the signature case; for IDAT and other chunks these values will
+       * be overwritten when read_chunk is called below.
+       */
+      length = file->length;
+      type = file->type;
+   }
+
+   do
+   {
+      png_uint_32 b;
+
+      /* Complete the read of a chunk; as a side effect this also instantiates
+       * a chunk control structure and sets the file length/type/data_pos fields
+       * for the *NEXT* chunk header.
+       *
+       * NOTE: at an IDAT any following IDAT chunks will also be read and the
+       * next_ fields will refer to the chunk after the last IDAT.
+       *
+       * NOTE: read_chunk only returns when it has read a chunk that must now be
+       * written.
+       */
+      if (file->state != STATE_SIGNATURE && chunk == NULL)
+      {
+         assert(file->read_count == 8);
+         assert(file->idat == NULL);
+         read_chunk(file);
+         chunk = file->chunk;
+         assert(chunk != NULL);
+
+         /* Do the initialization that was not done before. */
+         length = chunk->chunk_length;
+         type = chunk->chunk_type;
+
+         /* And start writing the new chunk. */
+         file->write_count = 0;
+      }
+
+      /* The chunk_ fields describe a chunk that must be written, or hold the
+       * signature.  Write the header first.  In the signature case this
+       * rewrites the signature.
+       */
+      switch (file->write_count)
+      {
+         case 0: b = length >> 24; break;
+         case 1: b = length >> 16; break;
+         case 2: b = length >>  8; break;
+         case 3: b = length      ; break;
+
+         case 4: b = type >> 24; break;
+         case 5: b = type >> 16; break;
+         case 6: b = type >>  8; break;
+         case 7: b = type      ; break;
+
+         case 8:
+            /* The header has been written.  If this is really the signature
+             * that's all that is required and we can go to normal chunk
+             * processing.
+             */
+            if (file->state == STATE_SIGNATURE)
+            {
+               /* The signature has been written, the tail call to read_callback
+                * below (it's just a goto to the start with a decent compiler)
+                * will read the IHDR header ahead and validate it.
+                */
+               assert(length == sig1 && type == sig2);
+               file->read_count = 0; /* Forces a header read */
+               file->state = STATE_CHUNKS; /* IHDR: checked above */
+               read_callback(png_ptr, buffer, count);
+               return;
+            }
+
+            else
+            {
+               assert(chunk != NULL);
+
+               /* Set up for write, notice that repositioning the input stream
+                * is only necessary if something is to be read from it.  Also
+                * notice that for the IDAT stream this must only happen once -
+                * on the first IDAT - to get back to the start of the list and
+                * this is done inside process_IDAT:
+                */
+               chunk->write_crc = crc_init_4(type);
+               if (file->state != STATE_IDAT && length > 0)
+                  setpos(chunk);
+            }
+            /* FALL THROUGH */
+
+         default:
+            assert(chunk != NULL);
+
+            /* NOTE: the arithmetic below overflows and gives a large positive
+             * png_uint_32 value until the whole chunk data has been written.
+             */
+            switch (file->write_count - length)
+            {
+               /* Write the chunk data, normally this just comes from
+                * the file.  The only exception is for that part of a
+                * chunk which is zlib data and which must be rewritten,
+                * and IDAT chunks which can be completely
+                * reconstructed.
+                */
+               default:
+                  if (file->state == STATE_IDAT)
+                  {
+                     struct IDAT *idat = file->idat;
+
+                     assert(idat != NULL);
+
+                     /* Read an IDAT byte from the input stream of IDAT chunks.
+                      * Because the IDAT stream can be re-chunked this stream is
+                      * held in the struct IDAT members.  The chunk members, in
+                      * particular chunk_length (and therefore the length local)
+                      * refer to the output chunk.
+                      */
+                     while (idat->idat_index >= idat->idat_length)
+                     {
+                        /* Advance one chunk */
+                        struct IDAT_list *cur = idat->idat_cur;
+
+                        assert(idat->idat_index == idat->idat_length);
+                        assert(cur != NULL && cur->count > 0);
+
+                        /* NOTE: IDAT_list::count here, not IDAT_list::length */
+                        if (++(idat->idat_count) >= cur->count)
+                        {
+                           assert(idat->idat_count == cur->count);
+
+                           /* Move on to the next IDAT_list: */
+                           cur = cur->next;
+
+                           /* This is an internal error - read beyond the end of
+                            * the pre-calculated stream.
+                            */
+                           if (cur == NULL || cur->count == 0)
+                              stop(file, UNEXPECTED_ERROR_CODE,
+                                 "read beyond end of IDAT");
+
+                           idat->idat_count = 0;
+                           idat->idat_cur = cur;
+                        }
+
+                        idat->idat_index = 0;
+                        /* Zero length IDAT chunks are permitted, so the length
+                         * here may be 0.
+                         */
+                        idat->idat_length = cur->lengths[idat->idat_count];
+
+                        /* And skip 12 bytes to the next chunk data */
+                        skip_12(file);
+                     }
+
+                     /* The index is always that of the next byte, the rest of
+                      * the information is always the current IDAT chunk and the
+                      * current list.
+                      */
+                     ++(idat->idat_index);
+                  }
+
+                  /* Read the byte from the stream. */
+                  b = reread_byte(file);
+
+                  /* If the byte must be rewritten handle that here */
+                  if (chunk->rewrite_length > 0)
+                  {
+                     if (chunk->rewrite_offset > 0)
+                        --(chunk->rewrite_offset);
+
+                     else
+                     {
+                        b = chunk->rewrite_buffer[0];
+                        memmove(chunk->rewrite_buffer, chunk->rewrite_buffer+1,
+                           (sizeof chunk->rewrite_buffer)-
+                              (sizeof chunk->rewrite_buffer[0]));
+
+                        --(chunk->rewrite_length);
+                     }
+                  }
+
+                  chunk->write_crc = crc_one_byte(chunk->write_crc, b);
+                  break;
+
+               /* The CRC is written at:
+                *
+                *    chunk_write == chunk_length+8..chunk_length+11
+                *
+                * so 8 to 11.  The CRC is not (yet) conditioned.
+                */
+               case  8: b = chunk->write_crc >> 24; goto write_crc;
+               case  9: b = chunk->write_crc >> 16; goto write_crc;
+               case 10: b = chunk->write_crc >>  8; goto write_crc;
+               case 11:
+                  /* This must happen before the chunk_end below: */
+                  b = chunk->write_crc;
+
+                  if (file->global->verbose > 2)
+                  {
+                     fputs("   ", stderr);
+                     type_name(type, stderr);
+                     fprintf(stderr, " %lu 0x%.8x\n", (unsigned long)length,
+                        chunk->write_crc ^ 0xffffffff);
+                  }
+
+                  /* The IDAT stream is written without a call to read_chunk
+                   * until the end is reached.  rechunk_length() calculates the
+                   * length of the output chunks.  Control gets to this point at
+                   * the end of an *output* chunk - the length calculated by
+                   * rechunk_length.  If this corresponds to the end of the
+                   * input stream stop writing IDAT chunks, otherwise continue.
+                   */
+                  if (file->state == STATE_IDAT &&
+                     (file->idat->idat_index < file->idat->idat_length ||
+                      1+file->idat->idat_count < file->idat->idat_cur->count ||
+                      file->idat->idat_cur != file->idat->idat_list_tail))
+                  {
+                     /* Write another IDAT chunk.  Call rechunk_length to
+                      * calculate the length required.
+                      */
+                     length = chunk->chunk_length = rechunk_length(file->idat);
+                     assert(type == png_IDAT);
+                     file->write_count = 0; /* for the new chunk */
+                     --(file->write_count); /* fake out the increment below */
+                  }
+
+                  else
+                  {
+                     /* Entered at the end of a non-IDAT chunk and at the end of
+                      * the IDAT stream.  The rewrite should have been cleared.
+                      */
+                     if (chunk->rewrite_length > 0 || chunk->rewrite_offset > 0)
+                        stop(file, UNEXPECTED_ERROR_CODE, "pending rewrite");
+
+                     /* This is the last byte so reset chunk_read for the next
+                      * chunk and move the input file to the position after the
+                      * *next* chunk header if required.
+                      */
+                     file->read_count = 8;
+                     file_setpos(file, &file->data_pos);
+
+                     if (file->idat == NULL)
+                        chunk_end(&file->chunk);
+
+                     else
+                        IDAT_end(&file->idat);
+                  }
+
+               write_crc:
+                  b ^= 0xff; /* conditioning */
+                  break;
+            }
+            break;
+      }
+
+      /* Write one byte */
+      b &= 0xff;
+      *buffer++ = (png_byte)b;
+      --count;
+      write_byte(file, (png_byte)b); /* increments chunk_write */
+   }
+   while (count > 0);
+}
+
+/* Bundle the file and an uninitialized chunk and IDAT control structure
+ * together to allow implementation of the chunk/IDAT allocate routine.
+ */
+struct control
+{
+   struct file  file;
+   struct chunk chunk;
+   struct IDAT  idat;
+};
+
+static int
+control_end(struct control *control)
+{
+   return file_end(&control->file);
+}
+
+static struct file *
+get_control(png_const_structrp png_ptr)
+{
+   /* This just returns the (file*).  The chunk and idat control structures
+    * don't always exist.
+    */
+   struct control *control = voidcast(struct control*,
+      png_get_error_ptr(png_ptr));
+   return &control->file;
+}
+
+static void
+allocate(struct file *file, int allocate_idat)
+{
+   struct control *control = voidcast(struct control*, file->alloc_ptr);
+
+   if (allocate_idat)
+   {
+      assert(file->idat == NULL);
+      IDAT_init(&control->idat, file);
+   }
+
+   else /* chunk */
+   {
+      assert(file->chunk == NULL);
+      chunk_init(&control->chunk, file);
+   }
+}
+
+static int
+control_init(struct control *control, struct global *global,
+   const char *file_name, const char *out_name)
+   /* This wraps file_init(&control::file) and simply returns the result from
+    * file_init.
+    */
+{
+   return file_init(&control->file, global, file_name, out_name, control,
+      allocate);
+}
+
+static int
+read_png(struct control *control)
+   /* Read a PNG, return 0 on success else an error (status) code; a bit mask as
+    * defined for file::status_code as above.
+    */
+{
+   png_structp png_ptr;
+   png_infop info_ptr = NULL;
+   volatile int rc;
+
+   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, control,
+      error_handler, warning_handler);
+
+   if (png_ptr == NULL)
+   {
+      /* This is not really expected. */
+      log_error(&control->file, LIBPNG_ERROR_CODE, "OOM allocating png_struct");
+      control->file.status_code |= INTERNAL_ERROR;
+      return LIBPNG_ERROR_CODE;
+   }
+
+   rc = setjmp(control->file.jmpbuf);
+   if (rc == 0)
+   {
+#     ifdef PNG_SET_USER_LIMITS_SUPPORTED
+         /* Remove any limits on the size of PNG files that can be read,
+          * without this we may reject files based on built-in safety
+          * limits.
+          */
+         png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
+         png_set_chunk_cache_max(png_ptr, 0);
+         png_set_chunk_malloc_max(png_ptr, 0);
+#     endif
+
+      png_set_read_fn(png_ptr, control, read_callback);
+
+      info_ptr = png_create_info_struct(png_ptr);
+      if (info_ptr == NULL)
+         png_error(png_ptr, "OOM allocating info structure");
+
+      if (control->file.global->verbose)
+         fprintf(stderr, " INFO\n");
+
+      png_read_info(png_ptr, info_ptr);
+
+      {
+        png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+        int passes = png_set_interlace_handling(png_ptr);
+        int pass;
+
+        png_start_read_image(png_ptr);
+
+        for (pass = 0; pass < passes; ++pass)
+        {
+           png_uint_32 y = height;
+
+           /* NOTE: this skips asking libpng to return either version of
+            * the image row, but libpng still reads the rows.
+            */
+           while (y-- > 0)
+              png_read_row(png_ptr, NULL, NULL);
+        }
+      }
+
+      if (control->file.global->verbose)
+         fprintf(stderr, " END\n");
+
+      /* Make sure to read to the end of the file: */
+      png_read_end(png_ptr, info_ptr);
+   }
+
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+   return rc;
+}
+
+static int
+one_file(struct global *global, const char *file_name, const char *out_name)
+{
+   int rc;
+   struct control control;
+
+   if (global->verbose)
+      fprintf(stderr, "FILE %s -> %s\n", file_name,
+         out_name ? out_name : "<none>");
+
+   /* Although control_init can return a failure code the structure is always
+    * initialized, so control_end can be used to accumulate any status codes.
+    */
+   rc = control_init(&control, global, file_name, out_name);
+
+   if (rc == 0)
+      rc = read_png(&control);
+
+   rc |= control_end(&control);
+
+   return rc;
+}
+
+static void
+usage(const char *prog)
+{
+   /* ANSI C-90 limits strings to 509 characters, so use a string array: */
+   size_t i;
+   static const char *usage_string[] = {
+"  Tests, optimizes and optionally fixes the zlib header in PNG files.",
+"  Optionally, when fixing, strips ancilliary chunks from the file.",
+0,
+"OPTIONS",
+"  OPERATION",
+"      By default files are just checked for readability with a summary of the",
+"      of zlib issues founds for each compressed chunk and the IDAT stream in",
+"      the file.",
+"    --optimize (-o):",
+"      Find the smallest deflate window size for the compressed data.",
+"    --strip=[none|crc|unsafe|unused|transform|color|all]:",
+"        none (default):   Retain all chunks.",
+"        crc:    Remove chunks with a bad CRC.",
+"        unsafe: Remove chunks that may be unsafe to retain if the image data",
+"                is modified.  This is set automatically if --max is given but",
+"                may be cancelled by a later --strip=none.",
+"        unused: Remove chunks not used by libpng when decoding an image.",
+"                This retains any chunks that might be used by libpng image",
+"                transformations.",
+"        transform: unused+bKGD.",
+"        color:  transform+iCCP and cHRM.",
+"        all:    color+gAMA and sRGB.",
+"      Only ancillary chunks are ever removed.  In addition the tRNS and sBIT",
+"      chunks are never removed as they affect exact interpretation of the",
+"      image pixel values.  The following known chunks are treated specially",
+"      by the above options:",
+"        gAMA, sRGB [all]: These specify the gamma encoding used for the pixel",
+"            values.",
+"        cHRM, iCCP [color]: These specify how colors are encoded.  iCCP also",
+"            specifies the exact encoding of a pixel value; however, in",
+"            practice most programs will ignore it.",
+"        bKGD [transform]: This is used by libpng transforms."
+"    --max=<number>:",
+"      Use IDAT chunks sized <number>.  If no number is given the the IDAT",
+"      chunks will be the maximum size permitted; 2^31-1 bytes.  If the option",
+"      is omitted the original chunk sizes will not be changed.  When the",
+"      option is given --strip=unsafe is set automatically. This may be",
+"      cancelled if you know that all unknown unsafe-to-copy chunks really are",
+"      safe to copy across an IDAT size change.  This is true of all chunks",
+"      that have ever been formally proposed as PNG extensions.",
+"  MESSAGES",
+"      By default the program only outputs summaries for each file.",
+"    --quiet (-q):",
+"      Do not output the summaries except for files that cannot be read. With",
+"      two --quiets these are not output either.",
+"    --errors (-e):",
+"      Output errors from libpng and the program (except too-far-back).",
+"    --warnings (-w):",
+"      Output warnings from libpng.",
+"  OUTPUT",
+"      By default nothing is written.",
+"    --out=<file>:",
+"      Write the optimized/corrected version of the next PNG to <file>.  This",
+"      overrides the following two options",
+"    --suffix=<suffix>:",
+"      Set --out=<name><suffix> for all following files unless overridden on",
+"      a per-file basis by explicit --out.",
+"    --prefix=<prefix>:",
+"      Set --out=<prefix><name> for all the following files unless overridden",
+"      on a per-file basis by explicit --out.",
+"      These two options can be used together to produce a suffix and prefix.",
+"  INTERNAL OPTIONS",
+#if 0 /*NYI*/
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+"    --test:",
+"      Test the PNG_MAXIMUM_INFLATE_WINDOW option.  Setting this disables",
+"      output as this would produce a broken file.",
+#endif
+#endif
+0,
+"EXIT CODES",
+"  *** SUBJECT TO CHANGE ***",
+"  The program exit code is value in the range 0..127 holding a bit mask of",
+"  the following codes.  Notice that the results for each file are combined",
+"  together - check one file at a time to get a meaningful error code!",
+"    0x01: The zlib too-far-back error existed in at least one chunk.",
+"    0x02: At least one chunk had a CRC error.",
+"    0x04: A chunk length was incorrect.",
+"    0x08: The file was truncated.",
+"  Errors less than 16 are potentially recoverable, for a single file if the",
+"  exit code is less than 16 the file could be read (with corrections if a",
+"  non-zero code is returned).",
+"    0x10: The file could not be read, even with corrections.",
+"    0x20: The output file could not be written.",
+"    0x40: An unexpected, potentially internal, error occurred.",
+"  If the command line arguments are incorrect the program exits with exit",
+"  255.  Some older operating systems only support 7-bit exit codes, on those",
+"  systems it is suggested that this program is first tested by supplying",
+"  invalid arguments.",
+0,
+"DESCRIPTION",
+"  " PROGRAM_NAME ":",
+"  checks each PNG file on the command line for errors.  By default errors are",
+"  not output and the program just returns an exit code and prints a summary.",
+"  With the --quiet (-q) option the summaries are suppressed too and the",
+"  program only outputs unexpected errors (internal errors and file open",
+"  errors).",
+"  Various known problems in PNG files are fixed while the file is being read",
+"  The exit code says what problems were fixed.  In particular the zlib error:",
+0,
+"        \"invalid distance too far back\"",
+0,
+"  caused by an incorrect optimization of a zlib stream is fixed in any",
+"  compressed chunk in which it is encountered.  An integrity problem of the",
+"  PNG stream caused by a bug in libpng which wrote an incorrect chunk length",
+"  is also fixed.  Chunk CRC errors are automatically fixed up.",
+0,
+"  Setting one of the \"OUTPUT\" options causes the possibly modified file to",
+"  be written to a new file.",
+0,
+"  Notice that some PNG files with the zlib optimization problem can still be",
+"  read by libpng under some circumstances.  This program will still detect",
+"  and, if requested, correct the error.",
+0,
+"  The program will reliably process all files on the command line unless",
+"  either an invalid argument causes the usage message (this message) to be",
+"  produced or the program crashes.",
+0,
+"  The summary lines describe issues encountered with the zlib compressed",
+"  stream of a chunk.  They have the following format, which is SUBJECT TO",
+"  CHANGE in the future:",
+0,
+"     chunk reason comp-level p1 p2 p3 p4 file",
+0,
+"  p1 through p4 vary according to the 'reason'.  There are always 8 space",
+"  separated fields.  Reasons specific formats are:",
+0,
+"     chunk ERR status code read-errno write-errno message file",
+"     chunk SKP comp-level file-bits zlib-rc compressed message file",
+"     chunk ??? comp-level file-bits ok-bits compressed uncompress file",
+0,
+"  The various fields are",
+0,
+"$1 chunk:      The chunk type of a chunk in the file or 'HEAD' if a problem",
+"               is reported by libpng at the start of the IDAT stream.",
+"$2 reason:     One of:",
+"          CHK: A zlib header checksum was detected and fixed.",
+"          TFB: The zlib too far back error was detected and fixed.",
+"          OK : No errors were detected in the zlib stream and optimization",
+"               was not requested, or was not possible.",
+"          OPT: The zlib stream window bits value could be improved (and was).",
+"          SKP: The chunk was skipped because of a zlib issue (zlib-rc) with",
+"               explanation 'message'",
+"          ERR: The read of the file was aborted.  The parameters explain why.",
+"$3 status:     For 'ERR' the accumulated status code from 'EXIT CODES' above.",
+"               This is printed as a 2 digit hexadecimal value",
+"   comp-level: The recorded compression level (FLEVEL) of a zlib stream",
+"               expressed as a string {supfast,stdfast,default,maximum}",
+"$4 code:       The file exit code; where stop was called, as a fairly terse",
+"               string {warning,libpng,zlib,invalid,read,write,unexpected}.",
+"   file-bits:  The zlib window bits recorded in the file.",
+"$5 read-errno: A system errno value from a read translated by strerror(3).",
+"   zlib-rc:    A zlib return code as a string (see zlib.h).",
+"   ok-bits:    The smallest zlib window bits value that works.",
+"$6 write-errno:A system errno value from a write translated by strerror(3).",
+"   compressed: The count of compressed bytes in the zlib stream, when the",
+"               reason is 'SKP'; this is a count of the bytes read from the",
+"               stream when the fatal error was encountered.",
+"$7 message:    An error message (spaces replaced by _, as in all parameters),",
+"   uncompress: The count of bytes from uncompressing the zlib stream; this",
+"               may not be the same as the number of bytes in the image.",
+"$8 file:       The name of the file (this may contain spaces).",
+};
+
+   fprintf(stderr, "Usage: %s {[options] png-file}\n", prog);
+
+   for (i=0; i < (sizeof usage_string)/(sizeof usage_string[0]); ++i)
+   {
+      if (usage_string[i] != 0)
+         fputs(usage_string[i], stderr);
+
+      fputc('\n', stderr);
+   }
+
+   exit(255);
+}
+
+int
+main(int argc, const char **argv)
+{
+   char temp_name[FILENAME_MAX+1];
+   const char *  prog = *argv;
+   const char *  outfile = NULL;
+   const char *  suffix = NULL;
+   const char *  prefix = NULL;
+   int           done = 0; /* if at least one file is processed */
+   struct global global;
+
+   global_init(&global);
+
+   while (--argc > 0)
+   {
+      ++argv;
+
+      if (strcmp(*argv, "--debug") == 0)
+      {
+         /* To help debugging problems: */
+         global.errors = global.warnings = 1;
+         global.quiet = 0;
+         global.verbose = 7;
+      }
+
+      else if (strncmp(*argv, "--max=", 6) == 0)
+      {
+         global.idat_max = (png_uint_32)atol(6+*argv);
+
+         if (global.skip < SKIP_UNSAFE)
+            global.skip = SKIP_UNSAFE;
+      }
+
+      else if (strcmp(*argv, "--max") == 0)
+      {
+         global.idat_max = 0x7fffffff;
+
+         if (global.skip < SKIP_UNSAFE)
+            global.skip = SKIP_UNSAFE;
+      }
+
+      else if (strcmp(*argv, "--optimize") == 0 || strcmp(*argv, "-o") == 0)
+         global.optimize_zlib = 1;
+
+      else if (strncmp(*argv, "--out=", 6) == 0)
+         outfile = 6+*argv;
+
+      else if (strncmp(*argv, "--suffix=", 9) == 0)
+         suffix = 9+*argv;
+
+      else if (strncmp(*argv, "--prefix=", 9) == 0)
+         prefix = 9+*argv;
+
+      else if (strcmp(*argv, "--strip=none") == 0)
+         global.skip = SKIP_NONE;
+
+      else if (strcmp(*argv, "--strip=crc") == 0)
+         global.skip = SKIP_BAD_CRC;
+
+      else if (strcmp(*argv, "--strip=unsafe") == 0)
+         global.skip = SKIP_UNSAFE;
+
+      else if (strcmp(*argv, "--strip=unused") == 0)
+         global.skip = SKIP_UNUSED;
+
+      else if (strcmp(*argv, "--strip=transform") == 0)
+         global.skip = SKIP_TRANSFORM;
+
+      else if (strcmp(*argv, "--strip=color") == 0)
+         global.skip = SKIP_COLOR;
+
+      else if (strcmp(*argv, "--strip=all") == 0)
+         global.skip = SKIP_ALL;
+
+      else if (strcmp(*argv, "--errors") == 0 || strcmp(*argv, "-e") == 0)
+         global.errors = 1;
+
+      else if (strcmp(*argv, "--warnings") == 0 || strcmp(*argv, "-w") == 0)
+         global.warnings = 1;
+
+      else if (strcmp(*argv, "--quiet") == 0 || strcmp(*argv, "-q") == 0)
+      {
+         if (global.quiet)
+            global.quiet = 2;
+
+         else
+            global.quiet = 1;
+      }
+
+      else if (strcmp(*argv, "--verbose") == 0 || strcmp(*argv, "-v") == 0)
+         ++global.verbose;
+
+#if 0
+      /* NYI */
+#     ifdef PNG_MAXIMUM_INFLATE_WINDOW
+         else if (strcmp(*argv, "--test") == 0)
+            ++set_option;
+#     endif
+#endif
+
+      else if ((*argv)[0] == '-')
+         usage(prog);
+
+      else
+      {
+         size_t outlen = strlen(*argv);
+
+         if (outfile == NULL) /* else this takes precedence */
+         {
+            /* Consider the prefix/suffix options */
+            if (prefix != NULL)
+            {
+               size_t prefixlen = strlen(prefix);
+
+               if (prefixlen+outlen > FILENAME_MAX)
+               {
+                  fprintf(stderr, "%s: output file name too long: %s%s%s\n",
+                     prog, prefix, *argv, suffix ? suffix : "");
+                  global.status_code |= WRITE_ERROR;
+                  continue;
+               }
+
+               memcpy(temp_name, prefix, prefixlen);
+               memcpy(temp_name+prefixlen, *argv, outlen);
+               outlen += prefixlen;
+               outfile = temp_name;
+            }
+
+            else if (suffix != NULL)
+               memcpy(temp_name, *argv, outlen);
+
+            temp_name[outlen] = 0;
+
+            if (suffix != NULL)
+            {
+               size_t suffixlen = strlen(suffix);
+
+               if (outlen+suffixlen > FILENAME_MAX)
+               {
+                  fprintf(stderr, "%s: output file name too long: %s%s\n",
+                     prog, *argv, suffix);
+                  global.status_code |= WRITE_ERROR;
+                  continue;
+               }
+
+               memcpy(temp_name+outlen, suffix, suffixlen);
+               outlen += suffixlen;
+               temp_name[outlen] = 0;
+               outfile = temp_name;
+            }
+         }
+
+         (void)one_file(&global, *argv, outfile);
+         ++done;
+         outfile = NULL;
+      }
+   }
+
+   if (!done)
+      usage(prog);
+
+   return global_end(&global);
+}
+
+#else /* ZLIB_VERNUM < 0x1240 */
+int
+main(void)
+{
+   fprintf(stderr,
+      "pngfix needs libpng with a zlib >=1.2.4 (not 0x%x)\n",
+      ZLIB_VERNUM);
+   return 77;
+}
+#endif /* ZLIB_VERNUM */
+
+#else /* No read support */
+
+int
+main(void)
+{
+   fprintf(stderr, "pngfix does not work without read deinterlace support\n");
+   return 77;
+}
+#endif /* PNG_READ_SUPPORTED && PNG_EASY_ACCESS_SUPPORTED */
+#else /* No setjmp support */
+int
+main(void)
+{
+   fprintf(stderr, "pngfix does not work without setjmp support\n");
+   return 77;
+}
+#endif /* PNG_SETJMP_SUPPORTED */
+
diff --git a/contrib/tools/sRGB.h b/contrib/tools/sRGB.h
new file mode 100644
index 0000000..22c8f7c
--- /dev/null
+++ b/contrib/tools/sRGB.h
@@ -0,0 +1,48 @@
+/*-
+ * sRGB.h
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2013.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work.  This work is published from:
+ * United States.
+ *
+ * Utility file; not actually a header, this contains definitions of sRGB
+ * calculation functions for inclusion in those test programs that need them.
+ *
+ * All routines take and return a floating point value in the range
+ * 0 to 1.0, doing a calculation according to the sRGB specification
+ * (in fact the source of the numbers is the wikipedia article at
+ * http://en.wikipedia.org/wiki/SRGB).
+ */
+static double
+sRGB_from_linear(double l)
+{
+   if (l <= 0.0031308)
+      l *= 12.92;
+
+   else
+      l = 1.055 * pow(l, 1/2.4) - 0.055;
+
+   return l;
+}
+
+static double
+linear_from_sRGB(double s)
+{
+   if (s <= 0.04045)
+      return s / 12.92;
+
+   else
+      return pow((s+0.055)/1.055, 2.4);
+}
+
+static double
+YfromRGB(double r, double g, double b)
+{
+   /* Use the sRGB (rounded) coefficients for Rlinear, Glinear, Blinear to get
+    * the CIE Y value (also linear).
+    */
+   return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+}
diff --git a/contrib/visupng/PngFile.c b/contrib/visupng/PngFile.c
index ff6d155..dcde18a 100644
--- a/contrib/visupng/PngFile.c
+++ b/contrib/visupng/PngFile.c
@@ -1,17 +1,19 @@
-//-------------------------------------
-//  PNGFILE.C -- Image File Functions
-//-------------------------------------
-
-// Copyright 2000, Willem van Schaik.
-//
-// This code is released under the libpng license.
-// For conditions of distribution and use, see the disclaimer
-// and license in png.h
+/*-------------------------------------
+ *  PNGFILE.C -- Image File Functions
+ *-------------------------------------
+ *
+ * Copyright 2000, Willem van Schaik.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
 
 #include <windows.h>
 #include <commdlg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <zlib.h>
 
 #include "png.h"
 #include "pngfile.h"
@@ -28,14 +30,14 @@
 static png_infop info_ptr = NULL;
 
 
-// cexcept interface
+/* cexcept interface */
 
 static void
 png_cexcept_error(png_structp png_ptr, png_const_charp msg)
 {
    if(png_ptr)
      ;
-#ifndef PNG_NO_CONSOLE_IO
+#ifdef PNG_CONSOLE_IO_SUPPORTED
    fprintf(stderr, "libpng error: %s\n", msg);
 #endif
    {
@@ -43,7 +45,7 @@
    }
 }
 
-// Windows open-file functions
+/* Windows open-file functions */
 
 void PngFileInitialize (HWND hwnd)
 {
@@ -57,13 +59,13 @@
     ofn.lpstrCustomFilter = NULL;
     ofn.nMaxCustFilter    = 0;
     ofn.nFilterIndex      = 0;
-    ofn.lpstrFile         = NULL;          // Set in Open and Close functions
+    ofn.lpstrFile         = NULL;          /* Set in Open and Close functions */
     ofn.nMaxFile          = MAX_PATH;
-    ofn.lpstrFileTitle    = NULL;          // Set in Open and Close functions
+    ofn.lpstrFileTitle    = NULL;          /* Set in Open and Close functions */
     ofn.nMaxFileTitle     = MAX_PATH;
     ofn.lpstrInitialDir   = NULL;
     ofn.lpstrTitle        = NULL;
-    ofn.Flags             = 0;             // Set in Open and Close functions
+    ofn.Flags             = 0;             /* Set in Open and Close functions */
     ofn.nFileOffset       = 0;
     ofn.nFileExtension    = 0;
     ofn.lpstrDefExt       = TEXT ("png");
@@ -92,7 +94,7 @@
     return GetSaveFileName (&ofn);
 }
 
-// PNG image handler functions
+/* PNG image handler functions */
 
 BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData,
                    int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor)
@@ -109,7 +111,7 @@
     static png_byte   **ppbRowPointers = NULL;
     int                 i;
 
-    // open the PNG input file
+    /* open the PNG input file */
 
     if (!pstrFileName)
     {
@@ -123,7 +125,7 @@
         return FALSE;
     }
 
-    // first check the eight byte PNG signature
+    /* first check the eight byte PNG signature */
 
     fread(pbSig, 1, 8, pfFile);
     if (png_sig_cmp(pbSig, 0, 8))
@@ -132,7 +134,7 @@
         return FALSE;
     }
 
-    // create the two png(-info) structures
+    /* create the two png(-info) structures */
 
     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
       (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
@@ -152,31 +154,37 @@
 
     Try
     {
-        
-        // initialize the png structure
-        
-#if !defined(PNG_NO_STDIO)
+
+        /* initialize the png structure */
+
+#ifdef PNG_STDIO_SUPPORTED
         png_init_io(png_ptr, pfFile);
 #else
         png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data);
 #endif
-        
+
         png_set_sig_bytes(png_ptr, 8);
-        
-        // read all PNG info up to image data
-        
+
+        /* read all PNG info up to image data */
+
         png_read_info(png_ptr, info_ptr);
-        
-        // get width, height, bit-depth and color-type
-        
+
+        /* get width, height, bit-depth and color-type */
+
         png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth,
             &iColorType, NULL, NULL, NULL);
-        
-        // expand images of all color-type and bit-depth to 3x8 bit RGB images
-        // let the library process things like alpha, transparency, background
-        
-        if (iBitDepth == 16)
-            png_set_strip_16(png_ptr);
+
+        /* expand images of all color-type and bit-depth to 3x8-bit RGB */
+        /* let the library process alpha, transparency, background, etc. */
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+    if (iBitDepth == 16)
+#  ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+        png_set_scale_16(png_ptr);
+#  else
+        png_set_strip_16(png_ptr);
+#  endif
+#endif
         if (iColorType == PNG_COLOR_TYPE_PALETTE)
             png_set_expand(png_ptr);
         if (iBitDepth < 8)
@@ -186,8 +194,8 @@
         if (iColorType == PNG_COLOR_TYPE_GRAY ||
             iColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
             png_set_gray_to_rgb(png_ptr);
-        
-        // set the background color to draw transparent and alpha images over.
+
+        /* set the background color to draw transparent and alpha images over */
         if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
         {
             png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
@@ -199,30 +207,30 @@
         {
             pBkgColor = NULL;
         }
-        
-        // if required set gamma conversion
+
+        /* if required set gamma conversion */
         if (png_get_gAMA(png_ptr, info_ptr, &dGamma))
             png_set_gamma(png_ptr, (double) 2.2, dGamma);
-        
-        // after the transformations have been registered update info_ptr data
-        
+
+        /* after the transformations are registered, update info_ptr data */
+
         png_read_update_info(png_ptr, info_ptr);
-        
-        // get again width, height and the new bit-depth and color-type
-        
+
+        /* get again width, height and the new bit-depth and color-type */
+
         png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth,
             &iColorType, NULL, NULL, NULL);
-        
-        
-        // row_bytes is the width x number of channels
-        
+
+
+        /* row_bytes is the width x number of channels */
+
         ulRowBytes = png_get_rowbytes(png_ptr, info_ptr);
         ulChannels = png_get_channels(png_ptr, info_ptr);
-        
+
         *piChannels = ulChannels;
-        
-        // now we can allocate memory to store the image
-        
+
+        /* now we can allocate memory to store the image */
+
         if (pbImageData)
         {
             free (pbImageData);
@@ -234,34 +242,34 @@
             png_error(png_ptr, "Visual PNG: out of memory");
         }
         *ppbImageData = pbImageData;
-        
-        // and allocate memory for an array of row-pointers
-        
+
+        /* and allocate memory for an array of row-pointers */
+
         if ((ppbRowPointers = (png_bytepp) malloc((*piHeight)
                             * sizeof(png_bytep))) == NULL)
         {
             png_error(png_ptr, "Visual PNG: out of memory");
         }
-        
-        // set the individual row-pointers to point at the correct offsets
-        
+
+        /* set the individual row-pointers to point at the correct offsets */
+
         for (i = 0; i < (*piHeight); i++)
             ppbRowPointers[i] = pbImageData + i * ulRowBytes;
-        
-        // now we can go ahead and just read the whole image
-        
+
+        /* now we can go ahead and just read the whole image */
+
         png_read_image(png_ptr, ppbRowPointers);
-        
-        // read the additional chunks in the PNG file (not really needed)
-        
+
+        /* read the additional chunks in the PNG file (not really needed) */
+
         png_read_end(png_ptr, NULL);
-        
-        // and we're done
-        
+
+        /* and we're done */
+
         free (ppbRowPointers);
         ppbRowPointers = NULL;
-        
-        // yepp, done
+
+        /* yepp, done */
     }
 
     Catch (msg)
@@ -269,7 +277,7 @@
         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
         *ppbImageData = pbImageData = NULL;
-        
+
         if(ppbRowPointers)
             free (ppbRowPointers);
 
@@ -295,7 +303,7 @@
     static png_byte   **ppbRowPointers = NULL;
     int                 i;
 
-    // open the PNG output file
+    /* open the PNG output file */
 
     if (!pstrFileName)
         return FALSE;
@@ -303,7 +311,7 @@
     if (!(pfFile = fopen(pstrFileName, "wb")))
         return FALSE;
 
-    // prepare the standard PNG structures
+    /* prepare the standard PNG structures */
 
     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
       (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
@@ -322,60 +330,60 @@
 
     Try
     {
-        // initialize the png structure
-        
-#if !defined(PNG_NO_STDIO)
+        /* initialize the png structure */
+
+#ifdef PNG_STDIO_SUPPORTED
         png_init_io(png_ptr, pfFile);
 #else
         png_set_write_fn(png_ptr, (png_voidp)pfFile, png_write_data, png_flush);
 #endif
-        
-        // we're going to write a very simple 3x8 bit RGB image
-        
+
+        /* we're going to write a very simple 3x8-bit RGB image */
+
         png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth,
             PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
             PNG_FILTER_TYPE_BASE);
-        
-        // write the file header information
-        
+
+        /* write the file header information */
+
         png_write_info(png_ptr, info_ptr);
-        
-        // swap the BGR pixels in the DiData structure to RGB
-        
+
+        /* swap the BGR pixels in the DiData structure to RGB */
+
         png_set_bgr(png_ptr);
-        
-        // row_bytes is the width x number of channels
-        
+
+        /* row_bytes is the width x number of channels */
+
         ulRowBytes = iWidth * ciChannels;
-        
-        // we can allocate memory for an array of row-pointers
-        
+
+        /* we can allocate memory for an array of row-pointers */
+
         if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL)
             Throw "Visualpng: Out of memory";
-        
-        // set the individual row-pointers to point at the correct offsets
-        
+
+        /* set the individual row-pointers to point at the correct offsets */
+
         for (i = 0; i < iHeight; i++)
             ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2);
-        
-        // write out the entire image data in one call
-        
+
+        /* write out the entire image data in one call */
+
         png_write_image (png_ptr, ppbRowPointers);
-        
-        // write the additional chunks to the PNG file (not really needed)
-        
+
+        /* write the additional chunks to the PNG file (not really needed) */
+
         png_write_end(png_ptr, info_ptr);
-        
-        // and we're done
-        
+
+        /* and we're done */
+
         free (ppbRowPointers);
         ppbRowPointers = NULL;
-        
-        // clean up after the write, and free any memory allocated
-        
+
+        /* clean up after the write, and free any memory allocated */
+
         png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
-        
-        // yepp, done
+
+        /* yepp, done */
     }
 
     Catch (msg)
@@ -389,13 +397,13 @@
 
         return FALSE;
     }
-    
+
     fclose (pfFile);
-    
+
     return TRUE;
 }
 
-#ifdef PNG_NO_STDIO
+#ifndef PNG_STDIO_SUPPORTED
 
 static void
 png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
@@ -437,6 +445,7 @@
 
 #endif
 
-//-----------------
-//  end of source
-//-----------------
+/*-----------------
+ *  end of source
+ *-----------------
+ */
diff --git a/contrib/visupng/PngFile.h b/contrib/visupng/PngFile.h
index f695811..474426c 100644
--- a/contrib/visupng/PngFile.h
+++ b/contrib/visupng/PngFile.h
@@ -1,12 +1,12 @@
-//------------------------------------------
-//  PNGFILE.H -- Header File for pngfile.c
-//------------------------------------------
+/*------------------------------------------*/
+/*  PNGFILE.H -- Header File for pngfile.c*/
+/*------------------------------------------*/
 
-// Copyright 2000, Willem van Schaik.
+/* Copyright 2000, Willem van Schaik.*/
 
-// This code is released under the libpng license.
-// For conditions of distribution and use, see the disclaimer
-// and license in png.h
+/* This code is released under the libpng license.*/
+/* For conditions of distribution and use, see the disclaimer*/
+/* and license in png.h*/
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -17,12 +17,12 @@
 BOOL PngFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ;
 BOOL PngFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ;
 
-BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData, 
+BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData,
                    int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor);
 BOOL PngSaveImage (PTSTR pstrFileName, png_byte *pDiData,
                    int iWidth, int iHeight, png_color BkgColor);
 
-#if defined(PNG_NO_STDIO)
+#ifndef PNG_STDIO_SUPPORTED
 static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
 static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length);
 static void png_flush(png_structp png_ptr);
diff --git a/contrib/visupng/README.txt b/contrib/visupng/README.txt
index 4047119..72c5cba 100644
--- a/contrib/visupng/README.txt
+++ b/contrib/visupng/README.txt
@@ -8,8 +8,8 @@
 and license in png.h
 
 As a PNG .dll demo VisualPng is finished. More features would only hinder
-the program's objective. However, further extensions (like support for other 
-graphics formats) are in development. To get these, or for pre-compiled 
+the program's objective. However, further extensions (like support for other
+graphics formats) are in development. To get these, or for pre-compiled
 binaries, go to "http://www.schaik.com/png/visualpng.html".
 
 ------------------------------------------------------------------------
diff --git a/contrib/visupng/VisualPng.c b/contrib/visupng/VisualPng.c
index 2018999..236525a 100644
--- a/contrib/visupng/VisualPng.c
+++ b/contrib/visupng/VisualPng.c
@@ -1,41 +1,43 @@
-//------------------------------------
-//  VisualPng.C -- Shows a PNG image
-//------------------------------------
+/*------------------------------------
+ *  VisualPng.C -- Shows a PNG image
+ *------------------------------------
+ *
+ * Copyright 2000, Willem van Schaik.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
 
-// Copyright 2000, Willem van Schaik.
+/* switches */
 
-// This code is released under the libpng license.
-// For conditions of distribution and use, see the disclaimer
-// and license in png.h
-
-// switches
-
-// defines
+/* defines */
 
 #define PROGNAME  "VisualPng"
 #define LONGNAME  "Win32 Viewer for PNG-files"
 #define VERSION   "1.0 of 2000 June 07"
 
-// constants
+/* constants */
 
 #define MARGIN 8
 
-// standard includes
+/* standard includes */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <windows.h>
+#include <zlib.h>
 
-// application includes
+/* application includes */
 
 #include "png.h"
 #include "pngfile.h"
 #include "resource.h"
 
-// macros
+/* macros */
 
-// function prototypes
+/* function prototypes */
 
 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
 BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
@@ -65,14 +67,14 @@
         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
         BOOL bStretched);
 
-// a few global variables
+/* a few global variables */
 
 static char *szProgName = PROGNAME;
 static char *szAppName = LONGNAME;
 static char *szIconName = PROGNAME;
 static char szCmdFileName [MAX_PATH];
 
-// MAIN routine
+/* MAIN routine */
 
 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     PSTR szCmdLine, int iCmdShow)
@@ -90,7 +92,7 @@
     wndclass.hInstance     = hInstance;
     wndclass.hIcon         = LoadIcon (hInstance, szIconName) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
-    wndclass.hbrBackground = NULL; // (HBRUSH) GetStockObject (GRAY_BRUSH);
+    wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */
     wndclass.lpszMenuName  = szProgName;
     wndclass.lpszClassName = szProgName;
 
@@ -101,7 +103,7 @@
         return 0;
     }
 
-    // if filename given on commandline, store it
+    /* if filename given on commandline, store it */
     if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
         if (szCmdLine[0] == '"')
             strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
@@ -110,20 +112,20 @@
     else
         strcpy (szCmdFileName, "");
 
-    // calculate size of window-borders
+    /* calculate size of window-borders */
     ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
                      GetSystemMetrics (SM_CXDLGFRAME));
     iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
                      GetSystemMetrics (SM_CYDLGFRAME)) +
                      GetSystemMetrics (SM_CYCAPTION) +
                      GetSystemMetrics (SM_CYMENUSIZE) +
-                     1; /* WvS: don't ask me why? */
+                     1; /* WvS: don't ask me why?  */
 
     hwnd = CreateWindow (szProgName, szAppName,
         WS_OVERLAPPEDWINDOW,
         CW_USEDEFAULT, CW_USEDEFAULT,
         512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
-//      CW_USEDEFAULT, CW_USEDEFAULT,
+/*      CW_USEDEFAULT, CW_USEDEFAULT, */
         NULL, NULL, hInstance, NULL);
 
     ShowWindow (hwnd, iCmdShow);
@@ -180,29 +182,29 @@
 
         strcpy (szImgPathName, "");
 
-        // in case we process file given on command-line
+        /* in case we process file given on command-line */
 
         if (szCmdFileName[0] != '\0')
         {
             strcpy (szImgPathName, szCmdFileName);
 
-            // read the other png-files in the directory for later
-            // next/previous commands
+            /* read the other png-files in the directory for later */
+            /* next/previous commands */
 
             BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
                           &iPngFileIndex);
 
-            // load the image from file
+            /* load the image from file */
 
             if (!LoadImageFile (hwnd, szImgPathName,
                 &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
                 return 0;
 
-            // invalidate the client area for later update
+            /* invalidate the client area for later update */
 
             InvalidateRect (hwnd, NULL, TRUE);
 
-            // display the PNG into the DIBitmap
+            /* display the PNG into the DIBitmap */
 
             DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
                 pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -214,11 +216,11 @@
         cxWinSize = LOWORD (lParam);
         cyWinSize = HIWORD (lParam);
 
-        // invalidate the client area for later update
+        /* invalidate the client area for later update */
 
         InvalidateRect (hwnd, NULL, TRUE);
 
-        // display the PNG into the DIBitmap
+        /* display the PNG into the DIBitmap */
 
         DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
             pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -242,28 +244,28 @@
         {
         case IDM_FILE_OPEN:
 
-            // show the File Open dialog box
+            /* show the File Open dialog box */
 
             if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName))
                 return 0;
 
-            // read the other png-files in the directory for later
-            // next/previous commands
+            /* read the other png-files in the directory for later */
+            /* next/previous commands */
 
             BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
                           &iPngFileIndex);
 
-            // load the image from file
+            /* load the image from file */
 
             if (!LoadImageFile (hwnd, szImgPathName,
                 &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
                 return 0;
 
-            // invalidate the client area for later update
+            /* invalidate the client area for later update */
 
             InvalidateRect (hwnd, NULL, TRUE);
 
-            // display the PNG into the DIBitmap
+            /* display the PNG into the DIBitmap */
 
             DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
                 pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -272,12 +274,12 @@
 
         case IDM_FILE_SAVE:
 
-            // show the File Save dialog box
+            /* show the File Save dialog box */
 
             if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
                 return 0;
 
-            // save the PNG to a disk file
+            /* save the PNG to a disk file */
 
             SetCursor (LoadCursor (NULL, IDC_WAIT));
             ShowCursor (TRUE);
@@ -295,55 +297,55 @@
 
         case IDM_FILE_NEXT:
 
-            // read next entry in the directory
+            /* read next entry in the directory */
 
             if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
                 NULL, szImgPathName))
             {
                 if (strcmp (szImgPathName, "") == 0)
                     return 0;
-                
-                // load the image from file
-                
+
+                /* load the image from file */
+
                 if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
                         &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
                     return 0;
-                
-                // invalidate the client area for later update
-                
+
+                /* invalidate the client area for later update */
+
                 InvalidateRect (hwnd, NULL, TRUE);
-                
-                // display the PNG into the DIBitmap
-                
+
+                /* display the PNG into the DIBitmap */
+
                 DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
                     pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
             }
-            
+
             return 0;
 
         case IDM_FILE_PREVIOUS:
 
-            // read previous entry in the directory
+            /* read previous entry in the directory */
 
             if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
                 szImgPathName, NULL))
             {
-                
+
                 if (strcmp (szImgPathName, "") == 0)
                     return 0;
-                
-                // load the image from file
-                
+
+                /* load the image from file */
+
                 if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
                     &cyImgSize, &cImgChannels, &bkgColor))
                     return 0;
-                
-                // invalidate the client area for later update
-                
+
+                /* invalidate the client area for later update */
+
                 InvalidateRect (hwnd, NULL, TRUE);
-                
-                // display the PNG into the DIBitmap
-                
+
+                /* display the PNG into the DIBitmap */
+
                 DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
                     pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
             }
@@ -352,9 +354,9 @@
 
         case IDM_FILE_EXIT:
 
-            // more cleanup needed...
+            /* more cleanup needed... */
 
-            // free image buffer
+            /* free image buffer */
 
             if (pDib != NULL)
             {
@@ -362,7 +364,7 @@
                 pDib = NULL;
             }
 
-            // free file-list
+            /* free file-list */
 
             if (pPngFileList != NULL)
             {
@@ -370,7 +372,7 @@
                 pPngFileList = NULL;
             }
 
-            // let's go ...
+            /* let's go ... */
 
             exit (0);
 
@@ -383,11 +385,11 @@
             else
                 CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
 
-            // invalidate the client area for later update
+            /* invalidate the client area for later update */
 
             InvalidateRect (hwnd, NULL, TRUE);
 
-            // display the PNG into the DIBitmap
+            /* display the PNG into the DIBitmap */
 
             DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
                 pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -398,7 +400,7 @@
             DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
             return 0;
 
-        } // end switch
+        } /* end switch */
 
         break;
 
@@ -450,10 +452,10 @@
      return FALSE ;
 }
 
-//---------------
-//  CenterAbout
-//---------------
-
+/*---------------
+ *  CenterAbout
+ *---------------
+ */
 BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
 {
    RECT    rChild, rParent, rWorkArea;
@@ -461,19 +463,19 @@
    int     xNew, yNew;
    BOOL  bResult;
 
-   // Get the Height and Width of the child window
+   /* Get the Height and Width of the child window */
    GetWindowRect (hwndChild, &rChild);
    wChild = rChild.right - rChild.left;
    hChild = rChild.bottom - rChild.top;
 
-   // Get the Height and Width of the parent window
+   /* Get the Height and Width of the parent window */
    GetWindowRect (hwndParent, &rParent);
    wParent = rParent.right - rParent.left;
    hParent = rParent.bottom - rParent.top;
 
-   // Get the limits of the 'workarea'
+   /* Get the limits of the 'workarea' */
    bResult = SystemParametersInfo(
-      SPI_GETWORKAREA,  // system parameter to query or set
+      SPI_GETWORKAREA,  /* system parameter to query or set */
       sizeof(RECT),
       &rWorkArea,
       0);
@@ -483,7 +485,7 @@
       rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
    }
 
-   // Calculate new X position, then adjust for workarea
+   /* Calculate new X position, then adjust for workarea */
    xNew = rParent.left + ((wParent - wChild) /2);
    if (xNew < rWorkArea.left) {
       xNew = rWorkArea.left;
@@ -491,7 +493,7 @@
       xNew = rWorkArea.right - wChild;
    }
 
-   // Calculate new Y position, then adjust for workarea
+   /* Calculate new Y position, then adjust for workarea */
    yNew = rParent.top  + ((hParent - hChild) /2);
    if (yNew < rWorkArea.top) {
       yNew = rWorkArea.top;
@@ -499,15 +501,15 @@
       yNew = rWorkArea.bottom - hChild;
    }
 
-   // Set it, and return
+   /* Set it, and return */
    return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
           SWP_NOZORDER);
 }
 
-//----------------
-//  BuildPngList
-//----------------
-
+/*----------------
+ *  BuildPngList
+ *----------------
+ */
 BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
      int *pFileIndex)
 {
@@ -523,7 +525,7 @@
     int                       i, ii;
     int                       j, jj;
 
-    // free previous file-list
+    /* free previous file-list */
 
     if (*ppFileList != NULL)
     {
@@ -531,7 +533,7 @@
         *ppFileList = NULL;
     }
 
-    // extract foldername, filename and search-name
+    /* extract foldername, filename and search-name */
 
     strcpy (szImgPathName, pstrPathName);
     strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
@@ -540,7 +542,7 @@
     *(strrchr (szImgFindName, '\\') + 1) = '\0';
     strcat (szImgFindName, "*.png");
 
-    // first cycle: count number of files in directory for memory allocation
+    /* first cycle: count number of files in directory for memory allocation */
 
     *pFileCount = 0;
 
@@ -554,11 +556,11 @@
     }
     FindClose(hFind);
 
-    // allocation memory for file-list
+    /* allocation memory for file-list */
 
     *ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
 
-    // second cycle: read directory and store filenames in file-list
+    /* second cycle: read directory and store filenames in file-list */
 
     hFind = FindFirstFile(szImgFindName, &finddata);
     bOk = (hFind != (HANDLE) -1);
@@ -580,7 +582,7 @@
     }
     FindClose(hFind);
 
-    // finally we must sort the file-list
+    /* finally we must sort the file-list */
 
     for (i = 0; i < *pFileCount - 1; i++)
     {
@@ -594,7 +596,7 @@
                 strcpy (*ppFileList + jj, *ppFileList + ii);
                 strcpy (*ppFileList + ii, szTmp);
 
-                // check if this was the current image that we moved
+                /* check if this was the current image that we moved */
 
                 if (*pFileIndex == i)
                     *pFileIndex = j;
@@ -608,9 +610,10 @@
     return TRUE;
 }
 
-//----------------
-//  SearchPngList
-//----------------
+/*----------------
+ *  SearchPngList
+ *----------------
+ */
 
 BOOL SearchPngList (
         TCHAR *pFileList, int FileCount, int *pFileIndex,
@@ -618,30 +621,30 @@
 {
     if (FileCount > 0)
     {
-        // get previous entry
-        
+        /* get previous entry */
+
         if (pstrPrevName != NULL)
         {
             if (*pFileIndex > 0)
                 *pFileIndex -= 1;
             else
                 *pFileIndex = FileCount - 1;
-            
+
             strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
         }
-        
-        // get next entry
-        
+
+        /* get next entry */
+
         if (pstrNextName != NULL)
         {
             if (*pFileIndex < FileCount - 1)
                 *pFileIndex += 1;
             else
                 *pFileIndex = 0;
-            
+
             strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
         }
-        
+
         return TRUE;
     }
     else
@@ -650,9 +653,10 @@
     }
 }
 
-//-----------------
-//  LoadImageFile
-//-----------------
+/*-----------------
+ *  LoadImageFile
+ *-----------------
+ */
 
 BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
                 png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
@@ -660,7 +664,7 @@
 {
     static TCHAR szTmp [MAX_PATH];
 
-    // if there's an existing PNG, free the memory
+    /* if there's an existing PNG, free the memory */
 
     if (*ppbImage)
     {
@@ -668,7 +672,7 @@
         *ppbImage = NULL;
     }
 
-    // Load the entire PNG into memory
+    /* Load the entire PNG into memory */
 
     SetCursor (LoadCursor (NULL, IDC_WAIT));
     ShowCursor (TRUE);
@@ -694,10 +698,10 @@
     return TRUE;
 }
 
-//----------------
-//  DisplayImage
-//----------------
-
+/*----------------
+ *  DisplayImage
+ *----------------
+ */
 BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
         BYTE **ppDiData, int cxWinSize, int cyWinSize,
         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
@@ -705,14 +709,14 @@
 {
     BYTE                       *pDib = *ppDib;
     BYTE                       *pDiData = *ppDiData;
-    // BITMAPFILEHEADER        *pbmfh;
+    /* BITMAPFILEHEADER        *pbmfh; */
     BITMAPINFOHEADER           *pbmih;
     WORD                        wDIRowBytes;
     png_color                   bkgBlack = {0, 0, 0};
     png_color                   bkgGray  = {127, 127, 127};
     png_color                   bkgWhite = {255, 255, 255};
 
-    // allocate memory for the Device Independant bitmap
+    /* allocate memory for the Device Independant bitmap */
 
     wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
 
@@ -733,7 +737,7 @@
     *ppDib = pDib;
     memset (pDib, 0, sizeof(BITMAPINFOHEADER));
 
-    // initialize the dib-structure
+    /* initialize the dib-structure */
 
     pbmih = (BITMAPINFOHEADER *) pDib;
     pbmih->biSize = sizeof(BITMAPINFOHEADER);
@@ -745,11 +749,11 @@
     pDiData = pDib + sizeof(BITMAPINFOHEADER);
     *ppDiData = pDiData;
 
-    // first fill bitmap with gray and image border
+    /* first fill bitmap with gray and image border */
 
     InitBitmap (pDiData, cxWinSize, cyWinSize);
 
-    // then fill bitmap with image
+    /* then fill bitmap with image */
 
     if (pbImage)
     {
@@ -762,16 +766,16 @@
     return TRUE;
 }
 
-//--------------
-//  InitBitmap
-//--------------
-
+/*--------------
+ *  InitBitmap
+ *--------------
+ */
 BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
 {
     BYTE *dst;
     int x, y, col;
 
-    // initialize the background with gray
+    /* initialize the background with gray */
 
     dst = pDiData;
     for (y = 0; y < cyWinSize; y++)
@@ -779,13 +783,13 @@
         col = 0;
         for (x = 0; x < cxWinSize; x++)
         {
-            // fill with GRAY
+            /* fill with GRAY */
             *dst++ = 127;
             *dst++ = 127;
             *dst++ = 127;
             col += 3;
         }
-        // rows start on 4 byte boundaries
+        /* rows start on 4 byte boundaries */
         while ((col % 4) != 0)
         {
             dst++;
@@ -796,10 +800,10 @@
     return TRUE;
 }
 
-//--------------
-//  FillBitmap
-//--------------
-
+/*--------------
+ *  FillBitmap
+ *--------------
+ */
 BOOL FillBitmap (
         BYTE *pDiData, int cxWinSize, int cyWinSize,
         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
@@ -824,11 +828,12 @@
         cxNewSize = cxWinSize - 2 * MARGIN;
         cyNewSize = cyWinSize - 2 * MARGIN;
 
-        // stretch the image to it's window determined size
+        /* stretch the image to it's window determined size */
 
-        // the following two are the same, but the first has side-effects
-        // because of rounding
-//      if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize))
+        /* the following two are mathematically the same, but the first
+         * has side-effects because of rounding
+         */
+/*      if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) */
         if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
         {
             cyNewSize = cxNewSize * cyImgSize / cxImgSize;
@@ -867,12 +872,12 @@
             }
         }
 
-        // calculate row-bytes
+        /* calculate row-bytes */
 
         wImgRowBytes = cImgChannels * cxNewSize;
         wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
 
-        // copy image to screen
+        /* copy image to screen */
 
         for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
         {
@@ -888,7 +893,7 @@
                 r = *src++;
                 g = *src++;
                 b = *src++;
-                *dst++ = b; /* note the reverse order */
+                *dst++ = b; /* note the reverse order  */
                 *dst++ = g;
                 *dst++ = r;
                 if (cImgChannels == 4)
@@ -898,7 +903,7 @@
             }
         }
 
-        // free memory
+        /* free memory */
 
         if (pStretchedImage != NULL)
         {
@@ -908,28 +913,28 @@
 
     }
 
-    // process the image not-stretched
+    /* process the image not-stretched */
 
     else
     {
-        // calculate the central position
+        /* calculate the central position */
 
         cxImgPos = (cxWinSize - cxImgSize) / 2;
         cyImgPos = (cyWinSize - cyImgSize) / 2;
 
-        // check for image larger than window
+        /* check for image larger than window */
 
         if (cxImgPos < MARGIN)
             cxImgPos = MARGIN;
         if (cyImgPos < MARGIN)
             cyImgPos = MARGIN;
 
-        // calculate both row-bytes
+        /* calculate both row-bytes */
 
         wImgRowBytes = cImgChannels * cxImgSize;
         wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
 
-        // copy image to screen
+        /* copy image to screen */
 
         for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
         {
@@ -945,7 +950,7 @@
                 r = *src++;
                 g = *src++;
                 b = *src++;
-                *dst++ = b; /* note the reverse order */
+                *dst++ = b; /* note the reverse order  */
                 *dst++ = g;
                 *dst++ = r;
                 if (cImgChannels == 4)
@@ -959,6 +964,7 @@
     return TRUE;
 }
 
-//-----------------
-//  end of source
-//-----------------
+/*-----------------
+ *  end of source
+ *-----------------
+ */
diff --git a/contrib/visupng/VisualPng.dsp b/contrib/visupng/VisualPng.dsp
index 53ef5a2..741ef6c 100644
--- a/contrib/visupng/VisualPng.dsp
+++ b/contrib/visupng/VisualPng.dsp
@@ -43,9 +43,9 @@
 # PROP Intermediate_Dir "Release"

 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

-# ADD BASE CPP /nologo /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c

+# ADD BASE CPP /nologo /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /FD /c

 # SUBTRACT BASE CPP /YX

-# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c

+# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /FD /c

 # SUBTRACT CPP /YX

 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32

@@ -77,9 +77,9 @@
 # PROP Intermediate_Dir "Debug"

 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

-# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c

+# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /FD /GZ /c

 # SUBTRACT BASE CPP /YX

-# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c

+# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /FD /GZ /c

 # SUBTRACT CPP /YX

 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32

diff --git a/contrib/visupng/resource.h b/contrib/visupng/resource.h
index 611dd03..b62176d 100644
--- a/contrib/visupng/resource.h
+++ b/contrib/visupng/resource.h
@@ -12,7 +12,7 @@
 #define IDM_HELP_ABOUT                  40008
 
 // Next default values for new objects
-// 
+//
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        113