zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/README.txt b/ap/lib/libpng/libpng-1.6.37/contrib/README.txt
new file mode 100755
index 0000000..97963c6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/README b/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/README
new file mode 100755
index 0000000..b4248cf
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/android-ndk.c b/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/android-ndk.c
new file mode 100755
index 0000000..fb3a489
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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
+ *    https://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/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/linux-auxv.c b/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/linux-auxv.c
new file mode 100755
index 0000000..4d26bd3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/linux.c b/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/linux.c
new file mode 100755
index 0000000..a9bc360
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/arm-neon/linux.c
@@ -0,0 +1,161 @@
+/* contrib/arm-neon/linux.c
+ *
+ * Last changed in libpng 1.6.31 [July 27, 2017]
+ * Copyright (c) 2014, 2017 Glenn Randers-Pehrson
+ * Written by John Bowler, 2014, 2017.
+ *
+ * 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;
+               /* FALLTHROUGH */
+
+            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;
+               /* FALLTHROUGH */
+
+            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;
+               /* FALLTHROUGH */
+
+            case Neon:
+               /* Look for 'neon' tag */
+               if ((ch & ~0x20) == ch_neon[counter])
+               {
+                  if (++counter == (sizeof ch_neon))
+                     state = HaveNeon;
+                  break;
+               }
+
+               state = SkipTag;
+               /* FALLTHROUGH */
+
+            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/ap/lib/libpng/libpng-1.6.37/contrib/conftest/README b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/README
new file mode 100755
index 0000000..0f47279
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/conftest/pngcp.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/pngcp.dfa
new file mode 100755
index 0000000..31c411d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/pngcp.dfa
@@ -0,0 +1,57 @@
+# pngcp.dfa
+#  Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2016
+# 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 support for pngcp.  This means just png_read_png,
+# png_write_png and small number of configuration settings.
+#
+everything = off
+
+# This option is specific to this configuration; it adds a #define to the
+# generated pnglibconf.h which turns on the (not portable) timing option for
+# pngcp.  Note that any option is automatically preceded by PNG_; there is no
+# way round this and this is deliberate.
+option PNGCP_TIMING
+
+# Because of the everything off above the option must also be turned on.  This
+# may not be done in one step because it is safer and avoids mis-spelled options
+# in user .dfa files to error out if an unrecognized option is turned on.
+option PNGCP_TIMING on
+
+# Options to turn on png_read_png and png_write_png:
+option INFO_IMAGE on
+option SEQUENTIAL_READ on
+option EASY_ACCESS on
+option WRITE on
+option WRITE_16BIT on
+option WRITE_FILTER on
+
+# pngcp needs this to preserve unknown chunks, switching all these on means that
+# pngcp can work without explicit known chunk reading support
+option UNKNOWN_CHUNKS on
+option SET_UNKNOWN_CHUNKS on
+option HANDLE_AS_UNKNOWN on
+option SAVE_UNKNOWN_CHUNKS on
+option WRITE_UNKNOWN_CHUNKS on
+
+# pngcp needs this to handle palette files with invalid indices:
+option CHECK_FOR_INVALID_INDEX on
+option GET_PALETTE_MAX on
+
+# Pre-libpng 1.7 pngcp has to stash text chunks manually, post 1.7 without this
+# text chunks should be handled as unknown ok.
+option TEXT on
+
+# this is used to turn off limits:
+option USER_LIMITS on
+option SET_USER_LIMITS on
+
+# these are just required for specific customizations
+option WRITE_CUSTOMIZE_ZTXT_COMPRESSION on
+option WRITE_CUSTOMIZE_COMPRESSION on
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/conftest/read.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/read.dfa
new file mode 100755
index 0000000..21e88d0
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/conftest/s_read.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/s_read.dfa
new file mode 100755
index 0000000..cb1ce0b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/conftest/s_write.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/s_write.dfa
new file mode 100755
index 0000000..e540a46
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/conftest/simple.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/simple.dfa
new file mode 100755
index 0000000..0419333
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/conftest/write.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/conftest/write.dfa
new file mode 100755
index 0000000..3319aab
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/examples/README.txt b/ap/lib/libpng/libpng-1.6.37/contrib/examples/README.txt
new file mode 100755
index 0000000..48dab4f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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 at acm.org>
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/examples/iccfrompng.c b/ap/lib/libpng/libpng-1.6.37/contrib/examples/iccfrompng.c
new file mode 100755
index 0000000..00056ab
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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 precedes 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/ap/lib/libpng/libpng-1.6.37/contrib/examples/pngpixel.c b/ap/lib/libpng/libpng-1.6.37/contrib/examples/pngpixel.c
new file mode 100755
index 0000000..9185d51
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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)(((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)
+{
+   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:
+         {
+            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/ap/lib/libpng/libpng-1.6.37/contrib/examples/pngtopng.c b/ap/lib/libpng/libpng-1.6.37/contrib/examples/pngtopng.c
new file mode 100755
index 0000000..904b972
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/examples/pngtopng.c
@@ -0,0 +1,98 @@
+/*- pngtopng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011, 2017.
+ * 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.
+ *
+ * Last changed in libpng 1.6.29 [March 16, 2017]
+ *
+ * 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);
+            }
+
+            else
+               fprintf(stderr, "pngtopng: read %s: %s\n", argv[1],
+                   image.message);
+
+            free(buffer);
+         }
+
+         else
+         {
+            fprintf(stderr, "pngtopng: 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 and so libpng
+             * has not got to the point where it can do cleanup.
+             */
+            png_image_free(&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/ap/lib/libpng/libpng-1.6.37/contrib/examples/simpleover.c b/ap/lib/libpng/libpng-1.6.37/contrib/examples/simpleover.c
new file mode 100755
index 0000000..59dd313
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/COPYING b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/COPYING
new file mode 100755
index 0000000..a3e9774
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/COPYING
@@ -0,0 +1,340 @@
+                    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
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            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
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            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
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/LICENSE b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/LICENSE
new file mode 100755
index 0000000..d956717
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/LICENSE
@@ -0,0 +1,50 @@
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2008 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.mingw32 b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.mingw32
new file mode 100755
index 0000000..3a3ff60
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.mingw32
@@ -0,0 +1,131 @@
+# Sample makefile for rpng-win / rpng2-win / wpng using mingw32-gcc and make.
+# Greg Roelofs
+# Last modified:  2 June 2007
+#
+#	The programs built by this makefile are described in the book,
+#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
+#	Associates, 1999).  Go buy a copy, eh?  Well, OK, it's not
+#	generally for sale anymore, but it's the thought that counts,
+#	right?  (Hint:  http://www.libpng.org/pub/png/book/ )
+#
+# Invoke this makefile from a DOS-prompt window via:
+#
+#	make -f Makefile.mingw32
+#
+# 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 PNGDIR and ZDIR macros below).  It makes no assumptions
+# at all about the mingw32 installation tree (W32DIR).  Edit as appropriate.
+#
+# Note that the names of the dynamic and static libpng and zlib libraries
+# used below may change in later releases of the libraries.  This makefile
+# builds both statically and dynamically linked executables by default.
+# (You need only one set, but for testing it can be handy to have both.)
+
+
+# macros --------------------------------------------------------------------
+
+#PNGDIR = ../..#		for libpng-x.y.z/contrib/gregbook builds
+PNGDIR = ../libpng-win32
+PNGINC = -I$(PNGDIR)
+PNGLIBd = $(PNGDIR)/libpng.dll.a	# dynamically linked
+PNGLIBs = $(PNGDIR)/libpng.a		# statically linked, local libpng
+
+#ZDIR = ../../../zlib-win32#	for libpng-x.y.z/contrib/gregbook builds
+ZDIR = ../zlib-win32
+ZINC = -I$(ZDIR)
+ZLIBd = $(ZDIR)/libzdll.a
+ZLIBs = $(ZDIR)/libz.a
+
+# change this to be the path where mingw32 installs its stuff:
+W32DIR =
+#W32DIR = /usr/local/cross-tools/i386-mingw32msvc
+W32INC = -I$(W32DIR)/include
+W32LIB = $(W32DIR)/lib/libuser32.a $(W32DIR)/lib/libgdi32.a
+
+CC = gcc
+#CC = i386-mingw32msvc-gcc #	e.g., Linux -> Win32 cross-compilation
+LD = $(CC)
+RM = rm -f
+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)
+O = .o
+E = .exe
+
+INCS = $(PNGINC) $(ZINC) $(W32INC)
+RLIBSd = $(PNGLIBd) $(ZLIBd) $(W32LIB) -lm
+RLIBSs = $(PNGLIBs) $(ZLIBs) $(W32LIB) -lm
+WLIBSd = $(PNGLIBd) $(ZLIBd)
+WLIBSs = $(PNGLIBs) $(ZLIBs)
+
+RPNG   = rpng-win
+RPNG2  = rpng2-win
+WPNG   = wpng
+
+ROBJSd  = $(RPNG)$(O) readpng.pic$(O)
+ROBJS2d = $(RPNG2)$(O) readpng2.pic$(O)
+WOBJSd  = $(WPNG)$(O) writepng.pic$(O)
+
+RPNGs  = $(RPNG)-static
+RPNG2s = $(RPNG2)-static
+WPNGs  = $(WPNG)-static
+
+ROBJSs  = $(RPNG)$(O) readpng$(O)
+ROBJS2s = $(RPNG2)$(O) readpng2$(O)
+WOBJSs  = $(WPNG)$(O) writepng$(O)
+
+STATIC_EXES  = $(RPNGs)$(E) $(RPNG2s)$(E) $(WPNGs)$(E)
+DYNAMIC_EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)
+
+EXES = $(STATIC_EXES) $(DYNAMIC_EXES)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+%.pic$(O): %.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) -DPNG_BUILD_DLL -o $@ $<
+
+
+# dependencies --------------------------------------------------------------
+
+all:  $(EXES)
+
+$(RPNGs)$(E): $(ROBJSs)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJSs) $(RLIBSs)
+
+$(RPNG)$(E): $(ROBJSd)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJSd) $(RLIBSd)
+
+$(RPNG2s)$(E): $(ROBJS2s)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS2s) $(RLIBSs)
+
+$(RPNG2)$(E): $(ROBJS2d)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS2d) $(RLIBSd)
+
+$(WPNGs)$(E): $(WOBJSs)
+	$(LD) $(LDFLAGS) -o $@ $(WOBJSs) $(WLIBSs)
+
+$(WPNG)$(E): $(WOBJSd)
+	$(LD) $(LDFLAGS) -o $@ $(WOBJSd) $(WLIBSd)
+
+$(RPNG)$(O):	$(RPNG).c readpng.h
+$(RPNG2)$(O):	$(RPNG2).c readpng2.h
+$(WPNG)$(O):	$(WPNG).c writepng.h
+
+readpng$(O) readpng.pic$(O):	readpng.c readpng.h
+readpng2$(O) readpng2.pic$(O):	readpng2.c readpng2.h
+writepng$(O) writepng.pic$(O):	writepng.c writepng.h
+
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+	$(RM) $(EXES)
+	$(RM) $(ROBJSs) $(ROBJS2s) $(WOBJSs)
+	$(RM) $(ROBJSd) $(ROBJS2d) $(WOBJSd)
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.sgi b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.sgi
new file mode 100755
index 0000000..94d61b4
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.sgi
@@ -0,0 +1,105 @@
+# Sample makefile for rpng-x / rpng2-x / wpng for SGI using cc and make.
+# Greg Roelofs
+# Last modified:  7 March 2002
+#
+#	The programs built by this makefile are described in the book,
+#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
+#	Associates, 1999).  Go buy a copy, eh?  Buy some for friends
+#	and family, too.  (Not that this is a blatant plug or anything.)
+#
+# Invoke this makefile from a shell prompt in the usual way; for example:
+#
+#	make -f Makefile.sgi
+#
+# This makefile assumes libpng and zlib have already been built or downloaded
+# and are both installed in /usr/local/{include,lib} (as indicated by the
+# PNG* and Z* macros below).  Edit as appropriate--choose only ONE each of
+# the PNGINC, PNGLIB, ZINC and ZLIB lines.
+#
+# This makefile builds dynamically linked executables (against libpng and zlib,
+# that is), but that can be changed by uncommenting the appropriate PNGLIB and
+# ZLIB lines.
+
+
+# macros --------------------------------------------------------------------
+
+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
+#PNGLIB = ../../libpng.a
+
+ZINC = -I/usr/local/include
+ZLIB = -L/usr/local/lib -lz		# dynamically linked against zlib
+#ZLIB = /usr/local/lib/libz.a		# statically linked against zlib
+#ZINC = -I../zlib
+#ZLIB = -L../zlib -lz
+#ZLIB = ../../../zlib/libz.a
+
+XINC = -I/usr/include/X11		# old-style, stock X distributions
+XLIB = -L/usr/lib/X11 -lX11
+#XINC = -I/usr/openwin/include    	# Sun workstations (OpenWindows)
+#XLIB = -L/usr/openwin/lib -lX11
+#XINC = -I/usr/X11R6/include		# new X distributions (XFree86, etc.)
+#XLIB = -L/usr/X11R6/lib -lX11
+
+INCS = $(PNGINC) $(ZINC) $(XINC)
+RLIBS = $(PNGLIB) $(ZLIB) $(XLIB) -lm
+WLIBS = $(PNGLIB) $(ZLIB)
+
+CC = cc
+LD = cc
+RM = rm -f
+# ABI must be the same as that used to build libpng.
+ABI =
+CPPFLAGS =
+CFLAGS = $(ABI) -O -fullwarn $(INCS)
+LDFLAGS = $(ABI)
+O = .o
+E =
+
+RPNG  = rpng-x
+RPNG2 = rpng2-x
+WPNG  = wpng
+
+ROBJS  = $(RPNG)$(O) readpng$(O)
+ROBJS2 = $(RPNG2)$(O) readpng2$(O)
+WOBJS  = $(WPNG)$(O) writepng$(O)
+
+EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+
+# dependencies --------------------------------------------------------------
+
+all:  $(EXES)
+
+$(RPNG)$(E): $(ROBJS)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBS)
+
+$(RPNG2)$(E): $(ROBJS2)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBS)
+
+$(WPNG)$(E): $(WOBJS)
+	$(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBS)
+
+$(RPNG)$(O):	$(RPNG).c readpng.h
+$(RPNG2)$(O):	$(RPNG2).c readpng2.h
+$(WPNG)$(O):	$(WPNG).c writepng.h
+
+readpng$(O):	readpng.c readpng.h
+readpng2$(O):	readpng2.c readpng2.h
+writepng$(O):	writepng.c writepng.h
+
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.unx b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.unx
new file mode 100755
index 0000000..1a73e03
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.unx
@@ -0,0 +1,134 @@
+# Sample makefile for rpng-x / rpng2-x / wpng using gcc and make.
+# Greg Roelofs
+# Last modified:  2 June 2007
+#
+#	The programs built by this makefile are described in the book,
+#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
+#	Associates, 1999).  Go buy a copy, eh?  Well, OK, it's not
+#	generally for sale anymore, but it's the thought that counts,
+#	right?  (Hint:  http://www.libpng.org/pub/png/book/ )
+#
+# Invoke this makefile from a shell prompt in the usual way; for example:
+#
+#	make -f Makefile.unx
+#
+# This makefile assumes libpng and zlib have already been built or downloaded
+# and are installed in /usr/local/{include,lib} or as otherwise indicated by
+# the PNG* and Z* macros below.  Edit as appropriate--choose only ONE each of
+# the PNGINC, PNGLIBd, PNGLIBs, ZINC, ZLIBd and ZLIBs lines.
+#
+# This makefile builds both dynamically and statically linked executables
+# (against libpng and zlib, that is), but that can be changed by modifying
+# the "EXES =" line.  (You need only one set, but for testing it can be handy
+# to have both.)
+
+
+# macros --------------------------------------------------------------------
+
+#PNGDIR = /usr/local/lib
+#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) -lpng16	# dynamically linked
+PNGLIBs = $(PNGDIR)/libpng.a		# statically linked, local libpng
+
+ZDIR = /usr/local/lib
+#ZDIR = /usr/lib64
+ZINC = -I/usr/local/include
+ZLIBd = -L$(ZDIR) -lz			# dynamically linked against zlib
+ZLIBs = $(ZDIR)/libz.a			# statically linked against zlib
+# or:
+#ZDIR = ../zlib
+#ZINC = -I$(ZDIR)
+#ZLIBd = -Wl,-rpath,$(ZDIR) -L$(ZDIR) -lz  # -rpath allows in-place testing
+#ZLIBs = $(ZDIR)/libz.a
+
+#XINC = -I/usr/include			# old-style, stock X distributions
+#XLIB = -L/usr/lib/X11 -lX11		#  (including SGI IRIX)
+#XINC = -I/usr/openwin/include		# Sun workstations (OpenWindows)
+#XLIB = -L/usr/openwin/lib -lX11
+XINC = -I/usr/X11R6/include		# new X distributions (X.org, etc.)
+XLIB = -L/usr/X11R6/lib -lX11
+#XLIB = -L/usr/X11R6/lib64 -lX11	# e.g., Red Hat on AMD64
+
+INCS = $(PNGINC) $(ZINC) $(XINC)
+RLIBSd = $(PNGLIBd) $(ZLIBd) $(XLIB) -lm
+RLIBSs = $(PNGLIBs) $(ZLIBs) $(XLIB) -lm
+WLIBSd = $(PNGLIBd) $(ZLIBd) -lm
+WLIBSs = $(PNGLIBs) $(ZLIBs) -lm
+
+CC = gcc
+LD = gcc
+RM = rm -f
+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, -Wextra, and -W can also be used]
+LDFLAGS =
+O = .o
+E =
+
+RPNG   = rpng-x
+RPNG2  = rpng2-x
+WPNG   = wpng
+
+RPNGs  = $(RPNG)-static
+RPNG2s = $(RPNG2)-static
+WPNGs  = $(WPNG)-static
+
+ROBJS  = $(RPNG)$(O) readpng$(O)
+ROBJS2 = $(RPNG2)$(O) readpng2$(O)
+WOBJS  = $(WPNG)$(O) writepng$(O)
+
+STATIC_EXES  = $(RPNGs)$(E) $(RPNG2s)$(E) $(WPNGs)$(E)
+DYNAMIC_EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)
+
+EXES = $(STATIC_EXES) $(DYNAMIC_EXES)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+
+# dependencies --------------------------------------------------------------
+
+all:  $(EXES)
+
+$(RPNGs)$(E): $(ROBJS)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBSs)
+
+$(RPNG)$(E): $(ROBJS)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBSd)
+
+$(RPNG2s)$(E): $(ROBJS2)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBSs)
+
+$(RPNG2)$(E): $(ROBJS2)
+	$(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBSd)
+
+$(WPNGs)$(E): $(WOBJS)
+	$(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBSs)
+
+$(WPNG)$(E): $(WOBJS)
+	$(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBSd)
+
+$(RPNG)$(O):	$(RPNG).c readpng.h
+$(RPNG2)$(O):	$(RPNG2).c readpng2.h
+$(WPNG)$(O):	$(WPNG).c writepng.h
+
+readpng$(O):	readpng.c readpng.h
+readpng2$(O):	readpng2.c readpng2.h
+writepng$(O):	writepng.c writepng.h
+
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.w32 b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.w32
new file mode 100755
index 0000000..ab7dcf7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/Makefile.w32
@@ -0,0 +1,114 @@
+# Sample makefile for rpng-win / rpng2-win / wpng using MSVC and NMAKE.
+# Greg Roelofs
+# Last modified:  2 June 2007
+#
+#	The programs built by this makefile are described in the book,
+#	"PNG:  The Definitive Guide," by Greg Roelofs (O'Reilly and
+#	Associates, 1999).  Go buy a copy, eh?  Well, OK, it's not
+#	generally for sale anymore, but it's the thought that counts,
+#	right?  (Hint:  http://www.libpng.org/pub/png/book/ )
+#
+# Invoke this makefile from a DOS prompt window via:
+#
+#	%devstudio%\vc\bin\vcvars32.bat
+#	nmake -nologo -f Makefile.w32
+#
+# where %devstudio% is the installation directory for MSVC / DevStudio.  If
+# you get "environment out of space" errors, create a desktop shortcut with
+# "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.
+#
+# Note that the names of the dynamic and static libpng and zlib libraries
+# used below may change in later releases of the libraries.  This makefile
+# builds statically linked executables, but that can be changed by uncom-
+# menting the appropriate PNGLIB and ZLIB lines.
+
+!include <ntwin32.mak>
+
+
+# macros --------------------------------------------------------------------
+
+PNGPATH = ../libpng
+PNGINC = -I$(PNGPATH)
+#PNGLIB = $(PNGPATH)/pngdll.lib
+PNGLIB = $(PNGPATH)/libpng.lib
+
+ZPATH = ../zlib
+ZINC = -I$(ZPATH)
+#ZLIB = $(ZPATH)/zlibdll.lib
+ZLIB = $(ZPATH)/zlibstat.lib
+
+WINLIBS = -defaultlib:user32.lib gdi32.lib
+# ["real" apps may also need comctl32.lib, comdlg32.lib, winmm.lib, etc.]
+
+INCS = $(PNGINC) $(ZINC)
+RLIBS = $(PNGLIB) $(ZLIB) $(WINLIBS)
+WLIBS = $(PNGLIB) $(ZLIB)
+
+CC = cl
+LD = link
+RM = del
+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
+E = .exe
+
+RLDFLAGS = -nologo -subsystem:windows
+WLDFLAGS = -nologo
+
+RPNG  = rpng-win
+RPNG2 = rpng2-win
+WPNG  = wpng
+
+ROBJS  = $(RPNG)$(O) readpng$(O)
+ROBJS2 = $(RPNG2)$(O) readpng2$(O)
+WOBJS  = $(WPNG)$(O) writepng$(O)
+
+EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+
+# dependencies --------------------------------------------------------------
+
+all:  $(EXES)
+
+$(RPNG)$(E): $(ROBJS)
+	$(LD) $(RLDFLAGS) -out:$@ $(ROBJS) $(RLIBS)
+
+$(RPNG2)$(E): $(ROBJS2)
+	$(LD) $(RLDFLAGS) -out:$@ $(ROBJS2) $(RLIBS)
+
+$(WPNG)$(E): $(WOBJS)
+	$(LD) $(WLDFLAGS) -out:$@ $(WOBJS) $(WLIBS)
+
+$(RPNG)$(O):	$(RPNG).c readpng.h
+$(RPNG2)$(O):	$(RPNG2).c readpng2.h
+$(WPNG)$(O):	$(WPNG).c writepng.h
+
+readpng$(O):	readpng.c readpng.h
+readpng2$(O):	readpng2.c readpng2.h
+writepng$(O):	writepng.c writepng.h
+
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+#	ideally we could just do this:
+#	$(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS)
+#	...but the Windows "DEL" command is none too bright, so:
+	$(RM) r*$(E)
+	$(RM) w*$(E)
+	$(RM) r*$(O)
+	$(RM) w*$(O)
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/README b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/README
new file mode 100755
index 0000000..90e28f7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/README
@@ -0,0 +1,186 @@
+                     ===========================
+                      PNG: The Definitive Guide
+                     ===========================
+
+                             Source Code
+
+Chapters 13, 14 and 15 of "PNG: The Definitive Guide" discuss three free,
+cross-platform demo programs that show how to use the libpng reference
+library:  rpng, rpng2 and wpng.  rpng and rpng2 are viewers; the first is
+a very simple example that that shows how a standard file-viewer might use
+libpng, while the second is designed to process streaming data and shows
+how a web browser might be written.  wpng is a simple command-line program
+that reads binary PGM and PPM files (the ``raw'' grayscale and RGB subsets
+of PBMPLUS/NetPBM) and converts them to PNG.
+
+The source code for all three demo programs currently compiles under
+Unix, OpenVMS, and 32-bit Windows.  (Special thanks to Martin Zinser,
+zinser at decus.de, for making the necessary changes for OpenVMS and for
+providing an appropriate build script.)  Build instructions can be found
+below.
+
+Files:
+
+   README             this file
+   LICENSE            terms of distribution and reuse (BSD-like or GNU GPL)
+   COPYING            GNU General Public License (GPL)
+
+   Makefile.unx       Unix makefile
+   Makefile.w32       Windows (MSVC) makefile
+   makevms.com        OpenVMS build script
+
+   rpng-win.c         Windows front end for the basic viewer
+   rpng-x.c           X Window System (Unix, OpenVMS) front end
+   readpng.c          generic back end for the basic viewer
+   readpng.h          header file for the basic viewer
+
+   rpng2-win.c        Windows front end for the progressive viewer
+   rpng2-x.c          X front end for the progressive viewer
+   readpng2.c         generic back end for the progressive viewer
+   readpng2.h         header file for the progressive viewer
+
+   wpng.c             generic (text) front end for the converter
+   writepng.c         generic back end for the converter
+   writepng.h         header file for the converter
+
+   toucan.png         transparent PNG for testing (by Stefan Schneider)
+
+Note that, although the programs are designed to be functional, their
+primary purpose is to illustrate how to use libpng to add PNG support to
+other programs.  As such, their user interfaces are crude and definitely
+are not intended for everyday use.
+
+Please see http://www.libpng.org/pub/png/pngbook.html for further infor-
+mation and links to the latest version of the source code, and Chapters
+13-15 of the book for detailed discussion of the three programs.
+
+Greg Roelofs
+https://pobox.com/~newt/greg_contact.html
+16 March 2008
+
+
+BUILD INSTRUCTIONS
+
+ - Prerequisites (in order of compilation):
+
+      - zlib            https://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,
+     you need to download and build both zlib (on which libpng depends) and
+     libpng.  A common build setup is to place the zlib, libpng and pngbook
+     subdirectory trees ("folders") in the same parent directory.  Then the
+     libpng build can refer to files in ../zlib (or ..\zlib or [-.zlib]),
+     and similarly for the pngbook build.
+
+     Note that all three packages are designed to be built from a command
+     line by default; those who wish to use a graphical or other integrated
+     development environments are on their own.
+
+
+ - Unix:
+
+     Unpack the latest pngbook sources (which should correspond to this
+     README file) into a directory and change into that directory.
+
+     Copy Makefile.unx to Makefile and edit the PNG* and Z* variables
+     appropriately (possibly also the X* variables if necessary).
+
+     make
+
+     There is no "install" target, so copy the three executables somewhere
+     in your path or run them from the current directory.  All three will
+     print a basic usage screen when run without any command-line arguments;
+     see the book for more details.
+
+
+ - Windows:
+
+     Unpack the latest pngbook sources (which should correspond to this
+     README file) into a folder, open a "DOS shell" or "command prompt"
+     or equivalent command-line window, and cd into the folder where you
+     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 /
+     DevStudio.  If you get "environment out of space" errors under 95/98,
+     create a desktop shortcut with "c:\windows\command.com /e:4096" as
+     the program command line and set the working directory to the pngbook
+     directory.  Then double-click to open the new DOS-prompt window with
+     a bigger environment and retry the commands above.
+
+     Copy Makefile.w32 to Makefile and edit the PNGPATH and ZPATH variables
+     appropriately (possibly also the "INC" and "LIB" variables if needed).
+     Note that the names of the dynamic and static libpng and zlib libraries
+     used in the makefile may change in later releases of the libraries.
+     Also note that, as of libpng version 1.0.5, MSVC DLL builds do not work.
+     This makefile therefore builds statically linked executables, but if
+     the DLL problems ever get fixed, uncommenting the appropriate PNGLIB
+     and ZLIB lines will build dynamically linked executables instead.
+
+     Do the build by typing
+
+        nmake
+
+     The result should be three executables:  rpng-win.exe, rpng2-win.exe,
+     and wpng.exe.  Copy them somewhere in your PATH or run them from the
+     current folder.  Like the Unix versions, the two windowed programs
+     (rpng and rpng2) now display a usage screen in a console window when
+     invoked without command-line arguments; this is new behavior as of
+     the June 2001 release.  Note that the programs use the Unix-style "-"
+     character to specify options, instead of the more common DOS/Windows
+     "/" character.  (For example:  "rpng2-win -bgpat 4 foo.png", not
+     "rpng2-win /bgpat 4 foo.png")
+
+
+ - OpenVMS:
+
+     Unpack the pngbook sources into a subdirectory and change into that
+     subdirectory.
+
+     Edit makevms.com appropriately, specifically the zpath and pngpath
+     variables.
+
+     @makevms
+
+     To run the programs, they probably first need to be set up as "foreign
+     symbols," with "disk" and "dir" set appropriately:
+
+     $ rpng  == "$disk:[dir]rpng-x.exe"
+     $ rpng2 == "$disk:[dir]rpng2-x.exe"
+     $ wpng  == "$disk:[dir]wpng.exe"
+
+     All three will print a basic usage screen when run without any command-
+     line arguments; see the book for more details.  Note that the options
+     style is Unix-like, i.e., preceded by "-" rather than "/".
+
+
+RUNNING THE PROGRAMS:  (VERY) BRIEF INTRO
+
+     rpng is a simple PNG viewer that can display transparent PNGs with a
+     specified background color; for example,
+
+        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
+
+     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
+
+     would convert the specified PPM file (using redirection) to PNG, auto-
+     matically setting the PNG modification-time chunk.
+
+     All options can be abbreviated to the shortest unique value; for example,
+     "-bgc" for -bgcolor (versus "-bgp" for -bgpat), or "-g" for -gamma.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/makevms.com b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/makevms.com
new file mode 100755
index 0000000..f32bcab
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/makevms.com
@@ -0,0 +1,132 @@
+$!------------------------------------------------------------------------------
+$! make "PNG: The Definitive Guide" demo programs (for X) under OpenVMS
+$!
+$! Script created by Martin Zinser for libpng; modified by Greg Roelofs
+$! for standalone pngbook source distribution.
+$!
+$!
+$!    Set locations where zlib and libpng sources live.
+$!
+$ zpath   = ""
+$ pngpath = ""
+$!
+$ if f$search("[---.zlib]zlib.h").nes."" then zpath = "[---.zlib]"
+$ if f$search("[--]png.h").nes."" then pngpath = "[--]"
+$!
+$ if f$search("[-.zlib]zlib.h").nes."" then zpath = "[-.zlib]"
+$ if f$search("[-.libpng]png.h").nes."" then pngpath = "[-.libpng]"
+$!
+$ if zpath .eqs. ""
+$ then
+$   write sys$output "zlib include not found. Exiting..."
+$   exit 2
+$ endif
+$!
+$ if pngpath .eqs. ""
+$ then
+$   write sys$output "libpng include not found. Exiting..."
+$   exit 2
+$ endif
+$!
+$!    Look for the compiler used.
+$!
+$ ccopt="/include=(''zpath',''pngpath')"
+$ if f$getsyi("HW_MODEL").ge.1024
+$ then
+$  ccopt = "/prefix=all"+ccopt
+$  comp  = "__decc__=1"
+$  if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$ else
+$  if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs.""
+$   then
+$    if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$    if f$search("SYS$SYSTEM:VAXC.EXE").eqs.""
+$     then
+$      comp  = "__gcc__=1"
+$      CC :== GCC
+$     else
+$      comp = "__vaxc__=1"
+$     endif
+$   else
+$    if f$trnlnm("SYS").eqs."" then define sys decc$library_include:
+$    ccopt = "/decc/prefix=all"+ccopt
+$    comp  = "__decc__=1"
+$  endif
+$ endif
+$ open/write lopt lib.opt
+$ write lopt "''pngpath'libpng.olb/lib"
+$ write lopt "''zpath'libz.olb/lib"
+$ close lopt
+$ open/write xopt x11.opt
+$ write xopt "sys$library:decw$xlibshr.exe/share"
+$ close xopt
+$!
+$!    Build 'em.
+$!
+$ write sys$output "Compiling PNG book programs ..."
+$   CALL MAKE readpng.OBJ "cc ''CCOPT' readpng" -
+	readpng.c readpng.h
+$   CALL MAKE readpng2.OBJ "cc ''CCOPT' readpng2" -
+	readpng2.c readpng2.h
+$   CALL MAKE writepng.OBJ "cc ''CCOPT' writepng" -
+	writepng.c writepng.h
+$   write sys$output "Building rpng-x..."
+$   CALL MAKE rpng-x.OBJ "cc ''CCOPT' rpng-x" -
+	rpng-x.c readpng.h
+$   call make rpng-x.exe -
+	"LINK rpng-x,readpng,lib.opt/opt,x11.opt/opt" -
+	rpng-x.obj readpng.obj
+$   write sys$output "Building rpng2-x..."
+$   CALL MAKE rpng2-x.OBJ "cc ''CCOPT' rpng2-x" -
+	rpng2-x.c readpng2.h
+$   call make rpng2-x.exe -
+	"LINK rpng2-x,readpng2,lib.opt/opt,x11.opt/opt" -
+	rpng2-x.obj readpng2.obj
+$   write sys$output "Building wpng..."
+$   CALL MAKE wpng.OBJ "cc ''CCOPT' wpng" -
+	wpng.c writepng.h
+$   call make wpng.exe -
+	"LINK wpng,writepng,lib.opt/opt" -
+	wpng.obj writepng.obj
+$ exit
+$!
+$!
+$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
+$ V = 'F$Verify(0)
+$! P1 = What we are trying to make
+$! P2 = Command to make it
+$! P3 - P8  What it depends on
+$
+$ If F$Search(P1) .Eqs. "" Then Goto Makeit
+$ Time = F$CvTime(F$File(P1,"RDT"))
+$arg=3
+$Loop:
+$       Argument = P'arg
+$       If Argument .Eqs. "" Then Goto Exit
+$       El=0
+$Loop2:
+$       File = F$Element(El," ",Argument)
+$       If File .Eqs. " " Then Goto Endl
+$       AFile = ""
+$Loop3:
+$       OFile = AFile
+$       AFile = F$Search(File)
+$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
+$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
+$       Goto Loop3
+$NextEL:
+$       El = El + 1
+$       Goto Loop2
+$EndL:
+$ arg=arg+1
+$ If arg .Le. 8 Then Goto Loop
+$ Goto Exit
+$
+$Makeit:
+$ VV=F$VERIFY(0)
+$ write sys$output P2
+$ 'P2
+$ VV='F$Verify(VV)
+$Exit:
+$ If V Then Set Verify
+$ENDSUBROUTINE
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng.c
new file mode 100755
index 0000000..fad9b53
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng.c
@@ -0,0 +1,323 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                              readpng.c
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2007,2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "png.h"        /* libpng header */
+#include "readpng.h"    /* typedefs, common macros, public prototypes */
+
+/* future versions of libpng will provide this macro: */
+#ifndef png_jmpbuf
+#  define png_jmpbuf(png_ptr)   ((png_ptr)->jmpbuf)
+#endif
+
+
+static png_structp png_ptr = NULL;
+static png_infop info_ptr = NULL;
+
+png_uint_32  width, height;
+int  bit_depth, color_type;
+uch  *image_data = NULL;
+
+
+void readpng_version_info(void)
+{
+    fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
+      PNG_LIBPNG_VER_STRING, png_libpng_ver);
+    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+      ZLIB_VERSION, zlib_version);
+}
+
+
+/* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */
+
+int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight)
+{
+    uch sig[8];
+
+
+    /* first do a quick check that the file really is a PNG image; could
+     * have used slightly more general png_sig_cmp() function instead */
+
+    fread(sig, 1, 8, infile);
+    if (png_sig_cmp(sig, 0, 8))
+        return 1;   /* bad signature */
+
+
+    /* could pass pointers to user-defined error handlers instead of NULLs: */
+
+    png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), NULL, NULL,
+        NULL);
+    if (!png_ptr)
+        return 4;   /* out of memory */
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 4;   /* out of memory */
+    }
+
+
+    /* we could create a second info struct here (end_info), but it's only
+     * useful if we want to keep pre- and post-IDAT chunk info separated
+     * (mainly for PNG-aware image editors and converters) */
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 2;
+    }
+
+
+    png_init_io(png_ptr, infile);
+    png_set_sig_bytes(png_ptr, 8);  /* we already read the 8 signature bytes */
+
+    png_read_info(png_ptr, info_ptr);  /* read all PNG info up to image data */
+
+
+    /* alternatively, could make separate calls to png_get_image_width(),
+     * etc., but want bit_depth and color_type for later [don't care about
+     * compression_type and filter_type => NULLs] */
+
+    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+      NULL, NULL, NULL);
+    *pWidth = width;
+    *pHeight = height;
+
+
+    /* OK, that's all we need for now; return happy */
+
+    return 0;
+}
+
+
+
+
+/* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
+ * scales values to 8-bit if necessary */
+
+int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
+{
+    png_color_16p pBackground;
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 2;
+    }
+
+
+    if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
+        return 1;
+
+    /* 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);
+
+
+    /* 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) {
+        *red   = pBackground->red   >> 8;
+        *green = pBackground->green >> 8;
+        *blue  = pBackground->blue  >> 8;
+    } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+        if (bit_depth == 1)
+            *red = *green = *blue = pBackground->gray? 255 : 0;
+        else if (bit_depth == 2)
+            *red = *green = *blue = (255/3) * pBackground->gray;
+        else /* bit_depth == 4 */
+            *red = *green = *blue = (255/15) * pBackground->gray;
+    } else {
+        *red   = (uch)pBackground->red;
+        *green = (uch)pBackground->green;
+        *blue  = (uch)pBackground->blue;
+    }
+
+    return 0;
+}
+
+
+
+
+/* display_exponent == LUT_exponent * CRT_exponent */
+
+uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
+{
+    double  gamma;
+    png_uint_32  i, rowbytes;
+    png_bytepp  row_pointers = NULL;
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * 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;
+    }
+
+
+    /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
+     * transparency chunks to full alpha channel; strip 16-bit-per-sample
+     * images to 8 bits per sample; and convert grayscale to RGB[A] */
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE)
+        png_set_expand(png_ptr);
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+        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);
+
+
+    /* unlike the example in the libpng documentation, we have *no* idea where
+     * this file may have come from--so if it doesn't have a file gamma, don't
+     * do any correction ("do no harm") */
+
+    if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+        png_set_gamma(png_ptr, display_exponent, gamma);
+
+
+    /* all transformations have been registered; now update info_ptr data,
+     * get rowbytes and channels, and allocate image memory */
+
+    png_read_update_info(png_ptr, info_ptr);
+
+    *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+    *pChannels = (int)png_get_channels(png_ptr, info_ptr);
+
+    /* Guard against integer overflow */
+    if (height > ((size_t)(-1))/rowbytes) {
+        fprintf(stderr, "readpng:  image_data buffer would be too large\n",
+        return NULL;
+    }
+
+    if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return NULL;
+    }
+    if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        free(image_data);
+        image_data = NULL;
+        return NULL;
+    }
+
+    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 */
+
+    for (i = 0;  i < height;  ++i)
+        row_pointers[i] = image_data + i*rowbytes;
+
+
+    /* now we can go ahead and just read the whole image */
+
+    png_read_image(png_ptr, row_pointers);
+
+
+    /* and we're done!  (png_read_end() can be omitted if no processing of
+     * post-IDAT text/time/etc. is desired) */
+
+    free(row_pointers);
+    row_pointers = NULL;
+
+    png_read_end(png_ptr, NULL);
+
+    return image_data;
+}
+
+
+void readpng_cleanup(int free_image_data)
+{
+    if (free_image_data && image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+
+    if (png_ptr && info_ptr) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        png_ptr = NULL;
+        info_ptr = NULL;
+    }
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng.h b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng.h
new file mode 100755
index 0000000..fad9fe3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng.h
@@ -0,0 +1,88 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                              readpng.h
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2007 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#ifndef MAX
+#  define MAX(a,b)  ((a) > (b)? (a) : (b))
+#  define MIN(a,b)  ((a) < (b)? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
+#else
+#  define Trace(x)  ;
+#endif
+
+typedef unsigned char   uch;
+typedef unsigned short  ush;
+typedef unsigned long   ulg;
+
+
+/* prototypes for public functions in readpng.c */
+
+void readpng_version_info(void);
+
+int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight);
+
+int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue);
+
+uch *readpng_get_image(double display_exponent, int *pChannels,
+                       ulg *pRowbytes);
+
+void readpng_cleanup(int free_image_data);
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng2.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng2.c
new file mode 100755
index 0000000..610b3cd
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng2.c
@@ -0,0 +1,521 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                 readpng2.c
+
+  ---------------------------------------------------------------------------
+
+      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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+     2015-11-12 - Check return value of png_get_bKGD() (Glenn R-P)
+     2017-04-22 - Guard against integer overflow (Glenn R-P)
+
+  ---------------------------------------------------------------------------*/
+
+
+#include <stdlib.h>     /* for exit() prototype */
+#include <setjmp.h>
+
+#include <zlib.h>
+#include "png.h"        /* libpng header from the local directory */
+#include "readpng2.h"   /* typedefs, common macros, public prototypes */
+
+
+/* local prototypes */
+
+static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr);
+static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
+                                 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)
+{
+    fprintf(stderr, "   Compiled with libpng %s; using libpng %s\n",
+      PNG_LIBPNG_VER_STRING, png_libpng_ver);
+
+    fprintf(stderr, "   and with zlib %s; using zlib %s.\n",
+      ZLIB_VERSION, zlib_version);
+}
+
+
+
+
+int readpng2_check_sig(uch *sig, int num)
+{
+    return !png_sig_cmp(sig, 0, num);
+}
+
+
+
+
+/* returns 0 for success, 2 for libpng problem, 4 for out of memory */
+
+int readpng2_init(mainprog_info *mainprog_ptr)
+{
+    png_structp  png_ptr;       /* note:  temporary variables! */
+    png_infop  info_ptr;
+
+
+    /* could also replace libpng warning-handler (final NULL), but no need: */
+
+    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 */
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return 4;   /* out of memory */
+    }
+
+
+    /* we could create a second info struct here (end_info), but it's only
+     * useful if we want to keep pre- and post-IDAT chunk info separated
+     * (mainly for PNG-aware image editors and converters) */
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * 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 are: */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return 2;
+    }
+
+
+#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 version
+         * of readpng2.c recognizes more chunks, add them to this list.
+         */
+        static const png_byte chunks_to_process[] = {
+            98,  75,  71,  68, '\0',  /* bKGD */
+           103,  65,  77,  65, '\0',  /* gAMA */
+           115,  82,  71,  66, '\0',  /* sRGB */
+           };
+
+       /* 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_HANDLE_AS_UNKNOWN_SUPPORTED */
+
+
+    /* instead of doing png_init_io() here, now we set up our callback
+     * functions for progressive decoding */
+
+    png_set_progressive_read_fn(png_ptr, mainprog_ptr,
+      readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);
+
+
+    /* make sure we save our pointers for use in readpng2_decode_data() */
+
+    mainprog_ptr->png_ptr = png_ptr;
+    mainprog_ptr->info_ptr = info_ptr;
+
+
+    /* and that's all there is to initialization */
+
+    return 0;
+}
+
+
+
+
+/* returns 0 for success, 2 for libpng (longjmp) problem */
+
+int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* setjmp() must be called in every function that calls a PNG-reading
+     * libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* hand off the next chunk of input data to libpng for decoding */
+
+    png_process_data(png_ptr, info_ptr, rawbuf, length);
+
+    return 0;
+}
+
+
+
+
+static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr)
+{
+    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(),
+     * longjmp() ourselves, or return control to libpng, which doesn't want
+     * to see us again.  By not doing anything here, libpng will instead jump
+     * to readpng2_decode_data(), which can return an error value to the main
+     * program. */
+
+
+    /* retrieve the pointer to our special-purpose struct, using the png_ptr
+     * that libpng passed back to us (i.e., not a global this time--there's
+     * no real difference for a single image, but for a multithreaded browser
+     * decoding several PNG images at the same time, one needs to avoid mixing
+     * up different images' structs) */
+
+    mainprog_ptr = png_get_progressive_ptr(png_ptr);
+
+    if (mainprog_ptr == NULL) {         /* we be hosed */
+        fprintf(stderr,
+          "readpng2 error:  main struct not recoverable in info_callback.\n");
+        fflush(stderr);
+        return;
+        /*
+         * Alternatively, we could call our error-handler just like libpng
+         * does, which would effectively terminate the program.  Since this
+         * can only happen if png_ptr gets redirected somewhere odd or the
+         * main PNG struct gets wiped, we're probably toast anyway.  (If
+         * png_ptr itself is NULL, we would not have been called.)
+         */
+    }
+
+
+    /* this is just like in the non-progressive case */
+
+    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+       NULL, NULL, NULL);
+    mainprog_ptr->width = (ulg)width;
+    mainprog_ptr->height = (ulg)height;
+
+
+    /* 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_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: */
+        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;
+           }
+        }
+    }
+
+
+    /* as before, let libpng expand palette images to RGB, low-bit-depth
+     * grayscale images to 8 bits, transparency chunks to full alpha channel;
+     * strip 16-bit-per-sample images to 8 bits per sample; and convert
+     * grayscale to RGB[A] */
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE)
+        png_set_expand(png_ptr);
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+        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);
+
+
+    /* Unlike the basic viewer, which was designed to operate on local files,
+     * this program is intended to simulate a web browser--even though we
+     * actually read from a local file, too.  But because we are pretending
+     * that most of the images originate on the Internet, we follow the recom-
+     * mendation of the sRGB proposal and treat unlabelled images (no gAMA
+     * chunk) as existing in the sRGB color space.  That is, we assume that
+     * such images have a file gamma of 0.45455, which corresponds to a PC-like
+     * display system.  This change in assumptions will have no effect on a
+     * PC-like system, but on a Mac, SGI, NeXT or other system with a non-
+     * identity lookup table, it will darken unlabelled images, which effec-
+     * tively favors images from PC-like systems over those originating on
+     * the local platform.  Note that mainprog_ptr->display_exponent is the
+     * "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 */
+
+    mainprog_ptr->passes = png_set_interlace_handling(png_ptr);
+
+
+    /* all transformations have been registered; now update info_ptr data and
+     * then get rowbytes and channels */
+
+    png_read_update_info(png_ptr, info_ptr);
+
+    mainprog_ptr->rowbytes = (int)png_get_rowbytes(png_ptr, info_ptr);
+    mainprog_ptr->channels = png_get_channels(png_ptr, info_ptr);
+
+
+    /* Call the main program to allocate memory for the image buffer and
+     * initialize windows and whatnot.  (The old-style function-pointer
+     * invocation is used for compatibility with a few supposedly ANSI
+     * compilers that nevertheless barf on "fn_ptr()"-style syntax.) */
+
+    (*mainprog_ptr->mainprog_init)();
+
+
+    /* and that takes care of initialization */
+
+    return;
+}
+
+
+
+
+
+static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
+                                  png_uint_32 row_num, int pass)
+{
+    mainprog_info  *mainprog_ptr;
+
+
+    /* first check whether the row differs from the previous pass; if not,
+     * nothing to combine or display */
+
+    if (!new_row)
+        return;
+
+
+    /* retrieve the pointer to our special-purpose struct so we can access
+     * the old rows and image-display callback function */
+
+    mainprog_ptr = png_get_progressive_ptr(png_ptr);
+
+
+    /* save the pass number for optional use by the front end */
+
+    mainprog_ptr->pass = pass;
+
+
+    /* have libpng either combine the new row data with the existing row data
+     * from previous passes (if interlaced) or else just copy the new row
+     * into the main program's image buffer */
+
+    png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num],
+      new_row);
+
+
+    /* finally, call the display routine in the main program with the number
+     * of the row we just updated */
+
+    (*mainprog_ptr->mainprog_display_row)(row_num);
+
+
+    /* and we're ready for more */
+
+    return;
+}
+
+
+
+
+
+static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr)
+{
+    mainprog_info  *mainprog_ptr;
+
+
+    /* retrieve the pointer to our special-purpose struct */
+
+    mainprog_ptr = png_get_progressive_ptr(png_ptr);
+
+
+    /* let the main program know that it should flush any buffered image
+     * data to the display now and set a "done" flag or whatever, but note
+     * that it SHOULD NOT DESTROY THE PNG STRUCTS YET--in other words, do
+     * NOT call readpng2_cleanup() either here or in the finish_display()
+     * routine; wait until control returns to the main program via
+     * readpng2_decode_data() */
+
+    (*mainprog_ptr->mainprog_finish_display)();
+
+
+    /* all done */
+
+    (void)info_ptr; /* Unused */
+
+    return;
+}
+
+
+
+
+
+void readpng2_cleanup(mainprog_info *mainprog_ptr)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+    if (png_ptr && info_ptr)
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+    mainprog_ptr->png_ptr = NULL;
+    mainprog_ptr->info_ptr = NULL;
+}
+
+
+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)
+{
+    mainprog_info  *mainprog_ptr;
+
+    /* This function, aside from the extra step of retrieving the "error
+     * pointer" (below) and the fact that it exists within the application
+     * rather than within libpng, is essentially identical to libpng's
+     * default error handler.  The second point is critical:  since both
+     * setjmp() and longjmp() are called from the same code, they are
+     * guaranteed to have compatible notions of how big a jmp_buf is,
+     * regardless of whether _BSD_SOURCE or anything else has (or has not)
+     * been defined. */
+
+    fprintf(stderr, "readpng2 libpng error: %s\n", msg);
+    fflush(stderr);
+
+    mainprog_ptr = png_get_error_ptr(png_ptr);
+    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
+        fprintf(stderr,
+          "readpng2 severe error:  jmpbuf not recoverable; terminating.\n");
+        fflush(stderr);
+        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/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng2.h b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng2.h
new file mode 100755
index 0000000..6b3660d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readpng2.h
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                 readpng2.h
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2008 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#ifndef MAX
+#  define MAX(a,b)  ((a) > (b)? (a) : (b))
+#  define MIN(a,b)  ((a) < (b)? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
+#else
+#  define Trace(x)  ;
+#endif
+
+enum rpng2_states {
+    kPreInit = 0,
+    kWindowInit,
+    kDone
+};
+
+typedef unsigned char   uch;
+typedef unsigned short  ush;
+typedef unsigned long   ulg;
+
+typedef struct _mainprog_info {
+    double display_exponent;
+    ulg width;
+    ulg height;
+    void *png_ptr;
+    void *info_ptr;
+    void (*mainprog_init)(void);
+    void (*mainprog_display_row)(ulg row_num);
+    void (*mainprog_finish_display)(void);
+    uch *image_data;
+    uch **row_pointers;
+    jmp_buf jmpbuf;
+    int passes;              /* not used */
+    int pass;
+    int rowbytes;
+    int channels;
+    int need_bgcolor;
+    int state;
+    uch bg_red;
+    uch bg_green;
+    uch bg_blue;
+} mainprog_info;
+
+
+/* prototypes for public functions in readpng2.c */
+
+void readpng2_version_info(void);
+
+int readpng2_check_sig(uch *sig, int num);
+
+int readpng2_init(mainprog_info *mainprog_ptr);
+
+int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length);
+
+void readpng2_cleanup(mainprog_info *mainprog_ptr);
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readppm.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readppm.c
new file mode 100755
index 0000000..52e7027
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/readppm.c
@@ -0,0 +1,188 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                              readppm.c
+
+  ---------------------------------------------------------------------------
+
+   This is a special-purpose replacement for readpng.c that allows binary
+   PPM files to be used in place of PNG images.
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2007,2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "readpng.h"    /* typedefs, common macros, public prototypes */
+
+
+ulg  width, height;
+int  bit_depth, color_type, channels;
+uch  *image_data = NULL;
+FILE *saved_infile;
+
+
+void readpng_version_info()
+{
+    fprintf(stderr, "   Compiled without libpng, zlib or PBMPLUS/NetPBM.\n");
+}
+
+
+/* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */
+
+int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight)
+{
+    static uch ppmline[256];
+    int maxval;
+
+
+    saved_infile = infile;
+
+    fgets(ppmline, 256, infile);
+    if (ppmline[0] != 'P' || ppmline[1] != '6') {
+        fprintf(stderr, "ERROR:  not a PPM file\n");
+        return 1;
+    }
+    /* possible color types:  P5 = grayscale (0), P6 = RGB (2), P8 = RGBA (6) */
+    if (ppmline[1] == '6') {
+        color_type = 2;
+        channels = 3;
+    } else if (ppmline[1] == '8') {
+        color_type = 6;
+        channels = 4;
+    } else /* if (ppmline[1] == '5') */ {
+        color_type = 0;
+        channels = 1;
+    }
+
+    do {
+        fgets(ppmline, 256, infile);
+    } while (ppmline[0] == '#');
+    sscanf(ppmline, "%lu %lu", &width, &height);
+
+    do {
+        fgets(ppmline, 256, infile);
+    } while (ppmline[0] == '#');
+    sscanf(ppmline, "%d", &maxval);
+    if (maxval != 255) {
+        fprintf(stderr, "ERROR:  maxval = %d\n", maxval);
+        return 2;
+    }
+    bit_depth = 8;
+
+    *pWidth = width;
+    *pHeight = height;
+
+    return 0;
+}
+
+
+
+
+/* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
+ * scales values to 8-bit if necessary */
+
+int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
+{
+    return 1;
+}
+
+
+
+
+/* display_exponent == LUT_exponent * CRT_exponent */
+
+uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
+{
+    ulg  rowbytes;
+
+
+    /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
+     * transparency chunks to full alpha channel; strip 16-bit-per-sample
+     * images to 8 bits per sample; and convert grayscale to RGB[A] */
+
+    /* GRR WARNING:  grayscale needs to be expanded and channels reset! */
+
+    *pRowbytes = rowbytes = channels*width;
+    *pChannels = channels;
+
+    Trace((stderr, "readpng_get_image:  rowbytes = %ld, height = %ld\n", rowbytes, height));
+
+    /* Guard against integer overflow */
+    if (height > ((size_t)(-1))/rowbytes) {
+        fprintf(stderr, PROGNAME ":  image_data buffer would be too large\n",
+        return NULL;
+    }
+
+    if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
+        return NULL;
+    }
+
+    /* now we can go ahead and just read the whole image */
+
+    if (fread(image_data, 1L, rowbytes*height, saved_infile) <
+       rowbytes*height) {
+        free (image_data);
+        image_data = NULL;
+        return NULL;
+    }
+
+    return image_data;
+}
+
+
+void readpng_cleanup(int free_image_data)
+{
+    if (free_image_data && image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng-win.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng-win.c
new file mode 100755
index 0000000..1a6f876
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng-win.c
@@ -0,0 +1,735 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                             rpng-win.c
+
+   This program decodes and displays PNG images, with gamma correction and
+   optionally with a user-specified background color (in case the image has
+   transparency).  It is very nearly the most basic PNG viewer possible.
+   This version is for 32-bit Windows; it may compile under 16-bit Windows
+   with a little tweaking (or maybe not).
+
+   to do:
+    - handle quoted command-line args (especially filenames with spaces)
+    - have minimum window width:  oh well
+    - use %.1023s to simplify truncation of title-bar string?
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+    - 1.00:  initial public release
+    - 1.01:  modified to allow abbreviated options; fixed long/ulong mis-
+              match; switched to png_jmpbuf() macro
+    - 1.02:  added extra set of parentheses to png_jmpbuf() macro; fixed
+              command-line parsing bug
+    - 1.10:  enabled "message window"/console (thanks to David Geldreich)
+    - 2.00:  dual-licensed (added GNU GPL)
+    - 2.01:  fixed improper display of usage screen on PNG error(s)
+    - 2.02:  check for integer overflow (Glenn R-P)
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2008, 2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng-win"
+#define LONGNAME  "Simple PNG Viewer for Windows"
+#define VERSION   "2.01 of 16 March 2008"
+
+#include <stdio.h>
+#include <stdlib.h>
+#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 */
+
+#include "readpng.h"    /* typedefs, common macros, readpng prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {               \
+    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
+}
+
+
+/* local prototypes */
+static int        rpng_win_create_window(HINSTANCE hInst, int showmode);
+static int        rpng_win_display_image(void);
+static void       rpng_win_cleanup(void);
+LRESULT CALLBACK  rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);
+
+
+static char titlebar[1024];
+static char *progname = PROGNAME;
+static char *appname = LONGNAME;
+static char *filename;
+static FILE *infile;
+
+static char *bgstr;
+static uch bg_red=0, bg_green=0, bg_blue=0;
+
+static double display_exponent;
+
+static ulg image_width, image_height, image_rowbytes;
+static int image_channels;
+static uch *image_data;
+
+/* Windows-specific variables */
+static ulg wimage_rowbytes;
+static uch *dib;
+static uch *wimage_data;
+static BITMAPINFOHEADER *bmih;
+
+static HWND global_hwnd;
+
+
+
+
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
+{
+    char *args[1024];                 /* arbitrary limit, but should suffice */
+    char *p, *q, **argv = args;
+    int argc = 0;
+    int rc, alen, flen;
+    int error = 0;
+    int have_bg = FALSE;
+    double LUT_exponent;              /* just the lookup table */
+    double CRT_exponent = 2.2;        /* just the monitor */
+    double default_display_exponent;  /* whole display system */
+    MSG msg;
+
+
+    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 at realviz.com for supplying the magical
+     * incantation. */
+
+    AllocConsole();
+    freopen("CONOUT$", "a", stderr);
+    freopen("CONOUT$", "a", stdout);
+#endif
+
+
+    /* Next set the default value for our display-system exponent, i.e.,
+     * the product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities.  And
+     * yes, these ifdefs are completely wasted in a Windows program... */
+
+#if defined(NeXT)
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to get the
+     * "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        display_exponent = atof(p);
+    else
+        display_exponent = default_display_exponent;
+
+
+    /* Windows really hates command lines, so we have to set up our own argv.
+     * Note that we do NOT bother with quoted arguments here, so don't use
+     * filenames with spaces in 'em! */
+
+    argv[argc++] = PROGNAME;
+    p = cmd;
+    for (;;) {
+        if (*p == ' ')
+            while (*++p == ' ')
+                ;
+        /* now p points at the first non-space after some spaces */
+        if (*p == '\0')
+            break;    /* nothing after the spaces:  done */
+        argv[argc++] = q = p;
+        while (*q && *q != ' ')
+            ++q;
+        /* now q points at a space or the end of the string */
+        if (*q == '\0')
+            break;    /* last argv already terminated; quit */
+        *q = '\0';    /* change space to terminator */
+        p = q + 1;
+    }
+    argv[argc] = NULL;   /* terminate the argv array itself */
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strncmp(*argv, "-gamma", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                display_exponent = atof(*argv);
+                if (display_exponent <= 0.0)
+                    ++error;
+            }
+        } else if (!strncmp(*argv, "-bgcolor", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                bgstr = *argv;
+                if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                    ++error;
+                else
+                    have_bg = TRUE;
+            }
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename)
+        ++error;
+
+
+    /* 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();
+        fprintf(stderr, "\n"
+          "Usage:  %s [-gamma exp] [-bgcolor bg] file.png\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  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 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);
+    }
+
+
+    if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
+            switch (rc) {
+                case 1:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] is not a PNG file: incorrect signature\n",
+                      filename);
+                    break;
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng_init() error\n");
+                    break;
+            }
+            ++error;
+        }
+        if (error)
+            fclose(infile);
+    }
+
+
+    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
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* if the user didn't specify a background color on the command line,
+     * check for one in the PNG file--if not, the initialized values of 0
+     * (black) will be used */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        bg_red   = (uch)r;
+        bg_green = (uch)g;
+        bg_blue  = (uch)b;
+    } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
+        readpng_cleanup(TRUE);
+        fprintf(stderr, PROGNAME
+          ":  libpng error while checking for background color\n");
+        exit(2);
+    }
+
+
+    /* do the basic Windows initialization stuff, make the window and fill it
+     * with the background color */
+
+    if (rpng_win_create_window(hInst, showmode))
+        exit(2);
+
+
+    /* decode the image, all at once */
+
+    Trace((stderr, "calling readpng_get_image()\n"))
+    image_data = readpng_get_image(display_exponent, &image_channels,
+      &image_rowbytes);
+    Trace((stderr, "done with readpng_get_image()\n"))
+
+
+    /* done with PNG file, so clean up to minimize memory usage (but do NOT
+     * nuke image_data!) */
+
+    readpng_cleanup(FALSE);
+    fclose(infile);
+
+    if (!image_data) {
+        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
+        exit(3);
+    }
+
+
+    /* display image (composite with background if requested) */
+
+    Trace((stderr, "calling rpng_win_display_image()\n"))
+    if (rpng_win_display_image()) {
+        free(image_data);
+        exit(4);
+    }
+    Trace((stderr, "done with rpng_win_display_image()\n"))
+
+
+    /* wait for the user to tell us when to quit */
+
+    printf(
+#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)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+
+    /* OK, we're done:  clean up all image and Windows resources and go away */
+
+    rpng_win_cleanup();
+
+    return msg.wParam;
+}
+
+
+
+
+
+static int rpng_win_create_window(HINSTANCE hInst, int showmode)
+{
+    uch *dest;
+    int extra_width, extra_height;
+    ulg i, j;
+    WNDCLASSEX wndclass;
+
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the display-specific version of the image (round up
+    to multiple of 4 for Windows DIB).
+  ---------------------------------------------------------------------------*/
+
+    wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;
+
+    /* Guard against integer overflow */
+    if (image_height > ((size_t)(-1))/wimage_rowbytes) {
+        fprintf(stderr, PROGNAME ":  image_data buffer would be too large\n");
+        return 4;   /* fail */
+    }
+
+    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
+                              wimage_rowbytes*image_height)))
+    {
+        return 4;   /* fail */
+    }
+
+/*---------------------------------------------------------------------------
+    Initialize the DIB.  Negative height means to use top-down BMP ordering
+    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
+    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
+    directly => wimage_data begins immediately after BMP header.
+  ---------------------------------------------------------------------------*/
+
+    memset(dib, 0, sizeof(BITMAPINFOHEADER));
+    bmih = (BITMAPINFOHEADER *)dib;
+    bmih->biSize = sizeof(BITMAPINFOHEADER);
+    bmih->biWidth = image_width;
+    bmih->biHeight = -((long)image_height);
+    bmih->biPlanes = 1;
+    bmih->biBitCount = 24;
+    bmih->biCompression = 0;
+    wimage_data = dib + sizeof(BITMAPINFOHEADER);
+
+/*---------------------------------------------------------------------------
+    Fill in background color (black by default); data are in BGR order.
+  ---------------------------------------------------------------------------*/
+
+    for (j = 0;  j < image_height;  ++j) {
+        dest = wimage_data + j*wimage_rowbytes;
+        for (i = image_width;  i > 0;  --i) {
+            *dest++ = bg_blue;
+            *dest++ = bg_green;
+            *dest++ = bg_red;
+        }
+    }
+
+/*---------------------------------------------------------------------------
+    Set the window parameters.
+  ---------------------------------------------------------------------------*/
+
+    memset(&wndclass, 0, sizeof(wndclass));
+
+    wndclass.cbSize = sizeof(wndclass);
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = rpng_win_wndproc;
+    wndclass.hInstance = hInst;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
+    wndclass.lpszMenuName = NULL;
+    wndclass.lpszClassName = progname;
+    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+
+    RegisterClassEx(&wndclass);
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
+                      GetSystemMetrics(SM_CXDLGFRAME));
+    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
+                      GetSystemMetrics(SM_CYDLGFRAME)) +
+                      GetSystemMetrics(SM_CYCAPTION);
+
+    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width,
+      image_height+extra_height, NULL, NULL, hInst, NULL);
+
+    ShowWindow(global_hwnd, showmode);
+    UpdateWindow(global_hwnd);
+
+    return 0;
+
+} /* end function rpng_win_create_window() */
+
+
+
+
+
+static int rpng_win_display_image()
+{
+    uch *src, *dest;
+    uch r, g, b, a;
+    ulg i, row, lastrow;
+    RECT rect;
+
+
+    Trace((stderr, "beginning display loop (image_channels == %d)\n",
+      image_channels))
+    Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n",
+      image_width, image_rowbytes, wimage_rowbytes))
+
+
+/*---------------------------------------------------------------------------
+    Blast image data to buffer.  This whole routine takes place before the
+    message loop begins, so there's no real point in any pseudo-progressive
+    display...
+  ---------------------------------------------------------------------------*/
+
+    for (lastrow = row = 0;  row < image_height;  ++row) {
+        src = image_data + row*image_rowbytes;
+        dest = wimage_data + row*wimage_rowbytes;
+        if (image_channels == 3) {
+            for (i = image_width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                *dest++ = b;
+                *dest++ = g;   /* note reverse order */
+                *dest++ = r;
+            }
+        } else /* if (image_channels == 4) */ {
+            for (i = image_width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                a = *src++;
+                if (a == 255) {
+                    *dest++ = b;
+                    *dest++ = g;
+                    *dest++ = r;
+                } else if (a == 0) {
+                    *dest++ = bg_blue;
+                    *dest++ = bg_green;
+                    *dest++ = bg_red;
+                } else {
+                    /* this macro (copied from png.h) composites the
+                     * foreground and background values and puts the
+                     * result into the first argument; there are no
+                     * side effects with the first argument */
+                    alpha_composite(*dest++, b, a, bg_blue);
+                    alpha_composite(*dest++, g, a, bg_green);
+                    alpha_composite(*dest++, r, a, bg_red);
+                }
+            }
+        }
+        /* display after every 16 lines */
+        if (((row+1) & 0xf) == 0) {
+            rect.left = 0L;
+            rect.top = (LONG)lastrow;
+            rect.right = (LONG)image_width;      /* possibly off by one? */
+            rect.bottom = (LONG)lastrow + 16L;   /* possibly off by one? */
+            InvalidateRect(global_hwnd, &rect, FALSE);
+            UpdateWindow(global_hwnd);     /* similar to XFlush() */
+            lastrow = row + 1;
+        }
+    }
+
+    Trace((stderr, "calling final image-flush routine\n"))
+    if (lastrow < image_height) {
+        rect.left = 0L;
+        rect.top = (LONG)lastrow;
+        rect.right = (LONG)image_width;      /* possibly off by one? */
+        rect.bottom = (LONG)image_height;    /* possibly off by one? */
+        InvalidateRect(global_hwnd, &rect, FALSE);
+        UpdateWindow(global_hwnd);     /* similar to XFlush() */
+    }
+
+/*
+    last param determines whether or not background is wiped before paint
+    InvalidateRect(global_hwnd, NULL, TRUE);
+    UpdateWindow(global_hwnd);
+ */
+
+    return 0;
+}
+
+
+
+
+
+static void rpng_win_cleanup()
+{
+    if (image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+
+    if (dib) {
+        free(dib);
+        dib = NULL;
+    }
+}
+
+
+
+
+
+LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
+{
+    HDC         hdc;
+    PAINTSTRUCT ps;
+    int rc;
+
+    switch (iMsg) {
+        case WM_CREATE:
+            /* one-time processing here, if any */
+            return 0;
+
+        case WM_PAINT:
+            hdc = BeginPaint(hwnd, &ps);
+                    /*                    dest                          */
+            rc = StretchDIBits(hdc, 0, 0, image_width, image_height,
+                    /*                    source                        */
+                                    0, 0, image_width, image_height,
+                                    wimage_data, (BITMAPINFO *)bmih,
+                    /*              iUsage: no clue                     */
+                                    0, SRCCOPY);
+            EndPaint(hwnd, &ps);
+            return 0;
+
+        /* wait for the user to tell us when to quit */
+        case WM_CHAR:
+            switch (wP) {      /* only need one, so ignore repeat count */
+                case 'q':
+                case 'Q':
+                case 0x1B:     /* Esc key */
+                    PostQuitMessage(0);
+            }
+            return 0;
+
+        case WM_LBUTTONDOWN:   /* another way of quitting */
+        case WM_DESTROY:
+            PostQuitMessage(0);
+            return 0;
+    }
+
+    return DefWindowProc(hwnd, iMsg, wP, lP);
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng-x.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng-x.c
new file mode 100755
index 0000000..92effaa
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng-x.c
@@ -0,0 +1,911 @@
+/*---------------------------------------------------------------------------
+
+   rpng - simple PNG display program                               rpng-x.c
+
+   This program decodes and displays PNG images, with gamma correction and
+   optionally with a user-specified background color (in case the image has
+   transparency).  It is very nearly the most basic PNG viewer possible.
+   This version is for the X Window System (tested by author under Unix and
+   by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking).
+
+   to do:
+    - 8-bit (colormapped) X support
+    - use %.1023s to simplify truncation of title-bar string?
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+    - 1.01:  initial public release
+    - 1.02:  modified to allow abbreviated options; fixed long/ulong mis-
+              match; switched to png_jmpbuf() macro
+    - 1.10:  added support for non-default visuals; fixed X pixel-conversion
+    - 1.11:  added extra set of parentheses to png_jmpbuf() macro; fixed
+              command-line parsing bug
+    - 1.12:  fixed some small X memory leaks (thanks to François Petitjean)
+    - 1.13:  fixed XFreeGC() crash bug (thanks to Patrick Welche)
+    - 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)
+    - 2.03:  check for integer overflow (Glenn R-P)
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2008, 2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng-x"
+#define LONGNAME  "Simple PNG Viewer for X"
+#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>
+#include <string.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/keysym.h>
+
+/* #define DEBUG  :  this enables the Trace() macros */
+
+#include "readpng.h"   /* typedefs, common macros, readpng prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {               \
+    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
+}
+
+
+/* local prototypes */
+static int  rpng_x_create_window(void);
+static int  rpng_x_display_image(void);
+static void rpng_x_cleanup(void);
+static int  rpng_x_msb(ulg u32val);
+
+
+static char titlebar[1024], *window_name = titlebar;
+static char *appname = LONGNAME;
+static char *icon_name = PROGNAME;
+static char *res_name = RESNAME;
+static char *res_class = RESCLASS;
+static char *filename;
+static FILE *infile;
+
+static char *bgstr;
+static uch bg_red=0, bg_green=0, bg_blue=0;
+
+static double display_exponent;
+
+static ulg image_width, image_height, image_rowbytes;
+static int image_channels;
+static uch *image_data;
+
+/* X-specific variables */
+static char *displayname;
+static XImage *ximage;
+static Display *display;
+static int depth;
+static Visual *visual;
+static XVisualInfo *visual_list;
+static int RShift, GShift, BShift;
+static ulg RMask, GMask, BMask;
+static Window window;
+static GC gc;
+static Colormap colormap;
+
+static int have_nondefault_visual = FALSE;
+static int have_colormap = FALSE;
+static int have_window = FALSE;
+static int have_gc = FALSE;
+/*
+ulg numcolors=0, pixels[256];
+ush reds[256], greens[256], blues[256];
+ */
+
+
+
+
+int main(int argc, char **argv)
+{
+#ifdef sgi
+    char tmpline[80];
+#endif
+    char *p;
+    int rc, alen, flen;
+    int error = 0;
+    int have_bg = FALSE;
+    double LUT_exponent;               /* just the lookup table */
+    double CRT_exponent = 2.2;         /* just the monitor */
+    double default_display_exponent;   /* whole display system */
+    XEvent e;
+    KeySym k;
+
+
+    displayname = (char *)NULL;
+    filename = (char *)NULL;
+
+
+    /* First set the default value for our display-system exponent, i.e.,
+     * the product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities. */
+
+#if defined(NeXT)
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to get the
+     * "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        display_exponent = atof(p);
+    else
+        display_exponent = default_display_exponent;
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strncmp(*argv, "-display", 2)) {
+            if (!*++argv)
+                ++error;
+            else
+                displayname = *argv;
+        } else if (!strncmp(*argv, "-gamma", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                display_exponent = atof(*argv);
+                if (display_exponent <= 0.0)
+                    ++error;
+            }
+        } else if (!strncmp(*argv, "-bgcolor", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                bgstr = *argv;
+                if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                    ++error;
+                else
+                    have_bg = TRUE;
+            }
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename)
+        ++error;
+
+
+    /* print usage screen if any errors up to this point */
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
+        readpng_version_info();
+        fprintf(stderr, "\n"
+          "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",
+          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");
+        exit(1);
+    }
+
+
+    if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
+            switch (rc) {
+                case 1:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] is not a PNG file: incorrect signature\n",
+                      filename);
+                    break;
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng_init() error\n");
+                    break;
+            }
+            ++error;
+        } else {
+            display = XOpenDisplay(displayname);
+            if (!display) {
+                readpng_cleanup(TRUE);
+                fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
+                  displayname? displayname : "default");
+                ++error;
+            }
+        }
+        if (error)
+            fclose(infile);
+    }
+
+
+    if (error) {
+        fprintf(stderr, PROGNAME ":  aborting.\n");
+        exit(2);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* if the user didn't specify a background color on the command line,
+     * check for one in the PNG file--if not, the initialized values of 0
+     * (black) will be used */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        bg_red   = (uch)r;
+        bg_green = (uch)g;
+        bg_blue  = (uch)b;
+    } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
+        readpng_cleanup(TRUE);
+        fprintf(stderr, PROGNAME
+          ":  libpng error while checking for background color\n");
+        exit(2);
+    }
+
+
+    /* do the basic X initialization stuff, make the window and fill it
+     * with the background color */
+
+    if (rpng_x_create_window())
+        exit(2);
+
+
+    /* decode the image, all at once */
+
+    Trace((stderr, "calling readpng_get_image()\n"))
+    image_data = readpng_get_image(display_exponent, &image_channels,
+      &image_rowbytes);
+    Trace((stderr, "done with readpng_get_image()\n"))
+
+
+    /* done with PNG file, so clean up to minimize memory usage (but do NOT
+     * nuke image_data!) */
+
+    readpng_cleanup(FALSE);
+    fclose(infile);
+
+    if (!image_data) {
+        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
+        exit(3);
+    }
+
+
+    /* display image (composite with background if requested) */
+
+    Trace((stderr, "calling rpng_x_display_image()\n"))
+    if (rpng_x_display_image()) {
+        free(image_data);
+        exit(4);
+    }
+    Trace((stderr, "done with rpng_x_display_image()\n"))
+
+
+    /* 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");
+    fflush(stdout);
+
+    do
+        XNextEvent(display, &e);
+    while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
+           !(e.type == KeyPress &&    /*  v--- or 1 for shifted keys */
+             ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
+
+
+    /* OK, we're done:  clean up all image and X resources and go away */
+
+    rpng_x_cleanup();
+
+    (void)argc; /* Unused */
+
+    return 0;
+}
+
+
+
+
+
+static int rpng_x_create_window(void)
+{
+    uch *xdata;
+    int need_colormap = FALSE;
+    int screen, pad;
+    ulg bg_pixel = 0L;
+    ulg attrmask;
+    Window root;
+    XEvent e;
+    XGCValues gcvalues;
+    XSetWindowAttributes attr;
+    XTextProperty windowName, *pWindowName = &windowName;
+    XTextProperty iconName, *pIconName = &iconName;
+    XVisualInfo visual_info;
+    XSizeHints *size_hints;
+    XWMHints *wm_hints;
+    XClassHint *class_hints;
+
+
+    screen = DefaultScreen(display);
+    depth = DisplayPlanes(display, screen);
+    root = RootWindow(display, screen);
+
+#ifdef DEBUG
+    XSynchronize(display, True);
+#endif
+
+#if 0
+/* GRR:  add 8-bit support */
+    if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) {
+        fprintf(stderr,
+          "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
+          depth);
+        return 2;
+    }
+
+    XMatchVisualInfo(display, screen, depth,
+      (depth == 8)? PseudoColor : TrueColor, &visual_info);
+    visual = visual_info.visual;
+#else
+    if (depth != 16 && depth != 24 && depth != 32) {
+        int visuals_matched = 0;
+
+        Trace((stderr, "default depth is %d:  checking other visuals\n",
+          depth))
+
+        /* 24-bit first */
+        visual_info.screen = screen;
+        visual_info.depth = 24;
+        visual_list = XGetVisualInfo(display,
+          VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
+        if (visuals_matched == 0) {
+/* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
+            fprintf(stderr, "default screen depth %d not supported, and no"
+              " 24-bit visuals found\n", depth);
+            return 2;
+        }
+        Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
+          visuals_matched))
+        visual = visual_list[0].visual;
+        depth = visual_list[0].depth;
+/*
+        colormap_size = visual_list[0].colormap_size;
+        visual_class = visual->class;
+        visualID = XVisualIDFromVisual(visual);
+ */
+        have_nondefault_visual = TRUE;
+        need_colormap = TRUE;
+    } else {
+        XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
+        visual = visual_info.visual;
+    }
+#endif
+
+    RMask = visual->red_mask;
+    GMask = visual->green_mask;
+    BMask = visual->blue_mask;
+
+/* GRR:  add/check 8-bit support */
+    if (depth == 8 || need_colormap) {
+        colormap = XCreateColormap(display, root, visual, AllocNone);
+        if (!colormap) {
+            fprintf(stderr, "XCreateColormap() failed\n");
+            return 2;
+        }
+        have_colormap = TRUE;
+    }
+    if (depth == 15 || depth == 16) {
+        RShift = 15 - rpng_x_msb(RMask);    /* these are right-shifts */
+        GShift = 15 - rpng_x_msb(GMask);
+        BShift = 15 - rpng_x_msb(BMask);
+    } else if (depth > 16) {
+#define NO_24BIT_MASKS
+#ifdef NO_24BIT_MASKS
+        RShift = rpng_x_msb(RMask) - 7;     /* these are left-shifts */
+        GShift = rpng_x_msb(GMask) - 7;
+        BShift = rpng_x_msb(BMask) - 7;
+#else
+        RShift = 7 - rpng_x_msb(RMask);     /* these are right-shifts, too */
+        GShift = 7 - rpng_x_msb(GMask);
+        BShift = 7 - rpng_x_msb(BMask);
+#endif
+    }
+    if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
+        fprintf(stderr, "rpng internal logic error:  negative X shift(s)!\n");
+        return 2;
+    }
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    attr.backing_store = Always;
+    attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
+    attrmask = CWBackingStore | CWEventMask;
+    if (have_nondefault_visual) {
+        attr.colormap = colormap;
+        attr.background_pixel = 0;
+        attr.border_pixel = 1;
+        attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
+    }
+
+    window = XCreateWindow(display, root, 0, 0, image_width, image_height, 0,
+      depth, InputOutput, visual, attrmask, &attr);
+
+    if (window == None) {
+        fprintf(stderr, "XCreateWindow() failed\n");
+        return 2;
+    } else
+        have_window = TRUE;
+
+    if (depth == 8)
+        XSetWindowColormap(display, window, colormap);
+
+    if (!XStringListToTextProperty(&window_name, 1, pWindowName))
+        pWindowName = NULL;
+    if (!XStringListToTextProperty(&icon_name, 1, pIconName))
+        pIconName = NULL;
+
+    /* OK if any hints allocation fails; XSetWMProperties() allows NULLs */
+
+    if ((size_hints = XAllocSizeHints()) != NULL) {
+        /* window will not be resizable */
+        size_hints->flags = PMinSize | PMaxSize;
+        size_hints->min_width = size_hints->max_width = (int)image_width;
+        size_hints->min_height = size_hints->max_height = (int)image_height;
+    }
+
+    if ((wm_hints = XAllocWMHints()) != NULL) {
+        wm_hints->initial_state = NormalState;
+        wm_hints->input = True;
+     /* wm_hints->icon_pixmap = icon_pixmap; */
+        wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
+    }
+
+    if ((class_hints = XAllocClassHint()) != NULL) {
+        class_hints->res_name = res_name;
+        class_hints->res_class = res_class;
+    }
+
+    XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
+      size_hints, wm_hints, class_hints);
+
+    /* various properties and hints no longer needed; free memory */
+    if (pWindowName)
+       XFree(pWindowName->value);
+    if (pIconName)
+       XFree(pIconName->value);
+    if (size_hints)
+        XFree(size_hints);
+    if (wm_hints)
+       XFree(wm_hints);
+    if (class_hints)
+       XFree(class_hints);
+
+    XMapWindow(display, window);
+
+    gc = XCreateGC(display, window, 0, &gcvalues);
+    have_gc = TRUE;
+
+/*---------------------------------------------------------------------------
+    Fill window with the specified background color.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        bg_pixel = ((ulg)bg_red   << RShift) |
+                   ((ulg)bg_green << GShift) |
+                   ((ulg)bg_blue  << BShift);
+    } else if (depth == 16) {
+        bg_pixel = ((((ulg)bg_red   << 8) >> RShift) & RMask) |
+                   ((((ulg)bg_green << 8) >> GShift) & GMask) |
+                   ((((ulg)bg_blue  << 8) >> BShift) & BMask);
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    XSetForeground(display, gc, bg_pixel);
+    XFillRectangle(display, window, gc, 0, 0, image_width, image_height);
+
+/*---------------------------------------------------------------------------
+    Wait for first Expose event to do any drawing, then flush.
+  ---------------------------------------------------------------------------*/
+
+    do
+        XNextEvent(display, &e);
+    while (e.type != Expose || e.xexpose.count);
+
+    XFlush(display);
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the X- and display-specific version of the image.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        xdata = (uch *)malloc(4*image_width*image_height);
+        pad = 32;
+    } else if (depth == 16) {
+        xdata = (uch *)malloc(2*image_width*image_height);
+        pad = 16;
+    } else /* depth == 8 */ {
+        xdata = (uch *)malloc(image_width*image_height);
+        pad = 8;
+    }
+
+    if (!xdata) {
+        fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
+        return 4;
+    }
+
+    ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
+      (char *)xdata, image_width, image_height, pad, 0);
+
+    if (!ximage) {
+        fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
+        free(xdata);
+        return 3;
+    }
+
+    /* to avoid testing the byte order every pixel (or doubling the size of
+     * the drawing routine with a giant if-test), we arbitrarily set the byte
+     * order to MSBFirst and let Xlib worry about inverting things on little-
+     * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most
+     * efficient approach (the giant if-test would be better), but in the
+     * interest of clarity, we take the easy way out... */
+
+    ximage->byte_order = MSBFirst;
+
+    return 0;
+
+} /* end function rpng_x_create_window() */
+
+
+
+
+
+static int rpng_x_display_image(void)
+{
+    uch *src;
+    char *dest;
+    uch r, g, b, a;
+    ulg i, row, lastrow = 0;
+    ulg pixel;
+    int ximage_rowbytes = ximage->bytes_per_line;
+/*  int bpp = ximage->bits_per_pixel;  */
+
+
+    Trace((stderr, "beginning display loop (image_channels == %d)\n",
+      image_channels))
+    Trace((stderr, "   (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n",
+      image_width, image_rowbytes, ximage_rowbytes))
+    Trace((stderr, "   (bpp = %d)\n", ximage->bits_per_pixel))
+    Trace((stderr, "   (byte_order = %s)\n", ximage->byte_order == MSBFirst?
+      "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+
+        for (lastrow = row = 0;  row < image_height;  ++row) {
+            src = image_data + row*image_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            if (image_channels == 3) {
+                for (i = image_width;  i > 0;  --i) {
+                    red   = *src++;
+                    green = *src++;
+                    blue  = *src++;
+#ifdef NO_24BIT_MASKS
+                    pixel = (red   << RShift) |
+                            (green << GShift) |
+                            (blue  << BShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    /* GRR BUG:  this assumes bpp == 32, but may be 24: */
+                    *dest++ = (char)((pixel >> 24) & 0xff);
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+#else
+                    red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
+                    green = (GShift < 0)? green << (-GShift) : green >> GShift;
+                    blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
+                    pixel = (red & RMask) | (green & GMask) | (blue & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >> 24) & 0xff);
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+#endif
+                }
+            } else /* if (image_channels == 4) */ {
+                for (i = image_width;  i > 0;  --i) {
+                    r = *src++;
+                    g = *src++;
+                    b = *src++;
+                    a = *src++;
+                    if (a == 255) {
+                        red   = r;
+                        green = g;
+                        blue  = b;
+                    } else if (a == 0) {
+                        red   = bg_red;
+                        green = bg_green;
+                        blue  = bg_blue;
+                    } else {
+                        /* this macro (from png.h) composites the foreground
+                         * and background values and puts the result into the
+                         * first argument */
+                        alpha_composite(red,   r, a, bg_red);
+                        alpha_composite(green, g, a, bg_green);
+                        alpha_composite(blue,  b, a, bg_blue);
+                    }
+                    pixel = (red   << RShift) |
+                            (green << GShift) |
+                            (blue  << BShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >> 24) & 0xff);
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            }
+            /* display after every 16 lines */
+            if (((row+1) & 0xf) == 0) {
+                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
+                  (int)lastrow, image_width, 16);
+                XFlush(display);
+                lastrow = row + 1;
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        for (lastrow = row = 0;  row < image_height;  ++row) {
+            src = image_data + row*image_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            if (image_channels == 3) {
+                for (i = image_width;  i > 0;  --i) {
+                    red   = ((ush)(*src) << 8);
+                    ++src;
+                    green = ((ush)(*src) << 8);
+                    ++src;
+                    blue  = ((ush)(*src) << 8);
+                    ++src;
+                    pixel = ((red   >> RShift) & RMask) |
+                            ((green >> GShift) & GMask) |
+                            ((blue  >> BShift) & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            } else /* if (image_channels == 4) */ {
+                for (i = image_width;  i > 0;  --i) {
+                    r = *src++;
+                    g = *src++;
+                    b = *src++;
+                    a = *src++;
+                    if (a == 255) {
+                        red   = ((ush)r << 8);
+                        green = ((ush)g << 8);
+                        blue  = ((ush)b << 8);
+                    } else if (a == 0) {
+                        red   = ((ush)bg_red   << 8);
+                        green = ((ush)bg_green << 8);
+                        blue  = ((ush)bg_blue  << 8);
+                    } else {
+                        /* this macro (from png.h) composites the foreground
+                         * and background values and puts the result back into
+                         * the first argument (== fg byte here:  safe) */
+                        alpha_composite(r, r, a, bg_red);
+                        alpha_composite(g, g, a, bg_green);
+                        alpha_composite(b, b, a, bg_blue);
+                        red   = ((ush)r << 8);
+                        green = ((ush)g << 8);
+                        blue  = ((ush)b << 8);
+                    }
+                    pixel = ((red   >> RShift) & RMask) |
+                            ((green >> GShift) & GMask) |
+                            ((blue  >> BShift) & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            }
+            /* display after every 16 lines */
+            if (((row+1) & 0xf) == 0) {
+                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
+                  (int)lastrow, image_width, 16);
+                XFlush(display);
+                lastrow = row + 1;
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    Trace((stderr, "calling final XPutImage()\n"))
+    if (lastrow < image_height) {
+        XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
+          (int)lastrow, image_width, image_height-lastrow);
+        XFlush(display);
+    }
+
+    return 0;
+}
+
+
+
+
+static void rpng_x_cleanup(void)
+{
+    if (image_data) {
+        free(image_data);
+        image_data = NULL;
+    }
+
+    if (ximage) {
+        if (ximage->data) {
+            free(ximage->data);           /* we allocated it, so we free it */
+            ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
+        }
+        XDestroyImage(ximage);
+        ximage = NULL;
+    }
+
+    if (have_gc)
+        XFreeGC(display, gc);
+
+    if (have_window)
+        XDestroyWindow(display, window);
+
+    if (have_colormap)
+        XFreeColormap(display, colormap);
+
+    if (have_nondefault_visual)
+        XFree(visual_list);
+}
+
+
+
+
+
+static int rpng_x_msb(ulg u32val)
+{
+    int i;
+
+    for (i = 31;  i >= 0;  --i) {
+        if (u32val & 0x80000000L)
+            break;
+        u32val <<= 1;
+    }
+    return i;
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng2-win.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng2-win.c
new file mode 100755
index 0000000..ed6b526
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng2-win.c
@@ -0,0 +1,1261 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                rpng2-win.c
+
+   This program decodes and displays PNG files progressively, as if it were
+   a web browser (though the front end is only set up to read from files).
+   It supports gamma correction, user-specified background colors, and user-
+   specified background patterns (for transparent images).  This version is
+   for 32-bit Windows; it may compile under 16-bit Windows with a little
+   tweaking (or maybe not).  Thanks to Adam Costello and Pieter S. van der
+   Meulen for the "diamond" and "radial waves" patterns, respectively.
+
+   to do (someday, maybe):
+    - handle quoted command-line args (especially filenames with spaces)
+    - finish resizable checkerboard-gradient (sizes 4-128?)
+    - use %.1023s to simplify truncation of title-bar string?
+    - have minimum window width:  oh well
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+    - 1.01:  initial public release
+    - 1.02:  fixed cut-and-paste error in usage screen (oops...)
+    - 1.03:  modified to allow abbreviated options
+    - 1.04:  removed bogus extra argument from usage fprintf() [Glenn R-P?];
+              fixed command-line parsing bug
+    - 1.10:  enabled "message window"/console (thanks to David Geldreich)
+    - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
+    - 1.21:  made minor tweak to usage screen to fit within 25-line console
+    - 1.22:  added AMD64/EM64T support (__x86_64__)
+    - 2.00:  dual-licensed (added GNU GPL)
+    - 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:  check for integer overflow (Glenn R-P)
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2008, 2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng2-win"
+#define LONGNAME  "Progressive PNG Viewer for Windows"
+#define VERSION   "2.02 of 16 March 2008"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>    /* for jmpbuf declaration in readpng2.h */
+#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
+#  define PI             3.141592653589793238
+#endif
+#define PI_2             (PI*0.5)
+#define INV_PI_360       (360.0 / PI)
+#define MAX(a,b)         (a>b?a:b)
+#define MIN(a,b)         (a<b?a:b)
+#define CLIP(a,min,max)  MAX(min,MIN((a),max))
+#define ABS(a)           ((a)<0?-(a):(a))
+#define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
+#define ROUNDF(f)        ((int)(f + 0.5))
+
+#define rgb1_max   bg_freq
+#define rgb1_min   bg_gray
+#define rgb2_max   bg_bsat
+#define rgb2_min   bg_brot
+
+/* #define DEBUG */     /* this enables the Trace() macros */
+
+#include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {               \
+    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
+}
+
+
+#define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
+                          *  block size corresponds roughly to a download
+                          *  speed 10% faster than theoretical 33.6K maximum
+                          *  (assuming 8 data bits, 1 stop bit and no other
+                          *  overhead) */
+
+/* local prototypes */
+static void       rpng2_win_init(void);
+static int        rpng2_win_create_window(void);
+static int        rpng2_win_load_bg_image(void);
+static void       rpng2_win_display_row(ulg row);
+static void       rpng2_win_finish_display(void);
+static void       rpng2_win_cleanup(void);
+LRESULT CALLBACK  rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM);
+
+
+static char titlebar[1024];
+static char *progname = PROGNAME;
+static char *appname = LONGNAME;
+static char *filename;
+static FILE *infile;
+
+static mainprog_info rpng2_info;
+
+static uch inbuf[INBUFSIZE];
+static int incount;
+
+static int pat = 6;         /* must be less than num_bgpat */
+static int bg_image = 0;
+static int bgscale = 16;
+static ulg bg_rowbytes;
+static uch *bg_data;
+
+static struct rgb_color {
+    uch r, g, b;
+} rgb[] = {
+    {  0,   0,   0},    /*  0:  black */
+    {255, 255, 255},    /*  1:  white */
+    {173, 132,  57},    /*  2:  tan */
+    { 64, 132,   0},    /*  3:  medium green */
+    {189, 117,   1},    /*  4:  gold */
+    {253, 249,   1},    /*  5:  yellow */
+    {  0,   0, 255},    /*  6:  blue */
+    {  0,   0, 120},    /*  7:  medium blue */
+    {255,   0, 255},    /*  8:  magenta */
+    { 64,   0,  64},    /*  9:  dark magenta */
+    {255,   0,   0},    /* 10:  red */
+    { 64,   0,   0},    /* 11:  dark red */
+    {255, 127,   0},    /* 12:  orange */
+    {192,  96,   0},    /* 13:  darker orange */
+    { 24,  60,   0},    /* 14:  dark green-yellow */
+    { 85, 125, 200}     /* 15:  ice blue */
+};
+/* not used for now, but should be for error-checking:
+static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
+ */
+
+/*
+    This whole struct is a fairly cheesy way to keep the number of
+    command-line options to a minimum.  The radial-waves background
+    type is a particularly poor fit to the integer elements of the
+    struct...but a few macros and a little fixed-point math will do
+    wonders for ya.
+
+    type bits:
+       F E D C B A 9 8 7 6 5 4 3 2 1 0
+                             | | | | |
+                             | | +-+-+-- 0 = sharp-edged checkerboard
+                             | |         1 = soft diamonds
+                             | |         2 = radial waves
+                             | |       3-7 = undefined
+                             | +-- gradient #2 inverted?
+                             +-- alternating columns inverted?
+ */
+static struct background_pattern {
+    ush type;
+    int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
+    int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
+} bg[] = {
+    {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
+    {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
+    {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
+    {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
+    {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
+    {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
+    {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
+    {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
+    {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
+    {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
+    {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
+    {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
+    {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
+    {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
+    {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
+    {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
+};
+static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
+
+
+/* Windows-specific global variables (could go in struct, but messy...) */
+static ulg wimage_rowbytes;
+static uch *dib;
+static uch *wimage_data;
+static BITMAPINFOHEADER *bmih;
+
+static HWND global_hwnd;
+static HINSTANCE global_hInst;
+static int global_showmode;
+
+
+
+
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
+{
+    char *args[1024];                 /* arbitrary limit, but should suffice */
+    char **argv = args;
+    char *p, *q, *bgstr = NULL;
+    int argc = 0;
+    int rc, alen, flen;
+    int error = 0;
+    int timing = FALSE;
+    int have_bg = FALSE;
+    double LUT_exponent;              /* just the lookup table */
+    double CRT_exponent = 2.2;        /* just the monitor */
+    double default_display_exponent;  /* whole display system */
+    MSG msg;
+
+
+    /* First initialize a few things, just to be sure--memset takes care of
+     * default background color (black), booleans (FALSE), pointers (NULL),
+     * etc. */
+
+    global_hInst = hInst;
+    global_showmode = showmode;
+    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 at realviz.com for supplying the magical
+     * incantation. */
+
+    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
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities.  And
+     * yes, these ifdefs are completely wasted in a Windows program... */
+
+#if defined(NeXT)
+    /* third-party utilities can modify the default LUT exponent */
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to
+     * get the "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        rpng2_info.display_exponent = atof(p);
+    else
+        rpng2_info.display_exponent = default_display_exponent;
+
+
+    /* Windows really hates command lines, so we have to set up our own argv.
+     * Note that we do NOT bother with quoted arguments here, so don't use
+     * filenames with spaces in 'em! */
+
+    argv[argc++] = PROGNAME;
+    p = cmd;
+    for (;;) {
+        if (*p == ' ')
+            while (*++p == ' ')
+                ;
+        /* now p points at the first non-space after some spaces */
+        if (*p == '\0')
+            break;    /* nothing after the spaces:  done */
+        argv[argc++] = q = p;
+        while (*q && *q != ' ')
+            ++q;
+        /* now q points at a space or the end of the string */
+        if (*q == '\0')
+            break;    /* last argv already terminated; quit */
+        *q = '\0';    /* change space to terminator */
+        p = q + 1;
+    }
+    argv[argc] = NULL;   /* terminate the argv array itself */
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strncmp(*argv, "-gamma", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                rpng2_info.display_exponent = atof(*argv);
+                if (rpng2_info.display_exponent <= 0.0)
+                    ++error;
+            }
+        } else if (!strncmp(*argv, "-bgcolor", 4)) {
+            if (!*++argv)
+                ++error;
+            else {
+                bgstr = *argv;
+                if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                    ++error;
+                else {
+                    have_bg = TRUE;
+                    bg_image = FALSE;
+                }
+            }
+        } else if (!strncmp(*argv, "-bgpat", 4)) {
+            if (!*++argv)
+                ++error;
+            else {
+                pat = atoi(*argv) - 1;
+                if (pat < 0 || pat >= num_bgpat)
+                    ++error;
+                else {
+                    bg_image = TRUE;
+                    have_bg = FALSE;
+                }
+            }
+        } else if (!strncmp(*argv, "-timing", 2)) {
+            timing = TRUE;
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename)
+        ++error;
+
+
+    /* 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"
+          "        %*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"
+          "\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; overrides -bgpat option\n"
+          "    pat \tdesired background pattern number (1-%d); used with\n"
+          "\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"
+          "\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__)) && \
+    !(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);
+    }
+
+
+    if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+        if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
+            fprintf(stderr, PROGNAME
+              ":  [%s] is not a PNG file: incorrect signature\n",
+              filename);
+            ++error;
+        } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
+            switch (rc) {
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng2_init() error\n");
+                    break;
+            }
+            ++error;
+        }
+        if (error)
+            fclose(infile);
+    }
+
+
+    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);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* set some final rpng2_info variables before entering main data loop */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        rpng2_info.bg_red   = (uch)r;
+        rpng2_info.bg_green = (uch)g;
+        rpng2_info.bg_blue  = (uch)b;
+    } else
+        rpng2_info.need_bgcolor = TRUE;
+
+    rpng2_info.state = kPreInit;
+    rpng2_info.mainprog_init = rpng2_win_init;
+    rpng2_info.mainprog_display_row = rpng2_win_display_row;
+    rpng2_info.mainprog_finish_display = rpng2_win_finish_display;
+
+
+    /* OK, this is the fun part:  call readpng2_decode_data() at the start of
+     * the loop to deal with our first buffer of data (read in above to verify
+     * that the file is a PNG image), then loop through the file and continue
+     * calling the same routine to handle each chunk of data.  It in turn
+     * passes the data to libpng, which will invoke one or more of our call-
+     * backs as decoded data become available.  We optionally call Sleep() for
+     * one second per iteration to simulate downloading the image via an analog
+     * modem. */
+
+    for (;;) {
+        Trace((stderr, "about to call readpng2_decode_data()\n"))
+        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
+            ++error;
+        Trace((stderr, "done with readpng2_decode_data()\n"))
+
+        if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
+            if (rpng2_info.state == kDone) {
+                Trace((stderr, "done decoding PNG image\n"))
+            } else if (ferror(infile)) {
+                fprintf(stderr, PROGNAME
+                  ":  error while reading PNG image file\n");
+                exit(3);
+            } else if (feof(infile)) {
+                fprintf(stderr, PROGNAME ":  end of file reached "
+                  "(unexpectedly) while reading PNG image file\n");
+                exit(3);
+            } else /* if (error) */ {
+                /* will print error message below */
+            }
+            break;
+        }
+
+        if (timing)
+            Sleep(1000L);
+
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+    }
+
+
+    /* clean up PNG stuff and report any decoding errors */
+
+    fclose(infile);
+    Trace((stderr, "about to call readpng2_cleanup()\n"))
+    readpng2_cleanup(&rpng2_info);
+
+    if (error) {
+        fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
+        exit(3);
+    }
+
+
+    /* wait for the user to tell us when to quit */
+
+    while (GetMessage(&msg, NULL, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+
+    /* we're done:  clean up all image and Windows resources and go away */
+
+    Trace((stderr, "about to call rpng2_win_cleanup()\n"))
+    rpng2_win_cleanup();
+
+    return msg.wParam;
+}
+
+
+
+
+
+/* this function is called by readpng2_info_callback() in readpng2.c, which
+ * in turn is called by libpng after all of the pre-IDAT chunks have been
+ * read and processed--i.e., we now have enough info to finish initializing */
+
+static void rpng2_win_init()
+{
+    ulg i;
+    ulg rowbytes = rpng2_info.rowbytes;
+
+    Trace((stderr, "beginning rpng2_win_init()\n"))
+    Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
+    Trace((stderr, "  width  = %ld\n", rpng2_info.width))
+    Trace((stderr, "  height = %ld\n", rpng2_info.height))
+
+    /* Guard against integer overflow */
+    if (rpng2_info.height > ((size_t)(-1))/rowbytes) {
+        fprintf(stderr, PROGNAME ":  image_data buffer would be too large\n",
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
+    if (!rpng2_info.image_data) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
+    if (!rpng2_info.row_pointers) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    for (i = 0;  i < rpng2_info.height;  ++i)
+        rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
+
+/*---------------------------------------------------------------------------
+    Do the basic Windows initialization stuff, make the window, and fill it
+    with the user-specified, file-specified or default background color.
+  ---------------------------------------------------------------------------*/
+
+    if (rpng2_win_create_window()) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.state = kWindowInit;
+}
+
+
+
+
+
+static int rpng2_win_create_window()
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *dest;
+    int extra_width, extra_height;
+    ulg i, j;
+    WNDCLASSEX wndclass;
+    RECT rect;
+
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the display-specific version of the image (round up
+    to multiple of 4 for Windows DIB).
+  ---------------------------------------------------------------------------*/
+
+    wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
+
+    if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
+                              wimage_rowbytes*rpng2_info.height)))
+    {
+        return 4;   /* fail */
+    }
+
+/*---------------------------------------------------------------------------
+    Initialize the DIB.  Negative height means to use top-down BMP ordering
+    (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
+    implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
+    directly => wimage_data begins immediately after BMP header.
+  ---------------------------------------------------------------------------*/
+
+    memset(dib, 0, sizeof(BITMAPINFOHEADER));
+    bmih = (BITMAPINFOHEADER *)dib;
+    bmih->biSize = sizeof(BITMAPINFOHEADER);
+    bmih->biWidth = rpng2_info.width;
+    bmih->biHeight = -((long)rpng2_info.height);
+    bmih->biPlanes = 1;
+    bmih->biBitCount = 24;
+    bmih->biCompression = 0;
+    wimage_data = dib + sizeof(BITMAPINFOHEADER);
+
+/*---------------------------------------------------------------------------
+    Fill window with the specified background color (default is black), but
+    defer loading faked "background image" until window is displayed (may be
+    slow to compute).  Data are in BGR order.
+  ---------------------------------------------------------------------------*/
+
+    if (bg_image) {   /* just fill with black for now */
+        memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
+    } else {
+        for (j = 0;  j < rpng2_info.height;  ++j) {
+            dest = wimage_data + j*wimage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                *dest++ = bg_blue;
+                *dest++ = bg_green;
+                *dest++ = bg_red;
+            }
+        }
+    }
+
+/*---------------------------------------------------------------------------
+    Set the window parameters.
+  ---------------------------------------------------------------------------*/
+
+    memset(&wndclass, 0, sizeof(wndclass));
+
+    wndclass.cbSize = sizeof(wndclass);
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = rpng2_win_wndproc;
+    wndclass.hInstance = global_hInst;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
+    wndclass.lpszMenuName = NULL;
+    wndclass.lpszClassName = progname;
+    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+
+    RegisterClassEx(&wndclass);
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
+                      GetSystemMetrics(SM_CXDLGFRAME));
+    extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
+                      GetSystemMetrics(SM_CYDLGFRAME)) +
+                      GetSystemMetrics(SM_CYCAPTION);
+
+    global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width,
+      rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL);
+
+    ShowWindow(global_hwnd, global_showmode);
+    UpdateWindow(global_hwnd);
+
+/*---------------------------------------------------------------------------
+    Now compute the background image and display it.  If it fails (memory
+    allocation), revert to a plain background color.
+  ---------------------------------------------------------------------------*/
+
+    if (bg_image) {
+        static const char *msg = "Computing background image...";
+        int x, y, len = strlen(msg);
+        HDC hdc = GetDC(global_hwnd);
+        TEXTMETRIC tm;
+
+        GetTextMetrics(hdc, &tm);
+        x = (rpng2_info.width - len*tm.tmAveCharWidth)/2;
+        y = (rpng2_info.height - tm.tmHeight)/2;
+        SetBkMode(hdc, TRANSPARENT);
+        SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+        /* this can still begin out of bounds even if x is positive (???): */
+        TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len);
+        ReleaseDC(global_hwnd, hdc);
+
+        rpng2_win_load_bg_image();   /* resets bg_image if fails */
+    }
+
+    if (!bg_image) {
+        for (j = 0;  j < rpng2_info.height;  ++j) {
+            dest = wimage_data + j*wimage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                *dest++ = bg_blue;
+                *dest++ = bg_green;
+                *dest++ = bg_red;
+            }
+        }
+    }
+
+    rect.left = 0L;
+    rect.top = 0L;
+    rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
+    rect.bottom = (LONG)rpng2_info.height;     /* possibly off by one? */
+    InvalidateRect(global_hwnd, &rect, FALSE);
+    UpdateWindow(global_hwnd);                 /* similar to XFlush() */
+
+    return 0;
+
+} /* end function rpng2_win_create_window() */
+
+
+
+
+
+static int rpng2_win_load_bg_image()
+{
+    uch *src, *dest;
+    uch r1, r2, g1, g2, b1, b2;
+    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
+    int k, hmax, max;
+    int xidx, yidx, yidx_max = (bgscale-1);
+    int even_odd_vert, even_odd_horiz, even_odd;
+    int invert_gradient2 = (bg[pat].type & 0x08);
+    int invert_column;
+    ulg i, row;
+
+/*---------------------------------------------------------------------------
+    Allocate buffer for fake background image to be used with transparent
+    images; if this fails, revert to plain background color.
+  ---------------------------------------------------------------------------*/
+
+    bg_rowbytes = 3 * rpng2_info.width;
+    bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
+    if (!bg_data) {
+        fprintf(stderr, PROGNAME
+          ":  unable to allocate memory for background image\n");
+        bg_image = 0;
+        return 1;
+    }
+
+/*---------------------------------------------------------------------------
+    Vertical gradients (ramps) in NxN squares, alternating direction and
+    colors (N == bgscale).
+  ---------------------------------------------------------------------------*/
+
+    if ((bg[pat].type & 0x07) == 0) {
+        uch r1_min  = rgb[bg[pat].rgb1_min].r;
+        uch g1_min  = rgb[bg[pat].rgb1_min].g;
+        uch b1_min  = rgb[bg[pat].rgb1_min].b;
+        uch r2_min  = rgb[bg[pat].rgb2_min].r;
+        uch g2_min  = rgb[bg[pat].rgb2_min].g;
+        uch b2_min  = rgb[bg[pat].rgb2_min].b;
+        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
+        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
+        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
+        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
+        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
+        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = row % bgscale;
+            even_odd_vert = (row / bgscale) & 1;
+
+            r1 = r1_min + (r1_diff * yidx) / yidx_max;
+            g1 = g1_min + (g1_diff * yidx) / yidx_max;
+            b1 = b1_min + (b1_diff * yidx) / yidx_max;
+            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
+            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
+            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
+
+            r2 = r2_min + (r2_diff * yidx) / yidx_max;
+            g2 = g2_min + (g2_diff * yidx) / yidx_max;
+            b2 = b2_min + (b2_diff * yidx) / yidx_max;
+            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
+            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
+            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
+
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                even_odd_horiz = (i / bgscale) & 1;
+                even_odd = even_odd_vert ^ even_odd_horiz;
+                invert_column =
+                  (even_odd_horiz && (bg[pat].type & 0x10));
+                if (even_odd == 0) {         /* gradient #1 */
+                    if (invert_column) {
+                        *dest++ = r1_inv;
+                        *dest++ = g1_inv;
+                        *dest++ = b1_inv;
+                    } else {
+                        *dest++ = r1;
+                        *dest++ = g1;
+                        *dest++ = b1;
+                    }
+                } else {                     /* gradient #2 */
+                    if ((invert_column && invert_gradient2) ||
+                        (!invert_column && !invert_gradient2))
+                    {
+                        *dest++ = r2;        /* not inverted or */
+                        *dest++ = g2;        /*  doubly inverted */
+                        *dest++ = b2;
+                    } else {
+                        *dest++ = r2_inv;
+                        *dest++ = g2_inv;    /* singly inverted */
+                        *dest++ = b2_inv;
+                    }
+                }
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
+    M. Costello.
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 1) {
+
+        hmax = (bgscale-1)/2;   /* half the max weight of a color */
+        max = 2*hmax;           /* the max weight of a color */
+
+        r1 = rgb[bg[pat].rgb1_max].r;
+        g1 = rgb[bg[pat].rgb1_max].g;
+        b1 = rgb[bg[pat].rgb1_max].b;
+        r2 = rgb[bg[pat].rgb2_max].r;
+        g2 = rgb[bg[pat].rgb2_max].g;
+        b2 = rgb[bg[pat].rgb2_max].b;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = row % bgscale;
+            if (yidx > hmax)
+                yidx = bgscale-1 - yidx;
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                xidx = i % bgscale;
+                if (xidx > hmax)
+                    xidx = bgscale-1 - xidx;
+                k = xidx + yidx;
+                *dest++ = (k*r1 + (max-k)*r2) / max;
+                *dest++ = (k*g1 + (max-k)*g2) / max;
+                *dest++ = (k*b1 + (max-k)*b2) / max;
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
+    soids will equal bgscale?].  This one is slow but very cool.  Code con-
+    tributed by Pieter S. van der Meulen (originally in Smalltalk).
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 2) {
+        uch ch;
+        int ii, x, y, hw, hh, grayspot;
+        double freq, rotate, saturate, gray, intensity;
+        double angle=0.0, aoffset=0.0, maxDist, dist;
+        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
+
+        fprintf(stderr, "%s:  computing radial background...",
+          PROGNAME);
+        fflush(stderr);
+
+        hh = rpng2_info.height / 2;
+        hw = rpng2_info.width / 2;
+
+        /* variables for radial waves:
+         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
+         *   freq:  number of color beams originating from the center
+         *   grayspot:  size of the graying center area (anti-alias)
+         *   rotate:  rotation of the beams as a function of radius
+         *   saturate:  saturation of beams' shape azimuthally
+         */
+        angle = CLIP(angle, 0.0, 360.0);
+        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
+        freq = MAX((double)bg[pat].bg_freq, 0.0);
+        saturate = (double)bg[pat].bg_bsat * 0.1;
+        rotate = (double)bg[pat].bg_brot * 0.1;
+        gray = 0.0;
+        intensity = 0.0;
+        maxDist = (double)((hw*hw) + (hh*hh));
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            y = row - hh;
+            dest = bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                x = i - hw;
+                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
+                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
+                gray = MIN(1.0, gray);
+                dist = (double)((x*x) + (y*y)) / maxDist;
+                intensity = cos((angle+(rotate*dist*PI)) * freq) *
+                  gray * saturate;
+                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
+                hue = (angle + PI) * INV_PI_360 + aoffset;
+                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
+                s = MIN(MAX(s,0.0), 1.0);
+                v = MIN(MAX(intensity,0.0), 1.0);
+
+                if (s == 0.0) {
+                    ch = (uch)(v * 255.0);
+                    *dest++ = ch;
+                    *dest++ = ch;
+                    *dest++ = ch;
+                } else {
+                    if ((hue < 0.0) || (hue >= 360.0))
+                        hue -= (((int)(hue / 360.0)) * 360.0);
+                    hue /= 60.0;
+                    ii = (int)hue;
+                    f = hue - (double)ii;
+                    p = (1.0 - s) * v;
+                    q = (1.0 - (s * f)) * v;
+                    t = (1.0 - (s * (1.0 - f))) * v;
+                    if      (ii == 0) { red = v; green = t; blue = p; }
+                    else if (ii == 1) { red = q; green = v; blue = p; }
+                    else if (ii == 2) { red = p; green = v; blue = t; }
+                    else if (ii == 3) { red = p; green = q; blue = v; }
+                    else if (ii == 4) { red = t; green = p; blue = v; }
+                    else if (ii == 5) { red = v; green = p; blue = q; }
+                    *dest++ = (uch)(red * 255.0);
+                    *dest++ = (uch)(green * 255.0);
+                    *dest++ = (uch)(blue * 255.0);
+                }
+            }
+        }
+        fprintf(stderr, "done.\n");
+        fflush(stderr);
+    }
+
+/*---------------------------------------------------------------------------
+    Blast background image to display buffer before beginning PNG decode;
+    calling function will handle invalidation and UpdateWindow() call.
+  ---------------------------------------------------------------------------*/
+
+    for (row = 0;  row < rpng2_info.height;  ++row) {
+        src = bg_data + row*bg_rowbytes;
+        dest = wimage_data + row*wimage_rowbytes;
+        for (i = rpng2_info.width;  i > 0;  --i) {
+            r1 = *src++;
+            g1 = *src++;
+            b1 = *src++;
+            *dest++ = b1;
+            *dest++ = g1;   /* note reverse order */
+            *dest++ = r1;
+        }
+    }
+
+    return 0;
+
+} /* end function rpng2_win_load_bg_image() */
+
+
+
+
+
+static void rpng2_win_display_row(ulg row)
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *src, *src2=NULL, *dest;
+    uch r, g, b, a;
+    ulg i;
+    static int rows=0;
+    static ulg firstrow;
+
+/*---------------------------------------------------------------------------
+    rows and firstrow simply track how many rows (and which ones) have not
+    yet been displayed; alternatively, we could call InvalidateRect() for
+    every row and not bother with the records-keeping.
+  ---------------------------------------------------------------------------*/
+
+    Trace((stderr, "beginning rpng2_win_display_row()\n"))
+
+    if (rows == 0)
+        firstrow = row;   /* first row not yet displayed */
+
+    ++rows;   /* count of rows received but not yet displayed */
+
+/*---------------------------------------------------------------------------
+    Aside from the use of the rpng2_info struct and the lack of an outer
+    loop (over rows), this routine is identical to rpng_win_display_image()
+    in the non-progressive version of the program.
+  ---------------------------------------------------------------------------*/
+
+    src = rpng2_info.image_data + row*rpng2_info.rowbytes;
+    if (bg_image)
+        src2 = bg_data + row*bg_rowbytes;
+    dest = wimage_data + row*wimage_rowbytes;
+
+    if (rpng2_info.channels == 3) {
+        for (i = rpng2_info.width;  i > 0;  --i) {
+            r = *src++;
+            g = *src++;
+            b = *src++;
+            *dest++ = b;
+            *dest++ = g;   /* note reverse order */
+            *dest++ = r;
+        }
+    } else /* if (rpng2_info.channels == 4) */ {
+        for (i = rpng2_info.width;  i > 0;  --i) {
+            r = *src++;
+            g = *src++;
+            b = *src++;
+            a = *src++;
+            if (bg_image) {
+                bg_red   = *src2++;
+                bg_green = *src2++;
+                bg_blue  = *src2++;
+            }
+            if (a == 255) {
+                *dest++ = b;
+                *dest++ = g;
+                *dest++ = r;
+            } else if (a == 0) {
+                *dest++ = bg_blue;
+                *dest++ = bg_green;
+                *dest++ = bg_red;
+            } else {
+                /* this macro (copied from png.h) composites the
+                 * foreground and background values and puts the
+                 * result into the first argument; there are no
+                 * side effects with the first argument */
+                alpha_composite(*dest++, b, a, bg_blue);
+                alpha_composite(*dest++, g, a, bg_green);
+                alpha_composite(*dest++, r, a, bg_red);
+            }
+        }
+    }
+
+/*---------------------------------------------------------------------------
+    Display after every 16 rows or when on last row.  (Region may include
+    previously displayed lines due to interlacing--i.e., not contiguous.)
+  ---------------------------------------------------------------------------*/
+
+    if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
+        RECT rect;
+
+        rect.left = 0L;
+        rect.top = (LONG)firstrow;
+        rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
+        rect.bottom = (LONG)row + 1L;              /* possibly off by one? */
+        InvalidateRect(global_hwnd, &rect, FALSE);
+        UpdateWindow(global_hwnd);                 /* similar to XFlush() */
+        rows = 0;
+    }
+
+} /* end function rpng2_win_display_row() */
+
+
+
+
+
+static void rpng2_win_finish_display()
+{
+    Trace((stderr, "beginning rpng2_win_finish_display()\n"))
+
+    /* last row has already been displayed by rpng2_win_display_row(), so
+     * we have nothing to do here except set a flag and let the user know
+     * that the image is done */
+
+    rpng2_info.state = kDone;
+    printf(
+#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);
+}
+
+
+
+
+
+static void rpng2_win_cleanup()
+{
+    if (bg_image && bg_data) {
+        free(bg_data);
+        bg_data = NULL;
+    }
+
+    if (rpng2_info.image_data) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+    }
+
+    if (rpng2_info.row_pointers) {
+        free(rpng2_info.row_pointers);
+        rpng2_info.row_pointers = NULL;
+    }
+
+    if (dib) {
+        free(dib);
+        dib = NULL;
+    }
+}
+
+
+
+
+
+LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
+{
+    HDC         hdc;
+    PAINTSTRUCT ps;
+    int rc;
+
+    switch (iMsg) {
+        case WM_CREATE:
+            /* one-time processing here, if any */
+            return 0;
+
+        case WM_PAINT:
+            hdc = BeginPaint(hwnd, &ps);
+            rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height,
+                                    0, 0, rpng2_info.width, rpng2_info.height,
+                                    wimage_data, (BITMAPINFO *)bmih,
+                                    0, SRCCOPY);
+            EndPaint(hwnd, &ps);
+            return 0;
+
+        /* wait for the user to tell us when to quit */
+        case WM_CHAR:
+            switch (wP) {       /* only need one, so ignore repeat count */
+                case 'q':
+                case 'Q':
+                case 0x1B:      /* Esc key */
+                    PostQuitMessage(0);
+            }
+            return 0;
+
+        case WM_LBUTTONDOWN:    /* another way of quitting */
+        case WM_DESTROY:
+            PostQuitMessage(0);
+            return 0;
+    }
+
+    return DefWindowProc(hwnd, iMsg, wP, lP);
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng2-x.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng2-x.c
new file mode 100755
index 0000000..af944c0
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/rpng2-x.c
@@ -0,0 +1,2143 @@
+/*---------------------------------------------------------------------------
+
+   rpng2 - progressive-model PNG display program                  rpng2-x.c
+
+   This program decodes and displays PNG files progressively, as if it were
+   a web browser (though the front end is only set up to read from files).
+   It supports gamma correction, user-specified background colors, and user-
+   specified background patterns (for transparent images).  This version is
+   for the X Window System (tested by the author under Unix and by Martin
+   Zinser under OpenVMS; may work under OS/2 with a little tweaking).
+
+   Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond"
+   and "radial waves" patterns, respectively.
+
+   to do (someday, maybe):
+    - fix expose/redraw code:  don't draw entire row if only part exposed
+    - 8-bit (colormapped) X support
+    - finish resizable checkerboard-gradient (sizes 4-128?)
+    - use %.1023s to simplify truncation of title-bar string?
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+    - 1.01:  initial public release
+    - 1.02:  modified to allow abbreviated options; fixed char/uchar mismatch
+    - 1.10:  added support for non-default visuals; fixed X pixel-conversion
+    - 1.11:  added -usleep option for demos; fixed command-line parsing bug
+    - 1.12:  added -pause option for demos and testing
+    - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
+    - 1.21:  fixed some small X memory leaks (thanks to François Petitjean)
+    - 1.22:  fixed XFreeGC() crash bug (thanks to Patrick Welche)
+    - 1.23:  added -bgpat 0 mode (std white/gray checkerboard, 8x8 squares)
+    - 1.30:  added -loop option for -bgpat (ifdef FEATURE_LOOP); fixed bpp =
+              24; added support for X resources (thanks to Gerhard Niklasch)
+    - 1.31:  added code to skip unused chunks (thanks to Glenn Randers-Pehrson)
+    - 1.32:  added AMD64/EM64T support (__x86_64__); added basic expose/redraw
+              handling
+    - 2.00:  dual-licensed (added GNU GPL)
+    - 2.01:  fixed 64-bit typo in readpng2.c; fixed -pause usage description
+    - 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).
+    - 2.06:  check for integer overflow (Glenn R-P)
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2010, 2014-2015, 2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "rpng2-x"
+#define LONGNAME  "Progressive PNG Viewer for X"
+#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>
+#include <ctype.h>
+#include <string.h>
+#include <setjmp.h>       /* for jmpbuf declaration in readpng2.h */
+#include <time.h>
+#include <math.h>         /* only for PvdM background code */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#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
+
+/* all for PvdM background code: */
+#ifndef PI
+#  define PI             3.141592653589793238
+#endif
+#define PI_2             (PI*0.5)
+#define INV_PI_360       (360.0 / PI)
+#define MAX(a,b)         (a>b?a:b)
+#define MIN(a,b)         (a<b?a:b)
+#define CLIP(a,min,max)  MAX(min,MIN((a),max))
+#define ABS(a)           ((a)<0?-(a):(a))
+#define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
+#define ROUNDF(f)        ((int)(f + 0.5))
+
+#define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) ||  \
+                  (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 rgb1_max   bg_freq
+#define rgb1_min   bg_gray
+#define rgb2_max   bg_bsat
+#define rgb2_min   bg_brot
+
+/* #define DEBUG */     /* this enables the Trace() macros */
+
+#include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
+
+
+/* could just include png.h, but this macro is the only thing we need
+ * (name and typedefs changed to local versions); note that side effects
+ * only happen with alpha (which could easily be avoided with
+ * "ush acopy = (alpha);") */
+
+#define alpha_composite(composite, fg, alpha, bg) {               \
+    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
+                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
+    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
+}
+
+
+#define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
+                          *  block size corresponds roughly to a download
+                          *  speed 10% faster than theoretical 33.6K maximum
+                          *  (assuming 8 data bits, 1 stop bit and no other
+                          *  overhead) */
+
+/* local prototypes */
+static void rpng2_x_init (void);
+static int  rpng2_x_create_window (void);
+static int  rpng2_x_load_bg_image (void);
+static void rpng2_x_display_row (ulg row);
+static void rpng2_x_finish_display (void);
+static void rpng2_x_redisplay_image (ulg startcol, ulg startrow,
+                                     ulg width, ulg height);
+#ifdef FEATURE_LOOP
+static void rpng2_x_reload_bg_image (void);
+static int  is_number (char *p);
+#endif
+static void rpng2_x_cleanup (void);
+static int  rpng2_x_msb (ulg u32val);
+
+
+static char titlebar[1024], *window_name = titlebar;
+static char *appname = LONGNAME;
+static char *icon_name = PROGNAME;
+static char *res_name = RESNAME;
+static char *res_class = RESCLASS;
+static char *filename;
+static FILE *infile;
+
+static mainprog_info rpng2_info;
+
+static uch inbuf[INBUFSIZE];
+static int incount;
+
+static int pat = 6;        /* must be less than num_bgpat */
+static int bg_image = 0;
+static int bgscale, bgscale_default = 16;
+static ulg bg_rowbytes;
+static uch *bg_data;
+
+int pause_after_pass = FALSE;
+int demo_timing = FALSE;
+ulg usleep_duration = 0L;
+
+static struct rgb_color {
+    uch r, g, b;
+} rgb[] = {
+    {  0,   0,   0},    /*  0:  black */
+    {255, 255, 255},    /*  1:  white */
+    {173, 132,  57},    /*  2:  tan */
+    { 64, 132,   0},    /*  3:  medium green */
+    {189, 117,   1},    /*  4:  gold */
+    {253, 249,   1},    /*  5:  yellow */
+    {  0,   0, 255},    /*  6:  blue */
+    {  0,   0, 120},    /*  7:  medium blue */
+    {255,   0, 255},    /*  8:  magenta */
+    { 64,   0,  64},    /*  9:  dark magenta */
+    {255,   0,   0},    /* 10:  red */
+    { 64,   0,   0},    /* 11:  dark red */
+    {255, 127,   0},    /* 12:  orange */
+    {192,  96,   0},    /* 13:  darker orange */
+    { 24,  60,   0},    /* 14:  dark green-yellow */
+    { 85, 125, 200},    /* 15:  ice blue */
+    {192, 192, 192}     /* 16:  Netscape/Mosaic gray */
+};
+/* not used for now, but should be for error-checking:
+static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
+ */
+
+/*
+    This whole struct is a fairly cheesy way to keep the number of
+    command-line options to a minimum.  The radial-waves background
+    type is a particularly poor fit to the integer elements of the
+    struct...but a few macros and a little fixed-point math will do
+    wonders for ya.
+
+    type bits:
+       F E D C B A 9 8 7 6 5 4 3 2 1 0
+                             | | | | |
+                             | | +-+-+-- 0 = sharp-edged checkerboard
+                             | |         1 = soft diamonds
+                             | |         2 = radial waves
+                             | |       3-7 = undefined
+                             | +-- gradient #2 inverted?
+                             +-- alternating columns inverted?
+ */
+static struct background_pattern {
+    ush type;
+    int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
+    int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
+} bg[] = {
+    {0,     1,1, 16,16},        /* checkered:  white vs. light gray (basic) */
+    {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
+    {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
+    {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
+    {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
+    {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
+    {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
+    {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
+    {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
+    {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
+    {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
+    {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
+    {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
+    {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
+    {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
+    {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
+    {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
+};
+static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
+
+
+/* X-specific variables */
+static char *displayname;
+static XImage *ximage;
+static Display *display;
+static int depth;
+static Visual *visual;
+static XVisualInfo *visual_list;
+static int RShift, GShift, BShift;
+static ulg RMask, GMask, BMask;
+static Window window;
+static GC gc;
+static Colormap colormap;
+
+static int have_nondefault_visual = FALSE;
+static int have_colormap = FALSE;
+static int have_window = FALSE;
+static int have_gc = FALSE;
+
+
+
+
+int main(int argc, char **argv)
+{
+#ifdef sgi
+    char tmpline[80];
+#endif
+    char *p, *bgstr = NULL;
+    int rc, alen, flen;
+    int error = 0;
+    int timing = FALSE;
+    int have_bg = FALSE;
+#ifdef FEATURE_LOOP
+    int loop = FALSE;
+    long loop_interval = -1;            /* seconds (100,000 max) */
+#endif
+    double LUT_exponent;                /* just the lookup table */
+    double CRT_exponent = 2.2;          /* just the monitor */
+    double default_display_exponent;    /* whole display system */
+    XEvent e;
+    KeySym k;
+
+
+    /* First initialize a few things, just to be sure--memset takes care of
+     * default background color (black), booleans (FALSE), pointers (NULL),
+     * etc. */
+
+    displayname = (char *)NULL;
+    filename = (char *)NULL;
+    memset(&rpng2_info, 0, sizeof(mainprog_info));
+
+
+    /* Set the default value for our display-system exponent, i.e., the
+     * product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  This is not an
+     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
+     * ones), but it should cover 99% of the current possibilities. */
+
+#if defined(NeXT)
+    /* third-party utilities can modify the default LUT exponent */
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to
+     * get the "gamma" value, so we do it the hard way */
+    infile = fopen("/etc/config/system.glGammaVal", "r");
+    if (infile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, infile);
+        fclose(infile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL)
+        rpng2_info.display_exponent = atof(p);
+    else
+        rpng2_info.display_exponent = default_display_exponent;
+
+
+    /* Now parse the command line for options and the PNG filename. */
+
+    while (*++argv && !error) {
+        if (!strncmp(*argv, "-display", 2)) {
+            if (!*++argv)
+                ++error;
+            else
+                displayname = *argv;
+        } else if (!strncmp(*argv, "-gamma", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                rpng2_info.display_exponent = atof(*argv);
+                if (rpng2_info.display_exponent <= 0.0)
+                    ++error;
+            }
+        } else if (!strncmp(*argv, "-bgcolor", 4)) {
+            if (!*++argv)
+                ++error;
+            else {
+                bgstr = *argv;
+                if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                    ++error;
+                else {
+                    have_bg = TRUE;
+                    bg_image = FALSE;
+                }
+            }
+        } else if (!strncmp(*argv, "-bgpat", 4)) {
+            if (!*++argv)
+                ++error;
+            else {
+                pat = atoi(*argv);
+                if (pat >= 0 && pat < num_bgpat) {
+                    bg_image = TRUE;
+                    have_bg = FALSE;
+                } else
+                    ++error;
+            }
+        } else if (!strncmp(*argv, "-usleep", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                usleep_duration = (ulg)atol(*argv);
+                demo_timing = TRUE;
+            }
+        } else if (!strncmp(*argv, "-pause", 2)) {
+            pause_after_pass = TRUE;
+        } else if (!strncmp(*argv, "-timing", 2)) {
+            timing = TRUE;
+#ifdef FEATURE_LOOP
+        } else if (!strncmp(*argv, "-loop", 2)) {
+            loop = TRUE;
+            if (!argv[1] || !is_number(argv[1]))
+                loop_interval = 2;
+            else {
+                ++argv;
+                loop_interval = atol(*argv);
+                if (loop_interval < 0)
+                    loop_interval = 2;
+                else if (loop_interval > 100000)   /* bit more than one day */
+                    loop_interval = 100000;
+            }
+#endif
+        } else {
+            if (**argv != '-') {
+                filename = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+    if (!filename)
+        ++error;
+
+
+    /* print usage screen if any errors up to this point */
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
+        readpng2_version_info();
+        fprintf(stderr, "\n"
+          "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
+          "        [-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",
+          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",
+          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");
+#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");
+        exit(1);
+    }
+
+    if (!(infile = fopen(filename, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
+        ++error;
+    } else {
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+        if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
+            fprintf(stderr, PROGNAME
+              ":  [%s] is not a PNG file: incorrect signature\n",
+              filename);
+            ++error;
+        } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
+            switch (rc) {
+                case 2:
+                    fprintf(stderr, PROGNAME
+                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
+                    break;
+                case 4:
+                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                    break;
+                default:
+                    fprintf(stderr, PROGNAME
+                      ":  unknown readpng2_init() error\n");
+                    break;
+            }
+            ++error;
+        } else {
+            Trace((stderr, "about to call XOpenDisplay()\n"))
+            display = XOpenDisplay(displayname);
+            if (!display) {
+                readpng2_cleanup(&rpng2_info);
+                fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
+                  displayname? displayname : "default");
+                ++error;
+            }
+        }
+        if (error)
+            fclose(infile);
+    }
+
+
+    if (error) {
+        fprintf(stderr, PROGNAME ":  aborting.\n");
+        exit(2);
+    }
+
+
+    /* set the title-bar string, but make sure buffer doesn't overflow */
+
+    alen = strlen(appname);
+    flen = strlen(filename);
+    if (alen + flen + 3 > 1023)
+        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
+    else
+        sprintf(titlebar, "%s:  %s", appname, filename);
+
+
+    /* set some final rpng2_info variables before entering main data loop */
+
+    if (have_bg) {
+        unsigned r, g, b;   /* this approach quiets compiler warnings */
+
+        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+        rpng2_info.bg_red   = (uch)r;
+        rpng2_info.bg_green = (uch)g;
+        rpng2_info.bg_blue  = (uch)b;
+    } else
+        rpng2_info.need_bgcolor = TRUE;
+
+    rpng2_info.state = kPreInit;
+    rpng2_info.mainprog_init = rpng2_x_init;
+    rpng2_info.mainprog_display_row = rpng2_x_display_row;
+    rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
+
+
+    /* OK, this is the fun part:  call readpng2_decode_data() at the start of
+     * the loop to deal with our first buffer of data (read in above to verify
+     * that the file is a PNG image), then loop through the file and continue
+     * calling the same routine to handle each chunk of data.  It in turn
+     * passes the data to libpng, which will invoke one or more of our call-
+     * backs as decoded data become available.  We optionally call sleep() for
+     * one second per iteration to simulate downloading the image via an analog
+     * modem. */
+
+    for (;;) {
+        Trace((stderr, "about to call readpng2_decode_data()\n"))
+        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
+            ++error;
+        Trace((stderr, "done with readpng2_decode_data()\n"))
+
+        if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
+            if (rpng2_info.state == kDone) {
+                Trace((stderr, "done decoding PNG image\n"))
+            } else if (ferror(infile)) {
+                fprintf(stderr, PROGNAME
+                  ":  error while reading PNG image file\n");
+                exit(3);
+            } else if (feof(infile)) {
+                fprintf(stderr, PROGNAME ":  end of file reached "
+                  "(unexpectedly) while reading PNG image file\n");
+                exit(3);
+            } else /* if (error) */ {
+                /* will print error message below */
+            }
+            break;
+        }
+
+        if (timing)
+            sleep(1);
+
+        incount = fread(inbuf, 1, INBUFSIZE, infile);
+    }
+
+
+    /* clean up PNG stuff and report any decoding errors */
+
+    fclose(infile);
+    Trace((stderr, "about to call readpng2_cleanup()\n"))
+    readpng2_cleanup(&rpng2_info);
+
+    if (error) {
+        fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
+        exit(3);
+    }
+
+
+#ifdef FEATURE_LOOP
+
+    if (loop && bg_image) {
+        Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n"))
+        for (;;) {
+            int i, use_sleep;
+            struct timeval now, then;
+
+            /* get current time and add loop_interval to get target time */
+            if (gettimeofday(&then, NULL) == 0) {
+                then.tv_sec += loop_interval;
+                use_sleep = FALSE;
+            } else
+                use_sleep = TRUE;
+
+            /* do quick check for a quit event but don't wait for it */
+            /* GRR BUG:  should also check for Expose events and redraw... */
+            if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e))
+                if (QUIT(e,k))
+                    break;
+
+            /* generate next background image */
+            if (++pat >= num_bgpat)
+                pat = 0;
+            rpng2_x_reload_bg_image();
+
+            /* wait for timeout, using whatever means are available */
+            if (use_sleep || gettimeofday(&now, NULL) != 0) {
+                for (i = loop_interval;  i > 0;  --i) {
+                    sleep(1);
+                    /* GRR BUG:  also need to check for Expose (and redraw!) */
+                    if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask,
+                        &e) && QUIT(e,k))
+                        break;
+                }
+            } else {
+                /* Y2038 BUG! */
+                if (now.tv_sec < then.tv_sec ||
+                    (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec))
+                {
+                    int quit = FALSE;
+                    long seconds_to_go = then.tv_sec - now.tv_sec;
+                    long usleep_usec;
+
+                    /* basically chew up most of remaining loop-interval with
+                     *  calls to sleep(1) interleaved with checks for quit
+                     *  events, but also recalc time-to-go periodically; when
+                     *  done, clean up any remaining time with usleep() call
+                     *  (could also use SIGALRM, but signals are a pain...) */
+                    while (seconds_to_go-- > 1) {
+                        int seconds_done = 0;
+
+                        for (i = seconds_to_go;  i > 0 && !quit;  --i) {
+                            sleep(1);
+                            /* GRR BUG:  need to check for Expose and redraw */
+                            if (XCheckMaskEvent(display, KeyPressMask |
+                                ButtonPressMask, &e) && QUIT(e,k))
+                                quit = TRUE;
+                            if (++seconds_done > 1000)
+                                break;   /* time to redo seconds_to_go meas. */
+                        }
+                        if (quit)
+                            break;
+
+                        /* OK, more than 1000 seconds since last check:
+                         *  correct the time-to-go measurement for drift */
+                        if (gettimeofday(&now, NULL) == 0) {
+                            if (now.tv_sec >= then.tv_sec)
+                                break;
+                            seconds_to_go = then.tv_sec - now.tv_sec;
+                        } else
+                            ++seconds_to_go;  /* restore what we subtracted */
+                    }
+                    if (quit)
+                        break;   /* breaks outer do-loop, skips redisplay */
+
+                    /* since difference between "now" and "then" is already
+                     *  eaten up to within a couple of seconds, don't need to
+                     *  worry about overflow--but might have overshot (neg.) */
+                    if (gettimeofday(&now, NULL) == 0) {
+                        usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) +
+                          then.tv_usec - now.tv_usec;
+                        if (usleep_usec > 0)
+                            usleep((ulg)usleep_usec);
+                    }
+                }
+            }
+
+            /* composite image against new background and display (note that
+             *  we do not take into account the time spent doing this...) */
+            rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height);
+        }
+
+    } else /* FALL THROUGH and do the normal thing */
+
+#endif /* FEATURE_LOOP */
+
+    /* wait for the user to tell us when to quit */
+
+    if (rpng2_info.state >= kWindowInit) {
+        Trace((stderr, "entering final wait-for-quit-event loop\n"))
+        do {
+            XNextEvent(display, &e);
+            if (e.type == Expose) {
+                XExposeEvent *ex = (XExposeEvent *)&e;
+                rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height);
+            }
+        } while (!QUIT(e,k));
+    } else {
+        fprintf(stderr, PROGNAME ":  init callback never called:  probable "
+          "libpng error while decoding PNG metadata\n");
+        exit(4);
+    }
+
+
+    /* we're done:  clean up all image and X resources and go away */
+
+    Trace((stderr, "about to call rpng2_x_cleanup()\n"))
+    rpng2_x_cleanup();
+
+    (void)argc; /* Unused */
+
+    return 0;
+}
+
+
+
+
+
+/* this function is called by readpng2_info_callback() in readpng2.c, which
+ * in turn is called by libpng after all of the pre-IDAT chunks have been
+ * read and processed--i.e., we now have enough info to finish initializing */
+
+static void rpng2_x_init(void)
+{
+    ulg i;
+    ulg rowbytes = rpng2_info.rowbytes;
+
+    Trace((stderr, "beginning rpng2_x_init()\n"))
+    Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
+    Trace((stderr, "  width  = %ld\n", rpng2_info.width))
+    Trace((stderr, "  height = %ld\n", rpng2_info.height))
+
+    /* Guard against integer overflow */
+    if (rpng2_info.height > ((size_t)(-1))/rpng2_info.rowbytes) {
+        fprintf(stderr, PROGNAME ":  image_data buffer would be too large\n");
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
+    if (!rpng2_info.image_data) {
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
+    if (!rpng2_info.row_pointers) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+        readpng2_cleanup(&rpng2_info);
+        return;
+    }
+
+    for (i = 0;  i < rpng2_info.height;  ++i)
+        rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
+
+
+    /* do the basic X initialization stuff, make the window, and fill it with
+     * the user-specified, file-specified or default background color or
+     * pattern */
+
+    if (rpng2_x_create_window()) {
+
+        /* GRR TEMPORARY HACK:  this is fundamentally no different from cases
+         * 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);
+        rpng2_x_cleanup();
+        exit(2);
+    }
+
+    rpng2_info.state = kWindowInit;
+}
+
+
+
+
+
+static int rpng2_x_create_window(void)
+{
+    ulg bg_red   = rpng2_info.bg_red;
+    ulg bg_green = rpng2_info.bg_green;
+    ulg bg_blue  = rpng2_info.bg_blue;
+    ulg bg_pixel = 0L;
+    ulg attrmask;
+    int need_colormap = FALSE;
+    int screen, pad;
+    uch *xdata;
+    Window root;
+    XEvent e;
+    XGCValues gcvalues;
+    XSetWindowAttributes attr;
+    XTextProperty windowName, *pWindowName = &windowName;
+    XTextProperty iconName, *pIconName = &iconName;
+    XVisualInfo visual_info;
+    XSizeHints *size_hints;
+    XWMHints *wm_hints;
+    XClassHint *class_hints;
+
+
+    Trace((stderr, "beginning rpng2_x_create_window()\n"))
+
+    screen = DefaultScreen(display);
+    depth = DisplayPlanes(display, screen);
+    root = RootWindow(display, screen);
+
+#ifdef DEBUG
+    XSynchronize(display, True);
+#endif
+
+    if (depth != 16 && depth != 24 && depth != 32) {
+        int visuals_matched = 0;
+
+        Trace((stderr, "default depth is %d:  checking other visuals\n",
+          depth))
+
+        /* 24-bit first */
+        visual_info.screen = screen;
+        visual_info.depth = 24;
+        visual_list = XGetVisualInfo(display,
+          VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
+        if (visuals_matched == 0) {
+/* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
+            fprintf(stderr, "default screen depth %d not supported, and no"
+              " 24-bit visuals found\n", depth);
+            return 2;
+        }
+        Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
+          visuals_matched))
+        visual = visual_list[0].visual;
+        depth = visual_list[0].depth;
+/*
+        colormap_size = visual_list[0].colormap_size;
+        visual_class = visual->class;
+        visualID = XVisualIDFromVisual(visual);
+ */
+        have_nondefault_visual = TRUE;
+        need_colormap = TRUE;
+    } else {
+        XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
+        visual = visual_info.visual;
+    }
+
+    RMask = visual->red_mask;
+    GMask = visual->green_mask;
+    BMask = visual->blue_mask;
+
+/* GRR:  add/check 8-bit support */
+    if (depth == 8 || need_colormap) {
+        colormap = XCreateColormap(display, root, visual, AllocNone);
+        if (!colormap) {
+            fprintf(stderr, "XCreateColormap() failed\n");
+            return 2;
+        }
+        have_colormap = TRUE;
+        if (depth == 8)
+            bg_image = FALSE;   /* gradient just wastes palette entries */
+    }
+    if (depth == 15 || depth == 16) {
+        RShift = 15 - rpng2_x_msb(RMask);    /* these are right-shifts */
+        GShift = 15 - rpng2_x_msb(GMask);
+        BShift = 15 - rpng2_x_msb(BMask);
+    } else if (depth > 16) {
+        RShift = rpng2_x_msb(RMask) - 7;     /* these are left-shifts */
+        GShift = rpng2_x_msb(GMask) - 7;
+        BShift = rpng2_x_msb(BMask) - 7;
+    }
+    if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
+        fprintf(stderr, "rpng2 internal logic error:  negative X shift(s)!\n");
+        return 2;
+    }
+
+/*---------------------------------------------------------------------------
+    Finally, create the window.
+  ---------------------------------------------------------------------------*/
+
+    attr.backing_store = Always;
+    attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
+    attrmask = CWBackingStore | CWEventMask;
+    if (have_nondefault_visual) {
+        attr.colormap = colormap;
+        attr.background_pixel = 0;
+        attr.border_pixel = 1;
+        attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
+    }
+
+    window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
+      rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
+
+    if (window == None) {
+        fprintf(stderr, "XCreateWindow() failed\n");
+        return 2;
+    } else
+        have_window = TRUE;
+
+    if (depth == 8)
+        XSetWindowColormap(display, window, colormap);
+
+    if (!XStringListToTextProperty(&window_name, 1, pWindowName))
+        pWindowName = NULL;
+    if (!XStringListToTextProperty(&icon_name, 1, pIconName))
+        pIconName = NULL;
+
+    /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
+
+    if ((size_hints = XAllocSizeHints()) != NULL) {
+        /* window will not be resizable */
+        size_hints->flags = PMinSize | PMaxSize;
+        size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
+        size_hints->min_height = size_hints->max_height =
+          (int)rpng2_info.height;
+    }
+
+    if ((wm_hints = XAllocWMHints()) != NULL) {
+        wm_hints->initial_state = NormalState;
+        wm_hints->input = True;
+     /* wm_hints->icon_pixmap = icon_pixmap; */
+        wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
+    }
+
+    if ((class_hints = XAllocClassHint()) != NULL) {
+        class_hints->res_name = res_name;
+        class_hints->res_class = res_class;
+    }
+
+    XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
+      size_hints, wm_hints, class_hints);
+
+    /* various properties and hints no longer needed; free memory */
+    if (pWindowName)
+       XFree(pWindowName->value);
+    if (pIconName)
+       XFree(pIconName->value);
+    if (size_hints)
+        XFree(size_hints);
+    if (wm_hints)
+       XFree(wm_hints);
+    if (class_hints)
+       XFree(class_hints);
+
+    XMapWindow(display, window);
+
+    gc = XCreateGC(display, window, 0, &gcvalues);
+    have_gc = TRUE;
+
+/*---------------------------------------------------------------------------
+    Allocate memory for the X- and display-specific version of the image.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
+        pad = 32;
+    } else if (depth == 16) {
+        xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
+        pad = 16;
+    } else /* depth == 8 */ {
+        xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
+        pad = 8;
+    }
+
+    if (!xdata) {
+        fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
+        return 4;
+    }
+
+    ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
+      (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
+
+    if (!ximage) {
+        fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
+        free(xdata);
+        return 3;
+    }
+
+    /* to avoid testing the byte order every pixel (or doubling the size of
+     * the drawing routine with a giant if-test), we arbitrarily set the byte
+     * order to MSBFirst and let Xlib worry about inverting things on little-
+     * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
+     * most efficient approach (the giant if-test would be better), but in
+     * the interest of clarity, we'll take the easy way out... */
+
+    ximage->byte_order = MSBFirst;
+
+/*---------------------------------------------------------------------------
+    Fill window with the specified background color (default is black) or
+    faked "background image" (but latter is disabled if 8-bit; gradients
+    just waste palette entries).
+  ---------------------------------------------------------------------------*/
+
+    if (bg_image)
+        rpng2_x_load_bg_image();    /* resets bg_image if fails */
+
+    if (!bg_image) {
+        if (depth == 24 || depth == 32) {
+            bg_pixel = (bg_red   << RShift) |
+                       (bg_green << GShift) |
+                       (bg_blue  << BShift);
+        } else if (depth == 16) {
+            bg_pixel = (((bg_red   << 8) >> RShift) & RMask) |
+                       (((bg_green << 8) >> GShift) & GMask) |
+                       (((bg_blue  << 8) >> BShift) & BMask);
+        } else /* depth == 8 */ {
+
+            /* GRR:  add 8-bit support */
+
+        }
+        XSetForeground(display, gc, bg_pixel);
+        XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
+          rpng2_info.height);
+    }
+
+/*---------------------------------------------------------------------------
+    Wait for first Expose event to do any drawing, then flush and return.
+  ---------------------------------------------------------------------------*/
+
+    do
+        XNextEvent(display, &e);
+    while (e.type != Expose || e.xexpose.count);
+
+    XFlush(display);
+
+    return 0;
+
+} /* end function rpng2_x_create_window() */
+
+
+
+
+
+static int rpng2_x_load_bg_image(void)
+{
+    uch *src;
+    char *dest;
+    uch r1, r2, g1, g2, b1, b2;
+    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
+    int k, hmax, max;
+    int xidx, yidx, yidx_max;
+    int even_odd_vert, even_odd_horiz, even_odd;
+    int invert_gradient2 = (bg[pat].type & 0x08);
+    int invert_column;
+    int ximage_rowbytes = ximage->bytes_per_line;
+    ulg i, row;
+    ulg pixel;
+
+/*---------------------------------------------------------------------------
+    Allocate buffer for fake background image to be used with transparent
+    images; if this fails, revert to plain background color.
+  ---------------------------------------------------------------------------*/
+
+    bg_rowbytes = 3 * rpng2_info.width;
+    bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
+    if (!bg_data) {
+        fprintf(stderr, PROGNAME
+          ":  unable to allocate memory for background image\n");
+        bg_image = 0;
+        return 1;
+    }
+
+    bgscale = (pat == 0)? 8 : bgscale_default;
+    yidx_max = bgscale - 1;
+
+/*---------------------------------------------------------------------------
+    Vertical gradients (ramps) in NxN squares, alternating direction and
+    colors (N == bgscale).
+  ---------------------------------------------------------------------------*/
+
+    if ((bg[pat].type & 0x07) == 0) {
+        uch r1_min  = rgb[bg[pat].rgb1_min].r;
+        uch g1_min  = rgb[bg[pat].rgb1_min].g;
+        uch b1_min  = rgb[bg[pat].rgb1_min].b;
+        uch r2_min  = rgb[bg[pat].rgb2_min].r;
+        uch g2_min  = rgb[bg[pat].rgb2_min].g;
+        uch b2_min  = rgb[bg[pat].rgb2_min].b;
+        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
+        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
+        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
+        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
+        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
+        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = (int)(row % bgscale);
+            even_odd_vert = (int)((row / bgscale) & 1);
+
+            r1 = r1_min + (r1_diff * yidx) / yidx_max;
+            g1 = g1_min + (g1_diff * yidx) / yidx_max;
+            b1 = b1_min + (b1_diff * yidx) / yidx_max;
+            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
+            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
+            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
+
+            r2 = r2_min + (r2_diff * yidx) / yidx_max;
+            g2 = g2_min + (g2_diff * yidx) / yidx_max;
+            b2 = b2_min + (b2_diff * yidx) / yidx_max;
+            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
+            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
+            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
+
+            dest = (char *)bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                even_odd_horiz = (int)((i / bgscale) & 1);
+                even_odd = even_odd_vert ^ even_odd_horiz;
+                invert_column =
+                  (even_odd_horiz && (bg[pat].type & 0x10));
+                if (even_odd == 0) {        /* gradient #1 */
+                    if (invert_column) {
+                        *dest++ = r1_inv;
+                        *dest++ = g1_inv;
+                        *dest++ = b1_inv;
+                    } else {
+                        *dest++ = r1;
+                        *dest++ = g1;
+                        *dest++ = b1;
+                    }
+                } else {                    /* gradient #2 */
+                    if ((invert_column && invert_gradient2) ||
+                        (!invert_column && !invert_gradient2))
+                    {
+                        *dest++ = r2;       /* not inverted or */
+                        *dest++ = g2;       /*  doubly inverted */
+                        *dest++ = b2;
+                    } else {
+                        *dest++ = r2_inv;
+                        *dest++ = g2_inv;   /* singly inverted */
+                        *dest++ = b2_inv;
+                    }
+                }
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
+    M. Costello.
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 1) {
+
+        hmax = (bgscale-1)/2;   /* half the max weight of a color */
+        max = 2*hmax;           /* the max weight of a color */
+
+        r1 = rgb[bg[pat].rgb1_max].r;
+        g1 = rgb[bg[pat].rgb1_max].g;
+        b1 = rgb[bg[pat].rgb1_max].b;
+        r2 = rgb[bg[pat].rgb2_max].r;
+        g2 = rgb[bg[pat].rgb2_max].g;
+        b2 = rgb[bg[pat].rgb2_max].b;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = (int)(row % bgscale);
+            if (yidx > hmax)
+                yidx = bgscale-1 - yidx;
+            dest = (char *)bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                xidx = (int)(i % bgscale);
+                if (xidx > hmax)
+                    xidx = bgscale-1 - xidx;
+                k = xidx + yidx;
+                *dest++ = (k*r1 + (max-k)*r2) / max;
+                *dest++ = (k*g1 + (max-k)*g2) / max;
+                *dest++ = (k*b1 + (max-k)*b2) / max;
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
+    soids will equal bgscale?].  This one is slow but very cool.  Code con-
+    tributed by Pieter S. van der Meulen (originally in Smalltalk).
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 2) {
+        uch ch;
+        int ii, x, y, hw, hh, grayspot;
+        double freq, rotate, saturate, gray, intensity;
+        double angle=0.0, aoffset=0.0, maxDist, dist;
+        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
+
+        fprintf(stderr, "%s:  computing radial background...",
+          PROGNAME);
+        fflush(stderr);
+
+        hh = (int)(rpng2_info.height / 2);
+        hw = (int)(rpng2_info.width / 2);
+
+        /* variables for radial waves:
+         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
+         *   freq:  number of color beams originating from the center
+         *   grayspot:  size of the graying center area (anti-alias)
+         *   rotate:  rotation of the beams as a function of radius
+         *   saturate:  saturation of beams' shape azimuthally
+         */
+        angle = CLIP(angle, 0.0, 360.0);
+        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
+        freq = MAX((double)bg[pat].bg_freq, 0.0);
+        saturate = (double)bg[pat].bg_bsat * 0.1;
+        rotate = (double)bg[pat].bg_brot * 0.1;
+        gray = 0.0;
+        intensity = 0.0;
+        maxDist = (double)((hw*hw) + (hh*hh));
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            y = (int)(row - hh);
+            dest = (char *)bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                x = (int)(i - hw);
+                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
+                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
+                gray = MIN(1.0, gray);
+                dist = (double)((x*x) + (y*y)) / maxDist;
+                intensity = cos((angle+(rotate*dist*PI)) * freq) *
+                  gray * saturate;
+                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
+                hue = (angle + PI) * INV_PI_360 + aoffset;
+                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
+                s = MIN(MAX(s,0.0), 1.0);
+                v = MIN(MAX(intensity,0.0), 1.0);
+
+                if (s == 0.0) {
+                    ch = (uch)(v * 255.0);
+                    *dest++ = ch;
+                    *dest++ = ch;
+                    *dest++ = ch;
+                } else {
+                    if ((hue < 0.0) || (hue >= 360.0))
+                        hue -= (((int)(hue / 360.0)) * 360.0);
+                    hue /= 60.0;
+                    ii = (int)hue;
+                    f = hue - (double)ii;
+                    p = (1.0 - s) * v;
+                    q = (1.0 - (s * f)) * v;
+                    t = (1.0 - (s * (1.0 - f))) * v;
+                    if      (ii == 0) { red = v; green = t; blue = p; }
+                    else if (ii == 1) { red = q; green = v; blue = p; }
+                    else if (ii == 2) { red = p; green = v; blue = t; }
+                    else if (ii == 3) { red = p; green = q; blue = v; }
+                    else if (ii == 4) { red = t; green = p; blue = v; }
+                    else if (ii == 5) { red = v; green = p; blue = q; }
+                    *dest++ = (uch)(red * 255.0);
+                    *dest++ = (uch)(green * 255.0);
+                    *dest++ = (uch)(blue * 255.0);
+                }
+            }
+        }
+        fprintf(stderr, "done.\n");
+        fflush(stderr);
+    }
+
+/*---------------------------------------------------------------------------
+    Blast background image to display buffer before beginning PNG decode.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+        int bpp = ximage->bits_per_pixel;
+
+        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 */
+                for (i = rpng2_info.width;  i > 0;  --i) {
+                    red   = *src++;
+                    green = *src++;
+                    blue  = *src++;
+                    pixel = (red   << RShift) |
+                            (green << GShift) |
+                            (blue  << BShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >> 24) & 0xff);
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            } else {
+                for (i = rpng2_info.width;  i > 0;  --i) {
+                    red   = *src++;
+                    green = *src++;
+                    blue  = *src++;
+                    pixel = (red   << RShift) |
+                            (green << GShift) |
+                            (blue  << BShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
+                    /*           (probably need to use RShift, RMask, etc.) */
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            src = bg_data + row*bg_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = ((ush)(*src) << 8);  ++src;
+                green = ((ush)(*src) << 8);  ++src;
+                blue  = ((ush)(*src) << 8);  ++src;
+                pixel = ((red   >> RShift) & RMask) |
+                        ((green >> GShift) & GMask) |
+                        ((blue  >> BShift) & BMask);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                *dest++ = (char)((pixel >>  8) & 0xff);
+                *dest++ = (char)( pixel        & 0xff);
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
+      rpng2_info.height);
+
+    return 0;
+
+} /* end function rpng2_x_load_bg_image() */
+
+
+
+
+
+static void rpng2_x_display_row(ulg row)
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *src, *src2=NULL;
+    char *dest;
+    uch r, g, b, a;
+    int ximage_rowbytes = ximage->bytes_per_line;
+    ulg i, pixel;
+    static int rows=0, prevpass=(-1);
+    static ulg firstrow;
+
+/*---------------------------------------------------------------------------
+    rows and firstrow simply track how many rows (and which ones) have not
+    yet been displayed; alternatively, we could call XPutImage() for every
+    row and not bother with the records-keeping.
+  ---------------------------------------------------------------------------*/
+
+    Trace((stderr, "beginning rpng2_x_display_row()\n"))
+
+    if (rpng2_info.pass != prevpass) {
+        if (pause_after_pass && rpng2_info.pass > 0) {
+            XEvent e;
+            KeySym k;
+
+            fprintf(stderr,
+              "%s:  end of pass %d of 7; click in image window to continue\n",
+              PROGNAME, prevpass + 1);
+            do
+                XNextEvent(display, &e);
+            while (!QUIT(e,k));
+        }
+        fprintf(stderr, "%s:  pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
+        fflush(stderr);
+        prevpass = rpng2_info.pass;
+    }
+
+    if (rows == 0)
+        firstrow = row;   /* first row that is not yet displayed */
+
+    ++rows;   /* count of rows received but not yet displayed */
+
+/*---------------------------------------------------------------------------
+    Aside from the use of the rpng2_info struct, the lack of an outer loop
+    (over rows) and moving the XPutImage() call outside the "if (depth)"
+    tests, this routine is identical to rpng_x_display_image() in the non-
+    progressive version of the program.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+        int bpp = ximage->bits_per_pixel;
+
+        src = rpng2_info.image_data + row*rpng2_info.rowbytes;
+        if (bg_image)
+            src2 = bg_data + row*bg_rowbytes;
+        dest = ximage->data + row*ximage_rowbytes;
+        if (rpng2_info.channels == 3) {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = *src++;
+                green = *src++;
+                blue  = *src++;
+                pixel = (red   << RShift) |
+                        (green << GShift) |
+                        (blue  << BShift);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                if (bpp == 32) {
+                    *dest++ = (char)((pixel >> 24) & 0xff);
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                } else {
+                    /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
+                    /*           (probably need to use RShift, RMask, etc.) */
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            }
+        } else /* if (rpng2_info.channels == 4) */ {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                a = *src++;
+                if (bg_image) {
+                    bg_red   = *src2++;
+                    bg_green = *src2++;
+                    bg_blue  = *src2++;
+                }
+                if (a == 255) {
+                    red   = r;
+                    green = g;
+                    blue  = b;
+                } else if (a == 0) {
+                    red   = bg_red;
+                    green = bg_green;
+                    blue  = bg_blue;
+                } else {
+                    /* this macro (from png.h) composites the foreground
+                     * and background values and puts the result into the
+                     * first argument */
+                    alpha_composite(red,   r, a, bg_red);
+                    alpha_composite(green, g, a, bg_green);
+                    alpha_composite(blue,  b, a, bg_blue);
+                }
+                pixel = (red   << RShift) |
+                        (green << GShift) |
+                        (blue  << BShift);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                if (bpp == 32) {
+                    *dest++ = (char)((pixel >> 24) & 0xff);
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                } else {
+                    /* GRR BUG?  this assumes bpp == 24 & bits are packed low */
+                    /*           (probably need to use RShift, RMask, etc.) */
+                    *dest++ = (char)((pixel >> 16) & 0xff);
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        src = rpng2_info.row_pointers[row];
+        if (bg_image)
+            src2 = bg_data + row*bg_rowbytes;
+        dest = ximage->data + row*ximage_rowbytes;
+        if (rpng2_info.channels == 3) {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                red   = ((ush)(*src) << 8);
+                ++src;
+                green = ((ush)(*src) << 8);
+                ++src;
+                blue  = ((ush)(*src) << 8);
+                ++src;
+                pixel = ((red   >> RShift) & RMask) |
+                        ((green >> GShift) & GMask) |
+                        ((blue  >> BShift) & BMask);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                *dest++ = (char)((pixel >>  8) & 0xff);
+                *dest++ = (char)( pixel        & 0xff);
+            }
+        } else /* if (rpng2_info.channels == 4) */ {
+            for (i = rpng2_info.width;  i > 0;  --i) {
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                a = *src++;
+                if (bg_image) {
+                    bg_red   = *src2++;
+                    bg_green = *src2++;
+                    bg_blue  = *src2++;
+                }
+                if (a == 255) {
+                    red   = ((ush)r << 8);
+                    green = ((ush)g << 8);
+                    blue  = ((ush)b << 8);
+                } else if (a == 0) {
+                    red   = ((ush)bg_red   << 8);
+                    green = ((ush)bg_green << 8);
+                    blue  = ((ush)bg_blue  << 8);
+                } else {
+                    /* this macro (from png.h) composites the foreground
+                     * and background values and puts the result back into
+                     * the first argument (== fg byte here:  safe) */
+                    alpha_composite(r, r, a, bg_red);
+                    alpha_composite(g, g, a, bg_green);
+                    alpha_composite(b, b, a, bg_blue);
+                    red   = ((ush)r << 8);
+                    green = ((ush)g << 8);
+                    blue  = ((ush)b << 8);
+                }
+                pixel = ((red   >> RShift) & RMask) |
+                        ((green >> GShift) & GMask) |
+                        ((blue  >> BShift) & BMask);
+                /* recall that we set ximage->byte_order = MSBFirst above */
+                *dest++ = (char)((pixel >>  8) & 0xff);
+                *dest++ = (char)( pixel        & 0xff);
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+
+/*---------------------------------------------------------------------------
+    Display after every 16 rows or when on one of last two rows.  (Region
+    may include previously displayed lines due to interlacing--i.e., not
+    contiguous.  Also, second-to-last row is final one in interlaced images
+    with odd number of rows.)  For demos, flush (and delay) after every 16th
+    row so "sparse" passes don't go twice as fast.
+  ---------------------------------------------------------------------------*/
+
+    if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
+        XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
+          (int)firstrow, rpng2_info.width, row - firstrow + 1);
+        XFlush(display);
+        rows = 0;
+        usleep(usleep_duration);
+    } else
+    if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
+        XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
+          (int)firstrow, rpng2_info.width, row - firstrow + 1);
+        XFlush(display);
+        rows = 0;
+    }
+
+}
+
+
+
+
+
+static void rpng2_x_finish_display(void)
+{
+    Trace((stderr, "beginning rpng2_x_finish_display()\n"))
+
+    /* last row has already been displayed by rpng2_x_display_row(), so we
+     * have nothing to do here except set a flag and let the user know that
+     * the image is done */
+
+    rpng2_info.state = kDone;
+    printf(
+      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
+    fflush(stdout);
+}
+
+
+
+
+
+static void rpng2_x_redisplay_image(ulg startcol, ulg startrow,
+                                    ulg width, ulg height)
+{
+    uch bg_red   = rpng2_info.bg_red;
+    uch bg_green = rpng2_info.bg_green;
+    uch bg_blue  = rpng2_info.bg_blue;
+    uch *src, *src2=NULL;
+    char *dest;
+    uch r, g, b, a;
+    ulg i, row, lastrow = 0;
+    ulg pixel;
+    int ximage_rowbytes = ximage->bytes_per_line;
+
+
+    Trace((stderr, "beginning display loop (image_channels == %d)\n",
+      rpng2_info.channels))
+    Trace((stderr, "   (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n",
+      rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes))
+    Trace((stderr, "   (bpp = %d)\n", ximage->bits_per_pixel))
+    Trace((stderr, "   (byte_order = %s)\n", ximage->byte_order == MSBFirst?
+      "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
+
+/*---------------------------------------------------------------------------
+    Aside from the use of the rpng2_info struct and of src2 (for background
+    image), this routine is identical to rpng_x_display_image() in the non-
+    progressive version of the program--for the simple reason that redisplay
+    of the image against a new background happens after the image is fully
+    decoded and therefore is, by definition, non-progressive.
+  ---------------------------------------------------------------------------*/
+
+    if (depth == 24 || depth == 32) {
+        ulg red, green, blue;
+        int bpp = ximage->bits_per_pixel;
+
+        for (lastrow = row = startrow;  row < startrow+height;  ++row) {
+            src = rpng2_info.image_data + row*rpng2_info.rowbytes;
+            if (bg_image)
+                src2 = bg_data + row*bg_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            if (rpng2_info.channels == 3) {
+                for (i = rpng2_info.width;  i > 0;  --i) {
+                    red   = *src++;
+                    green = *src++;
+                    blue  = *src++;
+#ifdef NO_24BIT_MASKS
+                    pixel = (red   << RShift) |
+                            (green << GShift) |
+                            (blue  << BShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    if (bpp == 32) {
+                        *dest++ = (char)((pixel >> 24) & 0xff);
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    } else {
+                        /* this assumes bpp == 24 & bits are packed low */
+                        /* (probably need to use RShift, RMask, etc.) */
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    }
+#else
+                    red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
+                    green = (GShift < 0)? green << (-GShift) : green >> GShift;
+                    blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
+                    pixel = (red & RMask) | (green & GMask) | (blue & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    if (bpp == 32) {
+                        *dest++ = (char)((pixel >> 24) & 0xff);
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    } else {
+                        /* GRR BUG */
+                        /* this assumes bpp == 24 & bits are packed low */
+                        /* (probably need to use RShift/RMask/etc. here, too) */
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    }
+#endif
+                }
+
+            } else /* if (rpng2_info.channels == 4) */ {
+                for (i = rpng2_info.width;  i > 0;  --i) {
+                    r = *src++;
+                    g = *src++;
+                    b = *src++;
+                    a = *src++;
+                    if (bg_image) {
+                        bg_red   = *src2++;
+                        bg_green = *src2++;
+                        bg_blue  = *src2++;
+                    }
+                    if (a == 255) {
+                        red   = r;
+                        green = g;
+                        blue  = b;
+                    } else if (a == 0) {
+                        red   = bg_red;
+                        green = bg_green;
+                        blue  = bg_blue;
+                    } else {
+                        /* this macro (from png.h) composites the foreground
+                         * and background values and puts the result into the
+                         * first argument */
+                        alpha_composite(red,   r, a, bg_red);
+                        alpha_composite(green, g, a, bg_green);
+                        alpha_composite(blue,  b, a, bg_blue);
+                    }
+#ifdef NO_24BIT_MASKS
+                    pixel = (red   << RShift) |
+                            (green << GShift) |
+                            (blue  << BShift);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    if (bpp == 32) {
+                        *dest++ = (char)((pixel >> 24) & 0xff);
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    } else {
+                        /* this assumes bpp == 24 & bits are packed low */
+                        /* (probably need to use RShift, RMask, etc.) */
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    }
+#else
+                    red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
+                    green = (GShift < 0)? green << (-GShift) : green >> GShift;
+                    blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
+                    pixel = (red & RMask) | (green & GMask) | (blue & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    if (bpp == 32) {
+                        *dest++ = (char)((pixel >> 24) & 0xff);
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    } else {
+                        /* GRR BUG */
+                        /* this assumes bpp == 24 & bits are packed low */
+                        /* (probably need to use RShift/RMask/etc. here, too) */
+                        *dest++ = (char)((pixel >> 16) & 0xff);
+                        *dest++ = (char)((pixel >>  8) & 0xff);
+                        *dest++ = (char)( pixel        & 0xff);
+                    }
+#endif
+                }
+            }
+            /* display after every 16 lines */
+            if (((row+1) & 0xf) == 0) {
+                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
+                  (int)lastrow, rpng2_info.width, 16);
+                XFlush(display);
+                lastrow = row + 1;
+            }
+        }
+
+    } else if (depth == 16) {
+        ush red, green, blue;
+
+        for (lastrow = row = startrow;  row < startrow+height;  ++row) {
+            src = rpng2_info.row_pointers[row];
+            if (bg_image)
+                src2 = bg_data + row*bg_rowbytes;
+            dest = ximage->data + row*ximage_rowbytes;
+            if (rpng2_info.channels == 3) {
+                for (i = rpng2_info.width;  i > 0;  --i) {
+                    red   = ((ush)(*src) << 8);
+                    ++src;
+                    green = ((ush)(*src) << 8);
+                    ++src;
+                    blue  = ((ush)(*src) << 8);
+                    ++src;
+                    pixel = ((red   >> RShift) & RMask) |
+                            ((green >> GShift) & GMask) |
+                            ((blue  >> BShift) & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            } else /* if (rpng2_info.channels == 4) */ {
+                for (i = rpng2_info.width;  i > 0;  --i) {
+                    r = *src++;
+                    g = *src++;
+                    b = *src++;
+                    a = *src++;
+                    if (bg_image) {
+                        bg_red   = *src2++;
+                        bg_green = *src2++;
+                        bg_blue  = *src2++;
+                    }
+                    if (a == 255) {
+                        red   = ((ush)r << 8);
+                        green = ((ush)g << 8);
+                        blue  = ((ush)b << 8);
+                    } else if (a == 0) {
+                        red   = ((ush)bg_red   << 8);
+                        green = ((ush)bg_green << 8);
+                        blue  = ((ush)bg_blue  << 8);
+                    } else {
+                        /* this macro (from png.h) composites the foreground
+                         * and background values and puts the result back into
+                         * the first argument (== fg byte here:  safe) */
+                        alpha_composite(r, r, a, bg_red);
+                        alpha_composite(g, g, a, bg_green);
+                        alpha_composite(b, b, a, bg_blue);
+                        red   = ((ush)r << 8);
+                        green = ((ush)g << 8);
+                        blue  = ((ush)b << 8);
+                    }
+                    pixel = ((red   >> RShift) & RMask) |
+                            ((green >> GShift) & GMask) |
+                            ((blue  >> BShift) & BMask);
+                    /* recall that we set ximage->byte_order = MSBFirst above */
+                    *dest++ = (char)((pixel >>  8) & 0xff);
+                    *dest++ = (char)( pixel        & 0xff);
+                }
+            }
+            /* display after every 16 lines */
+            if (((row+1) & 0xf) == 0) {
+                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
+                  (int)lastrow, rpng2_info.width, 16);
+                XFlush(display);
+                lastrow = row + 1;
+            }
+        }
+
+    } else /* depth == 8 */ {
+
+        /* GRR:  add 8-bit support */
+
+    }
+
+    Trace((stderr, "calling final XPutImage()\n"))
+    if (lastrow < startrow+height) {
+        XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
+          (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow);
+        XFlush(display);
+    }
+
+    (void)startcol;
+    (void)width;
+
+} /* end function rpng2_x_redisplay_image() */
+
+
+
+
+
+#ifdef FEATURE_LOOP
+
+static void rpng2_x_reload_bg_image(void)
+{
+    char *dest;
+    uch r1, r2, g1, g2, b1, b2;
+    uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
+    int k, hmax, max;
+    int xidx, yidx, yidx_max;
+    int even_odd_vert, even_odd_horiz, even_odd;
+    int invert_gradient2 = (bg[pat].type & 0x08);
+    int invert_column;
+    ulg i, row;
+
+
+    bgscale = (pat == 0)? 8 : bgscale_default;
+    yidx_max = bgscale - 1;
+
+/*---------------------------------------------------------------------------
+    Vertical gradients (ramps) in NxN squares, alternating direction and
+    colors (N == bgscale).
+  ---------------------------------------------------------------------------*/
+
+    if ((bg[pat].type & 0x07) == 0) {
+        uch r1_min  = rgb[bg[pat].rgb1_min].r;
+        uch g1_min  = rgb[bg[pat].rgb1_min].g;
+        uch b1_min  = rgb[bg[pat].rgb1_min].b;
+        uch r2_min  = rgb[bg[pat].rgb2_min].r;
+        uch g2_min  = rgb[bg[pat].rgb2_min].g;
+        uch b2_min  = rgb[bg[pat].rgb2_min].b;
+        int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
+        int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
+        int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
+        int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
+        int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
+        int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = (int)(row % bgscale);
+            even_odd_vert = (int)((row / bgscale) & 1);
+
+            r1 = r1_min + (r1_diff * yidx) / yidx_max;
+            g1 = g1_min + (g1_diff * yidx) / yidx_max;
+            b1 = b1_min + (b1_diff * yidx) / yidx_max;
+            r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
+            g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
+            b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
+
+            r2 = r2_min + (r2_diff * yidx) / yidx_max;
+            g2 = g2_min + (g2_diff * yidx) / yidx_max;
+            b2 = b2_min + (b2_diff * yidx) / yidx_max;
+            r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
+            g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
+            b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
+
+            dest = (char *)bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                even_odd_horiz = (int)((i / bgscale) & 1);
+                even_odd = even_odd_vert ^ even_odd_horiz;
+                invert_column =
+                  (even_odd_horiz && (bg[pat].type & 0x10));
+                if (even_odd == 0) {        /* gradient #1 */
+                    if (invert_column) {
+                        *dest++ = r1_inv;
+                        *dest++ = g1_inv;
+                        *dest++ = b1_inv;
+                    } else {
+                        *dest++ = r1;
+                        *dest++ = g1;
+                        *dest++ = b1;
+                    }
+                } else {                    /* gradient #2 */
+                    if ((invert_column && invert_gradient2) ||
+                        (!invert_column && !invert_gradient2))
+                    {
+                        *dest++ = r2;       /* not inverted or */
+                        *dest++ = g2;       /*  doubly inverted */
+                        *dest++ = b2;
+                    } else {
+                        *dest++ = r2_inv;
+                        *dest++ = g2_inv;   /* singly inverted */
+                        *dest++ = b2_inv;
+                    }
+                }
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
+    M. Costello.
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 1) {
+
+        hmax = (bgscale-1)/2;   /* half the max weight of a color */
+        max = 2*hmax;           /* the max weight of a color */
+
+        r1 = rgb[bg[pat].rgb1_max].r;
+        g1 = rgb[bg[pat].rgb1_max].g;
+        b1 = rgb[bg[pat].rgb1_max].b;
+        r2 = rgb[bg[pat].rgb2_max].r;
+        g2 = rgb[bg[pat].rgb2_max].g;
+        b2 = rgb[bg[pat].rgb2_max].b;
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            yidx = (int)(row % bgscale);
+            if (yidx > hmax)
+                yidx = bgscale-1 - yidx;
+            dest = (char *)bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                xidx = (int)(i % bgscale);
+                if (xidx > hmax)
+                    xidx = bgscale-1 - xidx;
+                k = xidx + yidx;
+                *dest++ = (k*r1 + (max-k)*r2) / max;
+                *dest++ = (k*g1 + (max-k)*g2) / max;
+                *dest++ = (k*b1 + (max-k)*b2) / max;
+            }
+        }
+
+/*---------------------------------------------------------------------------
+    Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
+    soids will equal bgscale?].  This one is slow but very cool.  Code con-
+    tributed by Pieter S. van der Meulen (originally in Smalltalk).
+  ---------------------------------------------------------------------------*/
+
+    } else if ((bg[pat].type & 0x07) == 2) {
+        uch ch;
+        int ii, x, y, hw, hh, grayspot;
+        double freq, rotate, saturate, gray, intensity;
+        double angle=0.0, aoffset=0.0, maxDist, dist;
+        double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
+
+        hh = (int)(rpng2_info.height / 2);
+        hw = (int)(rpng2_info.width / 2);
+
+        /* variables for radial waves:
+         *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
+         *   freq:  number of color beams originating from the center
+         *   grayspot:  size of the graying center area (anti-alias)
+         *   rotate:  rotation of the beams as a function of radius
+         *   saturate:  saturation of beams' shape azimuthally
+         */
+        angle = CLIP(angle, 0.0, 360.0);
+        grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
+        freq = MAX((double)bg[pat].bg_freq, 0.0);
+        saturate = (double)bg[pat].bg_bsat * 0.1;
+        rotate = (double)bg[pat].bg_brot * 0.1;
+        gray = 0.0;
+        intensity = 0.0;
+        maxDist = (double)((hw*hw) + (hh*hh));
+
+        for (row = 0;  row < rpng2_info.height;  ++row) {
+            y = (int)(row - hh);
+            dest = (char *)bg_data + row*bg_rowbytes;
+            for (i = 0;  i < rpng2_info.width;  ++i) {
+                x = (int)(i - hw);
+                angle = (x == 0)? PI_2 : atan((double)y / (double)x);
+                gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
+                gray = MIN(1.0, gray);
+                dist = (double)((x*x) + (y*y)) / maxDist;
+                intensity = cos((angle+(rotate*dist*PI)) * freq) *
+                  gray * saturate;
+                intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
+                hue = (angle + PI) * INV_PI_360 + aoffset;
+                s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
+                s = MIN(MAX(s,0.0), 1.0);
+                v = MIN(MAX(intensity,0.0), 1.0);
+
+                if (s == 0.0) {
+                    ch = (uch)(v * 255.0);
+                    *dest++ = ch;
+                    *dest++ = ch;
+                    *dest++ = ch;
+                } else {
+                    if ((hue < 0.0) || (hue >= 360.0))
+                        hue -= (((int)(hue / 360.0)) * 360.0);
+                    hue /= 60.0;
+                    ii = (int)hue;
+                    f = hue - (double)ii;
+                    p = (1.0 - s) * v;
+                    q = (1.0 - (s * f)) * v;
+                    t = (1.0 - (s * (1.0 - f))) * v;
+                    if      (ii == 0) { red = v; green = t; blue = p; }
+                    else if (ii == 1) { red = q; green = v; blue = p; }
+                    else if (ii == 2) { red = p; green = v; blue = t; }
+                    else if (ii == 3) { red = p; green = q; blue = v; }
+                    else if (ii == 4) { red = t; green = p; blue = v; }
+                    else if (ii == 5) { red = v; green = p; blue = q; }
+                    *dest++ = (uch)(red * 255.0);
+                    *dest++ = (uch)(green * 255.0);
+                    *dest++ = (uch)(blue * 255.0);
+                }
+            }
+        }
+    }
+
+} /* end function rpng2_x_reload_bg_image() */
+
+
+
+
+
+static int is_number(char *p)
+{
+    while (*p) {
+        if (!isdigit(*p))
+            return FALSE;
+        ++p;
+    }
+    return TRUE;
+}
+
+#endif /* FEATURE_LOOP */
+
+
+
+
+
+static void rpng2_x_cleanup(void)
+{
+    if (bg_image && bg_data) {
+        free(bg_data);
+        bg_data = NULL;
+    }
+
+    if (rpng2_info.image_data) {
+        free(rpng2_info.image_data);
+        rpng2_info.image_data = NULL;
+    }
+
+    if (rpng2_info.row_pointers) {
+        free(rpng2_info.row_pointers);
+        rpng2_info.row_pointers = NULL;
+    }
+
+    if (ximage) {
+        if (ximage->data) {
+            free(ximage->data);           /* we allocated it, so we free it */
+            ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
+        }
+        XDestroyImage(ximage);
+        ximage = NULL;
+    }
+
+    if (have_gc)
+        XFreeGC(display, gc);
+
+    if (have_window)
+        XDestroyWindow(display, window);
+
+    if (have_colormap)
+        XFreeColormap(display, colormap);
+
+    if (have_nondefault_visual)
+        XFree(visual_list);
+}
+
+
+
+
+
+static int rpng2_x_msb(ulg u32val)
+{
+    int i;
+
+    for (i = 31;  i >= 0;  --i) {
+        if (u32val & 0x80000000L)
+            break;
+        u32val <<= 1;
+    }
+    return i;
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/toucan.png b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/toucan.png
new file mode 100755
index 0000000..03960d4
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/toucan.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/wpng.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/wpng.c
new file mode 100755
index 0000000..a8f367f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/wpng.c
@@ -0,0 +1,865 @@
+/*---------------------------------------------------------------------------
+
+   wpng - simple PNG-writing program                                 wpng.c
+
+   This program converts certain NetPBM binary files (grayscale and RGB,
+   maxval = 255) to PNG.  Non-interlaced PNGs are written progressively;
+   interlaced PNGs are read and written in one memory-intensive blast.
+
+   Thanks to Jean-loup Gailly for providing the necessary trick to read
+   interactive text from the keyboard while stdin is redirected.  Thanks
+   to Cosmin Truta for Cygwin fixes.
+
+   NOTE:  includes provisional support for PNM type "8" (portable alphamap)
+          images, presumed to be a 32-bit interleaved RGBA format; no pro-
+          vision for possible interleaved grayscale+alpha (16-bit) format.
+          THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!
+
+   to do:
+    - delete output file if quit before calling any writepng routines
+    - process backspace with -text option under DOS/Win? (currently get ^H)
+
+  ---------------------------------------------------------------------------
+
+   Changelog:
+    - 1.01:  initial public release
+    - 1.02:  modified to allow abbreviated options
+    - 1.03:  removed extraneous character from usage screen; fixed bug in
+              command-line parsing
+    - 1.04:  fixed DOS/OS2/Win32 detection, including partial Cygwin fix
+              (see http://home.att.net/~perlspinr/diffs/GregBook_cygwin.diff)
+    - 2.00:  dual-licensed (added GNU GPL)
+    - 2.01:  check for integer overflow (Glenn R-P)
+
+        [REPORTED BUG (win32 only):  "contrib/gregbook/wpng.c - cmd line
+         dose not work!  In order to do something useful I needed to redirect
+         both input and output, with cygwin and with bcc32 as well.  Under
+         Linux, the same wpng appears to work fine.  I don't know what is
+         the problem."]
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2007, 2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#define PROGNAME  "wpng"
+#define VERSION   "2.00 of 2 June 2007"
+#define APPNAME   "Simple PGM/PPM/PAM to PNG Converter"
+
+#if defined(__MSDOS__) || defined(__OS2__)
+#  define DOS_OS2_W32
+#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+#  ifndef __GNUC__   /* treat Win32 native ports of gcc as Unix environments */
+#    define DOS_OS2_W32
+#  endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>     /* for jmpbuf declaration in writepng.h */
+#include <time.h>
+
+#ifdef DOS_OS2_W32
+#  include <io.h>       /* for isatty(), setmode() prototypes */
+#  include <fcntl.h>    /* O_BINARY for fdopen() without text translation */
+#  ifdef __EMX__
+#    ifndef getch
+#      define getch() _read_kbd(0, 1, 0)    /* need getche() */
+#    endif
+#  else /* !__EMX__ */
+#    ifdef __GO32__
+#      include <pc.h>
+#      define getch() getkey()  /* GRR:  need getche() */
+#    else
+#      include <conio.h>        /* for getche() console input */
+#    endif
+#  endif /* ?__EMX__ */
+#  define FGETS(buf,len,stream)  dos_kbd_gets(buf,len)
+#else
+#  include <unistd.h>           /* for isatty() prototype */
+#  define FGETS fgets
+#endif
+
+/* #define DEBUG  :  this enables the Trace() macros */
+
+/* #define FORBID_LATIN1_CTRL  :  this requires the user to re-enter any
+   text that includes control characters discouraged by the PNG spec; text
+   that includes an escape character (27) must be re-entered regardless */
+
+#include "writepng.h"   /* typedefs, common macros, writepng prototypes */
+
+
+
+/* local prototypes */
+
+static int  wpng_isvalid_latin1(uch *p, int len);
+static void wpng_cleanup(void);
+
+#ifdef DOS_OS2_W32
+   static char *dos_kbd_gets(char *buf, int len);
+#endif
+
+
+
+static mainprog_info wpng_info;   /* lone global */
+
+
+
+int main(int argc, char **argv)
+{
+#ifndef DOS_OS2_W32
+    FILE *keybd;
+#endif
+#ifdef sgi
+    FILE *tmpfile;      /* or we could just use keybd, since no overlap */
+    char tmpline[80];
+#endif
+    char *inname = NULL, outname[256];
+    char *p, pnmchar, pnmline[256];
+    char *bgstr, *textbuf = NULL;
+    ulg rowbytes;
+    int rc, len = 0;
+    int error = 0;
+    int text = FALSE;
+    int maxval;
+    double LUT_exponent;                /* just the lookup table */
+    double CRT_exponent = 2.2;          /* just the monitor */
+    double default_display_exponent;    /* whole display system */
+    double default_gamma = 0.0;
+
+
+    wpng_info.infile = NULL;
+    wpng_info.outfile = NULL;
+    wpng_info.image_data = NULL;
+    wpng_info.row_pointers = NULL;
+    wpng_info.filter = FALSE;
+    wpng_info.interlaced = FALSE;
+    wpng_info.have_bg = FALSE;
+    wpng_info.have_time = FALSE;
+    wpng_info.have_text = 0;
+    wpng_info.gamma = 0.0;
+
+
+    /* First get the default value for our display-system exponent, i.e.,
+     * the product of the CRT exponent and the exponent corresponding to
+     * the frame-buffer's lookup table (LUT), if any.  If the PNM image
+     * looks correct on the user's display system, its file gamma is the
+     * inverse of this value.  (Note that this is not an exhaustive list
+     * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
+     * cover 99% of the current possibilities.  This section must ensure
+     * that default_display_exponent is positive.) */
+
+#if defined(NeXT)
+    /* third-party utilities can modify the default LUT exponent */
+    LUT_exponent = 1.0 / 2.2;
+    /*
+    if (some_next_function_that_returns_gamma(&next_gamma))
+        LUT_exponent = 1.0 / next_gamma;
+     */
+#elif defined(sgi)
+    LUT_exponent = 1.0 / 1.7;
+    /* there doesn't seem to be any documented function to
+     * get the "gamma" value, so we do it the hard way */
+    tmpfile = fopen("/etc/config/system.glGammaVal", "r");
+    if (tmpfile) {
+        double sgi_gamma;
+
+        fgets(tmpline, 80, tmpfile);
+        fclose(tmpfile);
+        sgi_gamma = atof(tmpline);
+        if (sgi_gamma > 0.0)
+            LUT_exponent = 1.0 / sgi_gamma;
+    }
+#elif defined(Macintosh)
+    LUT_exponent = 1.8 / 2.61;
+    /*
+    if (some_mac_function_that_returns_gamma(&mac_gamma))
+        LUT_exponent = mac_gamma / 2.61;
+     */
+#else
+    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
+#endif
+
+    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
+    default_display_exponent = LUT_exponent * CRT_exponent;
+
+
+    /* If the user has set the SCREEN_GAMMA environment variable as suggested
+     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
+     * use the default value we just calculated.  Either way, the user may
+     * override this via a command-line option. */
+
+    if ((p = getenv("SCREEN_GAMMA")) != NULL) {
+        double exponent = atof(p);
+
+        if (exponent > 0.0)
+            default_gamma = 1.0 / exponent;
+    }
+
+    if (default_gamma == 0.0)
+        default_gamma = 1.0 / default_display_exponent;
+
+
+    /* Now parse the command line for options and the PNM filename. */
+
+    while (*++argv && !error) {
+        if (!strncmp(*argv, "-i", 2)) {
+            wpng_info.interlaced = TRUE;
+        } else if (!strncmp(*argv, "-time", 3)) {
+            wpng_info.modtime = time(NULL);
+            wpng_info.have_time = TRUE;
+        } else if (!strncmp(*argv, "-text", 3)) {
+            text = TRUE;
+        } else if (!strncmp(*argv, "-gamma", 2)) {
+            if (!*++argv)
+                ++error;
+            else {
+                wpng_info.gamma = atof(*argv);
+                if (wpng_info.gamma <= 0.0)
+                    ++error;
+                else if (wpng_info.gamma > 1.01)
+                    fprintf(stderr, PROGNAME
+                      " warning:  file gammas are usually less than 1.0\n");
+            }
+        } else if (!strncmp(*argv, "-bgcolor", 4)) {
+            if (!*++argv)
+                ++error;
+            else {
+                bgstr = *argv;
+                if (strlen(bgstr) != 7 || bgstr[0] != '#')
+                    ++error;
+                else {
+                    unsigned r, g, b;  /* this way quiets compiler warnings */
+
+                    sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
+                    wpng_info.bg_red   = (uch)r;
+                    wpng_info.bg_green = (uch)g;
+                    wpng_info.bg_blue  = (uch)b;
+                    wpng_info.have_bg = TRUE;
+                }
+            }
+        } else {
+            if (**argv != '-') {
+                inname = *argv;
+                if (argv[1])   /* shouldn't be any more args after filename */
+                    ++error;
+            } else
+                ++error;   /* not expecting any other options */
+        }
+    }
+
+
+    /* open the input and output files, or register an error and abort */
+
+    if (!inname) {
+        if (isatty(0)) {
+            fprintf(stderr, PROGNAME
+              ":  must give input filename or provide image data via stdin\n");
+            ++error;
+        } else {
+#ifdef DOS_OS2_W32
+            /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
+            setmode(fileno(stdin), O_BINARY);
+            setmode(fileno(stdout), O_BINARY);
+#endif
+            if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
+                fprintf(stderr, PROGNAME
+                  ":  unable to reopen stdin in binary mode\n");
+                ++error;
+            } else
+            if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
+                fprintf(stderr, PROGNAME
+                  ":  unable to reopen stdout in binary mode\n");
+                fclose(wpng_info.infile);
+                ++error;
+            } else
+                wpng_info.filter = TRUE;
+        }
+    } else if ((len = strlen(inname)) > 250) {
+        fprintf(stderr, PROGNAME ":  input filename is too long [%d chars]\n",
+          len);
+        ++error;
+    } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
+        fprintf(stderr, PROGNAME ":  can't open input file [%s]\n", inname);
+        ++error;
+    }
+
+    if (!error) {
+        fgets(pnmline, 256, wpng_info.infile);
+        if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
+            pnmchar != '6' && pnmchar != '8'))
+        {
+            fprintf(stderr, PROGNAME
+              ":  input file [%s] is not a binary PGM, PPM or PAM file\n",
+              inname);
+            ++error;
+        } else {
+            wpng_info.pnmtype = (int)(pnmchar - '0');
+            if (wpng_info.pnmtype != 8)
+                wpng_info.have_bg = FALSE;  /* no need for bg if opaque */
+            do {
+                fgets(pnmline, 256, wpng_info.infile);  /* lose any comments */
+            } while (pnmline[0] == '#');
+            sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
+            do {
+                fgets(pnmline, 256, wpng_info.infile);  /* more comment lines */
+            } while (pnmline[0] == '#');
+            sscanf(pnmline, "%d", &maxval);
+            if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
+                maxval != 255)
+            {
+                fprintf(stderr, PROGNAME
+                  ":  only positive width/height, maxval == 255 allowed \n");
+                ++error;
+            }
+            wpng_info.sample_depth = 8;  /* <==> maxval 255 */
+
+            if (!wpng_info.filter) {
+                /* make outname from inname */
+                if ((p = strrchr(inname, '.')) == NULL ||
+                    (p - inname) != (len - 4))
+                {
+                    strcpy(outname, inname);
+                    strcpy(outname+len, ".png");
+                } else {
+                    len -= 4;
+                    strncpy(outname, inname, len);
+                    strcpy(outname+len, ".png");
+                }
+                /* check if outname already exists; if not, open */
+                if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
+                    fprintf(stderr, PROGNAME ":  output file exists [%s]\n",
+                      outname);
+                    fclose(wpng_info.outfile);
+                    ++error;
+                } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
+                    fprintf(stderr, PROGNAME ":  can't open output file [%s]\n",
+                      outname);
+                    ++error;
+                }
+            }
+        }
+        if (error) {
+            fclose(wpng_info.infile);
+            wpng_info.infile = NULL;
+            if (wpng_info.filter) {
+                fclose(wpng_info.outfile);
+                wpng_info.outfile = NULL;
+            }
+        }
+    }
+
+
+    /* if we had any errors, print usage and die horrible death...arrr! */
+
+    if (error) {
+        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, APPNAME);
+        writepng_version_info();
+        fprintf(stderr, "\n"
+"Usage:  %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
+"or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
+         "    exp \ttransfer-function exponent (``gamma'') of the image in\n"
+         "\t\t  floating-point format (e.g., ``%.5f''); if image looks\n"
+         "\t\t  correct on given display system, image gamma is equal to\n"
+         "\t\t  inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
+         "\t\t  (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
+         "\t\t  first varies, second is usually 2.2, all are positive)\n"
+         "    bg  \tdesired background color for alpha-channel images, in\n"
+         "\t\t  7-character hex RGB format (e.g., ``#ff7700'' for orange:\n"
+         "\t\t  same as HTML colors)\n"
+         "    -text\tprompt interactively for text info (tEXt chunks)\n"
+         "    -time\tinclude a tIME chunk (last modification time)\n"
+         "    -interlace\twrite interlaced PNG image\n"
+         "\n"
+"pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
+"unofficial and unsupported!) PAM (`P8') file.  Currently it is required\n"
+"to have maxval == 255 (i.e., no scaling).  If pnmfile is specified, it\n"
+"is converted to the corresponding PNG file with the same base name but a\n"
+"``.png'' extension; files read from stdin are converted and sent to stdout.\n"
+"The conversion is progressive (low memory usage) unless interlacing is\n"
+"requested; in that case the whole image will be buffered in memory and\n"
+"written in one call.\n"
+         "\n", PROGNAME, PROGNAME, default_gamma);
+        exit(1);
+    }
+
+
+    /* prepare the text buffers for libpng's use; note that even though
+     * PNG's png_text struct includes a length field, we don't have to fill
+     * it out */
+
+    if (text &&
+#ifndef DOS_OS2_W32
+        (keybd = fdopen(fileno(stderr), "r")) != NULL &&
+#endif
+        (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
+    {
+        int i, valid, result;
+
+        fprintf(stderr,
+          "Enter text info (no more than 72 characters per line);\n");
+        fprintf(stderr, "to skip a field, hit the <Enter> key.\n");
+        /* note:  just <Enter> leaves len == 1 */
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_TITLE_OFFSET;
+            fprintf(stderr, "  Title: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.title = p;
+                wpng_info.have_text |= TEXT_TITLE;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_TITLE;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {    /* escape character */
+                        wpng_info.have_text &= ~TEXT_TITLE;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_AUTHOR_OFFSET;
+            fprintf(stderr, "  Author: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.author = p;
+                wpng_info.have_text |= TEXT_AUTHOR;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_AUTHOR;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {    /* escape character */
+                        wpng_info.have_text &= ~TEXT_AUTHOR;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_DESC_OFFSET;
+            fprintf(stderr, "  Description (up to 9 lines):\n");
+            for (i = 1;  i < 10;  ++i) {
+                fprintf(stderr, "    [%d] ", i);
+                fflush(stderr);
+                if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
+                    p += len;   /* now points at NULL; char before is newline */
+                else
+                    break;
+            }
+            if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
+                if (p[-1] == '\n') {
+                    p[-1] = '\0';
+                    --len;
+                }
+                wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
+                wpng_info.have_text |= TEXT_DESC;
+                p = textbuf + TEXT_DESC_OFFSET;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_DESC;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {    /* escape character */
+                        wpng_info.have_text &= ~TEXT_DESC;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_COPY_OFFSET;
+            fprintf(stderr, "  Copyright: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.copyright = p;
+                wpng_info.have_text |= TEXT_COPY;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_COPY;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {    /* escape character */
+                        wpng_info.have_text &= ~TEXT_COPY;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_EMAIL_OFFSET;
+            fprintf(stderr, "  E-mail: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.email = p;
+                wpng_info.have_text |= TEXT_EMAIL;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_EMAIL;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {    /* escape character */
+                        wpng_info.have_text &= ~TEXT_EMAIL;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+        do {
+            valid = TRUE;
+            p = textbuf + TEXT_URL_OFFSET;
+            fprintf(stderr, "  URL: ");
+            fflush(stderr);
+            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
+                if (p[len-1] == '\n')
+                    p[--len] = '\0';
+                wpng_info.url = p;
+                wpng_info.have_text |= TEXT_URL;
+                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
+                    fprintf(stderr, "    " PROGNAME " warning:  character code"
+                      " %u is %sdiscouraged by the PNG\n    specification "
+                      "[first occurrence was at character position #%d]\n",
+                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
+                      result+1);
+                    fflush(stderr);
+#ifdef FORBID_LATIN1_CTRL
+                    wpng_info.have_text &= ~TEXT_URL;
+                    valid = FALSE;
+#else
+                    if (p[result] == 27) {    /* escape character */
+                        wpng_info.have_text &= ~TEXT_URL;
+                        valid = FALSE;
+                    }
+#endif
+                }
+            }
+        } while (!valid);
+
+#ifndef DOS_OS2_W32
+        fclose(keybd);
+#endif
+
+    } else if (text) {
+        fprintf(stderr, PROGNAME ":  unable to allocate memory for text\n");
+        text = FALSE;
+        wpng_info.have_text = 0;
+    }
+
+
+    /* allocate libpng stuff, initialize transformations, write pre-IDAT data */
+
+    if ((rc = writepng_init(&wpng_info)) != 0) {
+        switch (rc) {
+            case 2:
+                fprintf(stderr, PROGNAME
+                  ":  libpng initialization problem (longjmp)\n");
+                break;
+            case 4:
+                fprintf(stderr, PROGNAME ":  insufficient memory\n");
+                break;
+            case 11:
+                fprintf(stderr, PROGNAME
+                  ":  internal logic error (unexpected PNM type)\n");
+                break;
+            default:
+                fprintf(stderr, PROGNAME
+                  ":  unknown writepng_init() error\n");
+                break;
+        }
+        exit(rc);
+    }
+
+
+    /* free textbuf, since it's a completely local variable and all text info
+     * has just been written to the PNG file */
+
+    if (text && textbuf) {
+        free(textbuf);
+        textbuf = NULL;
+    }
+
+
+    /* calculate rowbytes on basis of image type; note that this becomes much
+     * more complicated if we choose to support PBM type, ASCII PNM types, or
+     * 16-bit-per-sample binary data [currently not an official NetPBM type] */
+
+    if (wpng_info.pnmtype == 5)
+        rowbytes = wpng_info.width;
+    else if (wpng_info.pnmtype == 6)
+        rowbytes = wpng_info.width * 3;
+    else /* if (wpng_info.pnmtype == 8) */
+        rowbytes = wpng_info.width * 4;
+
+
+    /* read and write the image, either in its entirety (if writing interlaced
+     * PNG) or row by row (if non-interlaced) */
+
+    fprintf(stderr, "Encoding image data...\n");
+    fflush(stderr);
+
+    if (wpng_info.interlaced) {
+        long i;
+        ulg bytes;
+        ulg image_bytes;
+
+        /* Guard against integer overflow */
+        if (wpng_info_height > ((size_t)(-1)/rowbytes ||
+            wpng_info_height > ((ulg)(-1)/rowbytes) {
+            fprintf(stderr, PROGNAME ":  image_data buffer too large\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(5);
+        }
+
+        image_bytes = rowbytes * wpng_info.height;
+
+        wpng_info.image_data = (uch *)malloc(image_bytes);
+        wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
+        if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
+            fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(5);
+        }
+        for (i = 0;  i < wpng_info.height;  ++i)
+            wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
+        bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
+        if (bytes != image_bytes) {
+            fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
+              image_bytes, bytes);
+            fprintf(stderr, "  (continuing anyway)\n");
+        }
+        if (writepng_encode_image(&wpng_info) != 0) {
+            fprintf(stderr, PROGNAME
+              ":  libpng problem (longjmp) while writing image data\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(2);
+        }
+
+    } else /* not interlaced:  write progressively (row by row) */ {
+        long j;
+        ulg bytes;
+
+        wpng_info.image_data = (uch *)malloc(rowbytes);
+        if (wpng_info.image_data == NULL) {
+            fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(5);
+        }
+        error = 0;
+        for (j = wpng_info.height;  j > 0L;  --j) {
+            bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
+            if (bytes != rowbytes) {
+                fprintf(stderr, PROGNAME
+                  ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
+                  bytes, wpng_info.height-j);
+                ++error;
+                break;
+            }
+            if (writepng_encode_row(&wpng_info) != 0) {
+                fprintf(stderr, PROGNAME
+                  ":  libpng problem (longjmp) while writing row %ld\n",
+                  wpng_info.height-j);
+                ++error;
+                break;
+            }
+        }
+        if (error) {
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(2);
+        }
+        if (writepng_encode_finish(&wpng_info) != 0) {
+            fprintf(stderr, PROGNAME ":  error on final libpng call\n");
+            writepng_cleanup(&wpng_info);
+            wpng_cleanup();
+            exit(2);
+        }
+    }
+
+
+    /* OK, we're done (successfully):  clean up all resources and quit */
+
+    fprintf(stderr, "Done.\n");
+    fflush(stderr);
+
+    writepng_cleanup(&wpng_info);
+    wpng_cleanup();
+
+    return 0;
+}
+
+
+
+
+
+static int wpng_isvalid_latin1(uch *p, int len)
+{
+    int i, result = -1;
+
+    for (i = 0;  i < len;  ++i) {
+        if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
+            continue;           /* character is completely OK */
+        if (result < 0 || (p[result] != 27 && p[i] == 27))
+            result = i;         /* mark location of first questionable one */
+    }                           /*  or of first escape character (bad) */
+
+    return result;
+}
+
+
+
+
+
+static void wpng_cleanup(void)
+{
+    if (wpng_info.outfile) {
+        fclose(wpng_info.outfile);
+        wpng_info.outfile = NULL;
+    }
+
+    if (wpng_info.infile) {
+        fclose(wpng_info.infile);
+        wpng_info.infile = NULL;
+    }
+
+    if (wpng_info.image_data) {
+        free(wpng_info.image_data);
+        wpng_info.image_data = NULL;
+    }
+
+    if (wpng_info.row_pointers) {
+        free(wpng_info.row_pointers);
+        wpng_info.row_pointers = NULL;
+    }
+}
+
+
+
+
+#ifdef DOS_OS2_W32
+
+static char *dos_kbd_gets(char *buf, int len)
+{
+    int ch, count=0;
+
+    do {
+        buf[count++] = ch = getche();
+    } while (ch != '\r' && count < len-1);
+
+    buf[count--] = '\0';        /* terminate string */
+    if (buf[count] == '\r')     /* Enter key makes CR, so change to newline */
+        buf[count] = '\n';
+
+    fprintf(stderr, "\n");      /* Enter key does *not* cause a newline */
+    fflush(stderr);
+
+    return buf;
+}
+
+#endif /* DOS_OS2_W32 */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/writepng.c b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/writepng.c
new file mode 100755
index 0000000..055c743
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/writepng.c
@@ -0,0 +1,401 @@
+/*---------------------------------------------------------------------------
+
+   wpng - simple PNG-writing program                             writepng.c
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2007, 2017 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+
+#include <stdlib.h>     /* for exit() prototype */
+#include <zlib.h>
+
+#include "png.h"        /* libpng header, includes setjmp.h */
+#include "writepng.h"   /* typedefs, common macros, public prototypes */
+
+
+/* local prototype */
+
+static void writepng_error_handler(png_structp png_ptr, png_const_charp msg);
+
+
+
+void writepng_version_info(void)
+{
+  fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
+    PNG_LIBPNG_VER_STRING, png_libpng_ver);
+  fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+    ZLIB_VERSION, zlib_version);
+}
+
+
+
+
+/* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for
+ *  unexpected pnmtype; note that outfile might be stdout */
+
+int writepng_init(mainprog_info *mainprog_ptr)
+{
+    png_structp  png_ptr;       /* note:  temporary variables! */
+    png_infop  info_ptr;
+    int color_type, interlace_type;
+
+
+    /* could also replace libpng warning-handler (final NULL), but no need: */
+
+    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 */
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_write_struct(&png_ptr, NULL);
+        return 4;   /* out of memory */
+    }
+
+
+    /* 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 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);
+        return 2;
+    }
+
+
+    /* make sure outfile is (re)opened in BINARY mode */
+
+    png_init_io(png_ptr, mainprog_ptr->outfile);
+
+
+    /* set the compression levels--in general, always want to leave filtering
+     * turned on (except for palette images) and allow all of the filters,
+     * which is the default; want 32K zlib window, unless entire image buffer
+     * is 16K or smaller (unknown here)--also the default; usually want max
+     * compression (NOT the default); and remaining compression flags should
+     * be left alone */
+
+    png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
+/*
+    >> this is default for no filtering; Z_FILTERED is default otherwise:
+    png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
+    >> these are all defaults:
+    png_set_compression_mem_level(png_ptr, 8);
+    png_set_compression_window_bits(png_ptr, 15);
+    png_set_compression_method(png_ptr, 8);
+ */
+
+
+    /* set the image parameters appropriately */
+
+    if (mainprog_ptr->pnmtype == 5)
+        color_type = PNG_COLOR_TYPE_GRAY;
+    else if (mainprog_ptr->pnmtype == 6)
+        color_type = PNG_COLOR_TYPE_RGB;
+    else if (mainprog_ptr->pnmtype == 8)
+        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+    else {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return 11;
+    }
+
+    interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 :
+                                               PNG_INTERLACE_NONE;
+
+    png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
+      mainprog_ptr->sample_depth, color_type, interlace_type,
+      PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    if (mainprog_ptr->gamma > 0.0)
+        png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);
+
+    if (mainprog_ptr->have_bg) {   /* we know it's RGBA, not gray+alpha */
+        png_color_16  background;
+
+        background.red = mainprog_ptr->bg_red;
+        background.green = mainprog_ptr->bg_green;
+        background.blue = mainprog_ptr->bg_blue;
+        png_set_bKGD(png_ptr, info_ptr, &background);
+    }
+
+    if (mainprog_ptr->have_time) {
+        png_time  modtime;
+
+        png_convert_from_time_t(&modtime, mainprog_ptr->modtime);
+        png_set_tIME(png_ptr, info_ptr, &modtime);
+    }
+
+    if (mainprog_ptr->have_text) {
+        png_text  text[6];
+        int  num_text = 0;
+
+        if (mainprog_ptr->have_text & TEXT_TITLE) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Title";
+            text[num_text].text = mainprog_ptr->title;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_AUTHOR) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Author";
+            text[num_text].text = mainprog_ptr->author;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_DESC) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Description";
+            text[num_text].text = mainprog_ptr->desc;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_COPY) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "Copyright";
+            text[num_text].text = mainprog_ptr->copyright;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_EMAIL) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "E-mail";
+            text[num_text].text = mainprog_ptr->email;
+            ++num_text;
+        }
+        if (mainprog_ptr->have_text & TEXT_URL) {
+            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
+            text[num_text].key = "URL";
+            text[num_text].text = mainprog_ptr->url;
+            ++num_text;
+        }
+        png_set_text(png_ptr, info_ptr, text, num_text);
+    }
+
+
+    /* write all chunks up to (but not including) first IDAT */
+
+    png_write_info(png_ptr, info_ptr);
+
+
+    /* if we wanted to write any more text info *after* the image data, we
+     * would set up text struct(s) here and call png_set_text() again, with
+     * just the new data; png_set_tIME() could also go here, but it would
+     * have no effect since we already called it above (only one tIME chunk
+     * allowed) */
+
+
+    /* set up the transformations:  for now, just pack low-bit-depth pixels
+     * into bytes (one, two or four pixels per byte) */
+
+    png_set_packing(png_ptr);
+/*  png_set_shift(png_ptr, &sig_bit);  to scale low-bit-depth values */
+
+
+    /* make sure we save our pointers for use in writepng_encode_image() */
+
+    mainprog_ptr->png_ptr = png_ptr;
+    mainprog_ptr->info_ptr = info_ptr;
+
+
+    /* OK, that's all we need to do for now; return happy */
+
+    return 0;
+}
+
+
+
+
+
+/* returns 0 for success, 2 for libpng (longjmp) problem */
+
+int writepng_encode_image(mainprog_info *mainprog_ptr)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* as always, setjmp() must be called in every function that calls a
+     * PNG-writing libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* and now we just write the whole image; libpng takes care of interlacing
+     * for us */
+
+    png_write_image(png_ptr, mainprog_ptr->row_pointers);
+
+
+    /* since that's it, we also close out the end of the PNG file now--if we
+     * had any text or time info to write after the IDATs, second argument
+     * would be info_ptr, but we optimize slightly by sending NULL pointer: */
+
+    png_write_end(png_ptr, NULL);
+
+    return 0;
+}
+
+
+
+
+
+/* returns 0 if succeeds, 2 if libpng problem */
+
+int writepng_encode_row(mainprog_info *mainprog_ptr)  /* NON-interlaced only! */
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* as always, setjmp() must be called in every function that calls a
+     * PNG-writing libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* image_data points at our one row of image data */
+
+    png_write_row(png_ptr, mainprog_ptr->image_data);
+
+    return 0;
+}
+
+
+
+
+
+/* returns 0 if succeeds, 2 if libpng problem */
+
+int writepng_encode_finish(mainprog_info *mainprog_ptr)   /* NON-interlaced! */
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+
+    /* as always, setjmp() must be called in every function that calls a
+     * PNG-writing libpng function */
+
+    if (setjmp(mainprog_ptr->jmpbuf)) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        mainprog_ptr->png_ptr = NULL;
+        mainprog_ptr->info_ptr = NULL;
+        return 2;
+    }
+
+
+    /* close out PNG file; if we had any text or time info to write after
+     * the IDATs, second argument would be info_ptr: */
+
+    png_write_end(png_ptr, NULL);
+
+    return 0;
+}
+
+
+
+
+
+void writepng_cleanup(mainprog_info *mainprog_ptr)
+{
+    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
+    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
+
+    if (png_ptr && info_ptr)
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+}
+
+
+
+
+
+static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
+{
+    mainprog_info  *mainprog_ptr;
+
+    /* This function, aside from the extra step of retrieving the "error
+     * pointer" (below) and the fact that it exists within the application
+     * rather than within libpng, is essentially identical to libpng's
+     * default error handler.  The second point is critical:  since both
+     * setjmp() and longjmp() are called from the same code, they are
+     * guaranteed to have compatible notions of how big a jmp_buf is,
+     * regardless of whether _BSD_SOURCE or anything else has (or has not)
+     * been defined. */
+
+    fprintf(stderr, "writepng libpng error: %s\n", msg);
+    fflush(stderr);
+
+    mainprog_ptr = png_get_error_ptr(png_ptr);
+    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
+        fprintf(stderr,
+          "writepng severe error:  jmpbuf not recoverable; terminating.\n");
+        fflush(stderr);
+        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/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/writepng.h b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/writepng.h
new file mode 100755
index 0000000..78b966b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/gregbook/writepng.h
@@ -0,0 +1,133 @@
+/*---------------------------------------------------------------------------
+
+   wpng - simple PNG-writing program                             writepng.h
+
+  ---------------------------------------------------------------------------
+
+      Copyright (c) 1998-2007 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
+      be held liable for any damages arising in any way from the use of
+      this software.
+
+      The contents of this file are DUAL-LICENSED.  You may modify and/or
+      redistribute this software according to the terms of one of the
+      following two licenses (at your option):
+
+
+      LICENSE 1 ("BSD-like with advertising clause"):
+
+      Permission is granted to anyone to use this software for any purpose,
+      including commercial applications, and to alter it and redistribute
+      it freely, subject to the following restrictions:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, disclaimer, and this list of conditions.
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, disclaimer, and this list of conditions in the documenta-
+         tion and/or other materials provided with the distribution.
+      3. All advertising materials mentioning features or use of this
+         software must display the following acknowledgment:
+
+            This product includes software developed by Greg Roelofs
+            and contributors for the book, "PNG: The Definitive Guide,"
+            published by O'Reilly and Associates.
+
+
+      LICENSE 2 (GNU GPL v2 or later):
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of the GNU General Public License as published by
+      the Free Software Foundation; either version 2 of the License, or
+      (at your option) any later version.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software Foundation,
+      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  ---------------------------------------------------------------------------*/
+
+#ifndef TRUE
+#  define TRUE 1
+#  define FALSE 0
+#endif
+
+#ifndef MAX
+#  define MAX(a,b)  ((a) > (b)? (a) : (b))
+#  define MIN(a,b)  ((a) < (b)? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  define Trace(x)  {fprintf x ; fflush(stderr); fflush(stdout);}
+#else
+#  define Trace(x)  ;
+#endif
+
+#define TEXT_TITLE    0x01
+#define TEXT_AUTHOR   0x02
+#define TEXT_DESC     0x04
+#define TEXT_COPY     0x08
+#define TEXT_EMAIL    0x10
+#define TEXT_URL      0x20
+
+#define TEXT_TITLE_OFFSET        0
+#define TEXT_AUTHOR_OFFSET      72
+#define TEXT_COPY_OFFSET     (2*72)
+#define TEXT_EMAIL_OFFSET    (3*72)
+#define TEXT_URL_OFFSET      (4*72)
+#define TEXT_DESC_OFFSET     (5*72)
+
+typedef unsigned char   uch;
+typedef unsigned short  ush;
+typedef unsigned long   ulg;
+
+typedef struct _mainprog_info {
+    double gamma;
+    long width;
+    long height;
+    time_t modtime;
+    FILE *infile;
+    FILE *outfile;
+    void *png_ptr;
+    void *info_ptr;
+    uch *image_data;
+    uch **row_pointers;
+    char *title;
+    char *author;
+    char *desc;
+    char *copyright;
+    char *email;
+    char *url;
+    int filter;    /* command-line-filter flag, not PNG row filter! */
+    int pnmtype;
+    int sample_depth;
+    int interlaced;
+    int have_bg;
+    int have_time;
+    int have_text;
+    jmp_buf jmpbuf;
+    uch bg_red;
+    uch bg_green;
+    uch bg_blue;
+} mainprog_info;
+
+
+/* prototypes for public functions in writepng.c */
+
+void writepng_version_info(void);
+
+int writepng_init(mainprog_info *mainprog_ptr);
+
+int writepng_encode_image(mainprog_info *mainprog_ptr);
+
+int writepng_encode_row(mainprog_info *mainprog_ptr);
+
+int writepng_encode_finish(mainprog_info *mainprog_ptr);
+
+void writepng_cleanup(mainprog_info *mainprog_ptr);
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/libtests/fakepng.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/fakepng.c
new file mode 100755
index 0000000..6512c14
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/gentests.sh b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/gentests.sh
new file mode 100755
index 0000000..f0f8d23
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/makepng.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/makepng.c
new file mode 100755
index 0000000..312062b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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.6.20 [November 24, 2015]
+ *
+ * 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:
+            {
+               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:
+            {
+               png_uint_32 luma = colors[1];
+               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:
+            {
+               png_uint_32 red = colors[1];
+               png_uint_32 green = colors[2];
+               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:
+            {
+               png_uint_32 red = colors[1];
+               png_uint_32 green = colors[2];
+               png_uint_32 blue = colors[3];
+               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");
+
+   {
+      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)
+      {
+         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;
+         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 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 '<':
+         {
+            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':
+         {
+            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;
+            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)
+{
+   int ct = png_get_color_type(png_ptr, info_ptr);
+   int c = (ct & PNG_COLOR_MASK_COLOR ? 3 : 1) +
+      (ct & PNG_COLOR_MASK_ALPHA ? 1 : 0);
+   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 */
+   {
+      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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngimage.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngimage.c
new file mode 100755
index 0000000..f130c04
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngimage.c
@@ -0,0 +1,1712 @@
+/* pngimage.c
+ *
+ * Copyright (c) 2015,2016 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.24 [August 4, 2016]
+ *
+ * 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
+
+/* 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 && defined(HAVE_CONFIG_H)
+#  define SKIP 77
+#else
+#  define SKIP 0
+#endif
+
+#if PNG_LIBPNG_VER < 10700
+   /* READ_PNG and WRITE_PNG were not defined, so: */
+#  ifdef PNG_INFO_IMAGE_SUPPORTED
+#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#        define PNG_READ_PNG_SUPPORTED
+#     endif /* SEQUENTIAL_READ */
+#     ifdef PNG_WRITE_SUPPORTED
+#        define PNG_WRITE_PNG_SUPPORTED
+#     endif /* WRITE */
+#  endif /* INFO_IMAGE */
+#endif /* pre 1.7.0 */
+
+#ifdef PNG_READ_PNG_SUPPORTED
+/* 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_PNG_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 */
+
+   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_PNG_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_PNG_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_PNG_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_PNG_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_PNG_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,
+   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, 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 transform. */
+   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
+#     ifdef PNG_sBIT_SUPPORTED
+      {
+         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 */
+      }
+#     else /* !sBIT */
+         display_log(dp, INTERNAL_ERROR,
+               "active shift transform but no sBIT support");
+#     endif /* !sBIT */
+   }
+
+   return 1; /* compare succeeded */
+}
+
+#ifdef PNG_WRITE_PNG_SUPPORTED
+static void
+buffer_write(struct display *dp, struct buffer *buffer, png_bytep data,
+   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, 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_PNG */
+
+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_PNG_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;
+      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_PNG_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(int argc, char **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
+          */
+         {
+            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 /* !READ_PNG */
+int
+main(void)
+{
+   fprintf(stderr, "pngimage: no support for png_read/write_image\n");
+   return SKIP;
+}
+#endif
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngstest-errors.h b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngstest-errors.h
new file mode 100755
index 0000000..3356f1c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngstest.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngstest.c
new file mode 100755
index 0000000..a368bf0
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngstest.c
@@ -0,0 +1,3828 @@
+/*-
+ * pngstest.c
+ *
+ * Last changed in libpng 1.6.31 [July 27, 2017]
+ * Copyright (c) 2013-2017 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
+ *
+ * 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
+
+/* 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 && defined(HAVE_CONFIG_H)
+#  define SKIP 77
+#else
+#  define SKIP 0
+#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 png_uint_32 color_seed[2];
+
+static void
+reseed(void)
+{
+   color_seed[0] = 0x12345678U;
+   color_seed[1] = 0x9abcdefU;
+}
+
+static void
+random_color(png_colorp color)
+{
+   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 USE_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
+#define NO_RESEED  512   /* do not reseed on each new file */
+#define GBG_ERROR 1024   /* do not ignore the gamma+background_rgb_to_gray
+                          * libpng warning. */
+
+static void
+print_opts(png_uint_32 opts)
+{
+   if (opts & USE_FILE)
+      printf(" --file");
+   if (opts & USE_STDIO)
+      printf(" --stdio");
+   if (!(opts & STRICT))
+      printf(" --nostrict");
+   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");
+   if (opts & NO_RESEED)
+      printf(" --noreseed");
+#if PNG_LIBPNG_VER < 10700 /* else on by default */
+   if (opts & GBG_ERROR)
+      printf(" --fault-gbg-warning");
+#endif
+}
+
+#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 const char * 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;
+   size_t      input_memory_size;
+   png_bytep   buffer;
+   ptrdiff_t   stride;
+   size_t      bufsize;
+   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)
+{
+   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", "");
+   }
+
+   /* Separate out the gamma+background_rgb_to_gray warning because it may
+    * produce opaque component errors:
+    */
+   else if (image->image.warning_or_error != 0 &&
+            (strcmp(image->image.message,
+               "libpng does not support gamma+background+rgb_to_gray") == 0 ?
+                  (image->opts & GBG_ERROR) != 0 : (image->opts & STRICT) != 0))
+      return logerror(image, image->file_name, (image->opts & GBG_ERROR) != 0 ?
+                      " --fault-gbg-warning" : " --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;
+      }
+   }
+}
+
+/* Conversion 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;  /* Accumulate 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)
+{
+   png_uint_32 in_format = transform->in_image->image.format;
+   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;
+   png_uint_32 width = a->image.width;
+   png_uint_32 height = a->image.height;
+   png_uint_32 formata = a->image.format;
+   png_uint_32 formatb = b->image.format;
+   unsigned int a_sample = PNG_IMAGE_SAMPLE_SIZE(formata);
+   unsigned int b_sample = PNG_IMAGE_SAMPLE_SIZE(formatb);
+   int alpha_added, alpha_removed;
+   int bchannels;
+   png_uint_32 y;
+   Transform tr;
+   int btoa[4]={0,0,0,0};
+
+   /* 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 requested, 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)
+      {
+#        ifdef __GNUC__
+#           define BYTE_CHARS 20 /* 2^32: GCC sprintf warning */
+#        else
+#           define BYTE_CHARS 3 /* 2^8: real maximum value */
+#        endif
+         /* Check the original image first,
+          * TODO: deal with input images with bad pixel values?
+          */
+         if (amax >= a->image.colormap_entries)
+         {
+            char pindex[3+2*BYTE_CHARS];
+            sprintf(pindex, "%d[%u]", amax,
+               (png_byte)/*SAFE*/a->image.colormap_entries);
+            return logerror(a, a->file_name, ": bad pixel index: ", pindex);
+         }
+
+         else if (bmax >= b->image.colormap_entries)
+         {
+            char pindex[3+2*BYTE_CHARS];
+            sprintf(pindex, "%d[%u]", bmax,
+               (png_byte)/*SAFE*/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? */
+      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;
+                     /* FALLTHROUGH */
+                  case 3:
+                     if (pua[btoa[2]] != pub[2]) break;
+                     /* FALLTHROUGH */
+                  case 2:
+                     if (pua[btoa[1]] != pub[1]) break;
+                     /* FALLTHROUGH */
+                  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;
+                     /* FALLTHROUGH */
+                  case 3:
+                     if (psa[btoa[2]] != psb[2]) break;
+                     /* FALLTHROUGH */
+                  case 2:
+                     if (psa[btoa[1]] != psb[1]) break;
+                     /* FALLTHROUGH */
+                  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 requested, 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 & USE_FILE) || (image->opts & USE_STDIO))
+   {
+      /* memory or stdio. */
+      FILE *f = fopen(image->file_name, "rb");
+
+      if (f != NULL)
+      {
+         if (image->opts & USE_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)
+   {
+#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
+#ifndef __COVERITY__
+      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 /* SIMPLIFIED_WRITE_STDIO */
+      return logerror(image, "tmpfile", ": open: unsupported", "");
+#endif /* SIMPLIFIED_WRITE_STDIO */
+   }
+
+   else if (image->opts & USE_FILE)
+   {
+#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
+      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", "");
+#else /* SIMPLIFIED_WRITE_STDIO */
+      return logerror(image, "stdio", ": open: unsupported", "");
+#endif /* SIMPLIFIED_WRITE_STDIO */
+   }
+
+   else /* use memory */
+   {
+      png_alloc_size_t size;
+
+      if (png_image_write_get_memory_size(image->image, size, convert_to_8bit,
+               image->buffer+16, (png_int_32)image->stride, image->colormap))
+      {
+         /* This is non-fatal but ignoring it was causing serious problems in
+          * the macro to be ignored:
+          */
+         if (size > PNG_IMAGE_PNG_SIZE_MAX(image->image))
+            return logerror(image, "memory", ": PNG_IMAGE_SIZE_MAX wrong", "");
+
+         initimage(output, image->opts, "memory", image->stride_extra);
+         output->input_memory = malloc(size);
+
+         if (output->input_memory != NULL)
+         {
+            output->input_memory_size = size;
+
+            if (png_image_write_to_memory(&image->image, output->input_memory,
+                  &output->input_memory_size, convert_to_8bit, image->buffer+16,
+                  (png_int_32)image->stride, image->colormap))
+            {
+               /* This is also non-fatal but it safes safer to error out anyway:
+                */
+               if (size != output->input_memory_size)
+                  return logerror(image, "memory", ": memory size wrong", "");
+            }
+
+            else
+               return logerror(image, "memory", ": write failed", "");
+         }
+
+         else
+            return logerror(image, "memory", ": out of memory", "");
+      }
+
+      else
+         return logerror(image, "memory", ": get size:", "");
+   }
+
+   /* '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;
+
+   if (!(opts & NO_RESEED))
+      reseed(); /* ensure that the random numbers don't depend on file order */
+   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 | STRICT;
+   format_list formats;
+   const char *touch = NULL;
+   int log_pass = 0;
+   int redundant = 0;
+   int stride_extra = 0;
+   int retval = 0;
+   int c;
+
+#if PNG_LIBPNG_VER >= 10700
+      /* This error should not exist in 1.7 or later: */
+      opts |= GBG_ERROR;
+#endif
+
+   init_sRGB_to_d();
+#if 0
+   init_error_via_linear();
+#endif
+   format_init(&formats);
+   reseed(); /* initialize random number seeds */
+
+   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 |= USE_FILE;
+#        else
+            return SKIP; /* skipped: no support */
+#        endif
+      else if (strcmp(arg, "--memory") == 0)
+         opts &= ~USE_FILE;
+      else if (strcmp(arg, "--stdio") == 0)
+#        ifdef PNG_STDIO_SUPPORTED
+            opts |= USE_STDIO;
+#        else
+            return SKIP; /* 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, "--nostrict") == 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, "--noreseed") == 0)
+         opts |= NO_RESEED;
+      else if (strcmp(arg, "--fault-gbg-warning") == 0)
+         opts |= GBG_ERROR;
+      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] == '-')
+         {
+            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 SKIP;
+}
+#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngunknown.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngunknown.c
new file mode 100755
index 0000000..05bdd83
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngunknown.c
@@ -0,0 +1,1294 @@
+
+/* pngunknown.c - test the read side unknown chunk handling
+ *
+ * Last changed in libpng 1.6.32 [August 24, 2017]
+ * Copyright (c) 2015,2017 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
+
+/* 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 && defined(HAVE_CONFIG_H)
+#  define SKIP 77
+#else
+#  define SKIP 0
+#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_STDIO_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 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 after 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_eXIf PNG_U32(101,  88,  73, 102) /* registered July 2017 */
+#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 },
+   { "eXIf", PNG_INFO_eXIf, png_eXIf,
+#     ifdef PNG_READ_eXIf_SUPPORTED
+         0,
+#     else
+         1,
+#     endif
+      1,  END, 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;
+               }
+               /* FALLTHROUGH */ /* (safe) */
+            case PNG_HANDLE_CHUNK_ALWAYS:
+               break;
+         }
+
+         else
+            flags |= chunk_info[chunk].flag;
+      }
+   }
+
+   return flags;
+}
+#else /* SAVE_UNKNOWN_CHUNKS */
+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 /* READ_USER_CHUNKS */
+#endif /* SAVE_UNKNOWN_CHUNKS */
+
+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);
+
+   /* If IDAT is being handled as unknown the image read is skipped and all the
+    * IDATs after the first end up in the end info struct, so in this case add
+    * IDAT to the list of unknowns.  (Do this after 'check' above sets the
+    * chunk_info 'keep' fields.)
+    *
+    * Note that the flag setting has to be in the 'known' field to avoid
+    * triggering the consistency check below and the flag must only be set if
+    * there are multiple IDATs, so if the check above did find an unknown IDAT
+    * after IDAT.
+    */
+   if (chunk_info[0/*IDAT*/].keep != PNG_HANDLE_CHUNK_AS_DEFAULT &&
+       (flags[1][3] & PNG_INFO_IDAT) != 0)
+      flags[0][2] |= PNG_INFO_IDAT;
+
+   /* 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", "eXIf=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 SKIP;
+}
+#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 SKIP;
+}
+#endif /* SET_UNKNOWN_CHUNKS && READ*/
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngvalid.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngvalid.c
new file mode 100755
index 0000000..d800110
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/pngvalid.c
@@ -0,0 +1,12230 @@
+
+/* pngvalid.c - validate libpng by constructing then reading png files.
+ *
+ * Last changed in libpng 1.6.31 [July 27, 2017]
+ * Copyright (c) 2014-2017 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 */
+#define _BSD_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 && defined(HAVE_CONFIG_H)
+#  define SKIP 77
+#else
+#  define SKIP 0
+#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 after 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
+
+#ifndef RELEASE_BUILD
+   /* RELEASE_BUILD is true for releases and release candidates: */
+#  define RELEASE_BUILD (PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC)
+#endif
+#if RELEASE_BUILD
+#   define debugonly(something)
+#else /* !RELEASE_BUILD */
+#   define debugonly(something) something
+#endif /* !RELEASE_BUILD */
+
+#include <float.h>  /* For floating point constants */
+#include <stdlib.h> /* For malloc */
+#include <string.h> /* For memcpy, memset */
+#include <math.h>   /* For floor */
+
+/* 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)
+
+/* 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 ||\
+    defined PNG_WRITE_FILTER_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 R8(this) randomize(&(this), sizeof (this))
+
+#ifdef PNG_READ_SUPPORTED
+static png_byte
+random_byte(void)
+{
+   unsigned char b1[1];
+   randomize(b1, sizeof b1);
+   return b1[0];
+}
+#endif /* READ */
+
+static png_uint_16
+random_u16(void)
+{
+   unsigned char b2[2];
+   randomize(b2, sizeof b2);
+   return png_get_uint_16(b2);
+}
+
+#if defined PNG_READ_RGB_TO_GRAY_SUPPORTED ||\
+    defined PNG_READ_FILLER_SUPPORTED
+static png_uint_32
+random_u32(void)
+{
+   unsigned char b4[4];
+   randomize(b4, sizeof b4);
+   return png_get_uint_32(b4);
+}
+#endif /* READ_FILLER || READ_RGB_TO_GRAY */
+
+#endif /* READ || WRITE_tRNS || WRITE_FILTER */
+
+#if defined PNG_READ_TRANSFORMS_SUPPORTED ||\
+    defined PNG_WRITE_FILTER_SUPPORTED
+static unsigned int
+random_mod(unsigned int max)
+{
+   return random_u16() % max; /* 0 .. max-1 */
+}
+#endif /* READ_TRANSFORMS || WRITE_FILTER */
+
+#if (defined PNG_READ_RGB_TO_GRAY_SUPPORTED) ||\
+    (defined PNG_READ_FILLER_SUPPORTED)
+static int
+random_choice(void)
+{
+   return random_byte() & 1;
+}
+#endif /* READ_RGB_TO_GRAY || READ_FILLER */
+
+/* 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];
+   unsigned int            IDAT_bits; /* Number of bits in IDAT size */
+   png_uint_32             IDAT_size; /* Total size of IDAT data */
+   png_uint_32             id;        /* must be correct (see FILEID) */
+   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];
+
+   /* Share fields */
+   png_uint_32        chunklen; /* Length of chunk+overhead (chunkpos >= 8) */
+   png_uint_32        chunktype;/* Type of chunk (valid if chunkpos >= 4) */
+   png_uint_32        chunkpos; /* Position in chunk */
+   png_uint_32        IDAT_size;/* Accumulated IDAT size in .new */
+   unsigned int       IDAT_bits;/* Cache of the file store value */
+
+   /* 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 */
+   size_t             readpos;  /* Position in *next */
+   png_byte*          image;    /* Buffer for reading interlaced images */
+   size_t             cb_image; /* Size of this buffer */
+   size_t             cb_row;   /* Row size of the image(s) */
+   uLong              IDAT_crc;
+   png_uint_32        IDAT_len; /* Used when re-chunking IDAT chunks */
+   png_uint_32        IDAT_pos; /* Used when re-chunking IDAT chunks */
+   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;
+   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->chunkpos = 8;
+   ps->chunktype = 0;
+   ps->chunklen = 16;
+   ps->IDAT_size = 0;
+   ps->IDAT_bits = 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;
+   ps->chunkpos = 8;
+   ps->chunktype = 0;
+   ps->chunklen = 16;
+   ps->IDAT_size = 0;
+   ps->IDAT_bits = 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;
+
+   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;
+   }
+}
+
+static unsigned int
+bits_of(png_uint_32 num)
+{
+   /* Return the number of bits in 'num' */
+   unsigned int b = 0;
+
+   if (num & 0xffff0000U)  b += 16U, num >>= 16;
+   if (num & 0xff00U)      b += 8U, num >>= 8;
+   if (num & 0xf0U)        b += 4U, num >>= 4;
+   if (num & 0xcU)         b += 2U, num >>= 2;
+   if (num & 0x2U)         ++b, num >>= 1;
+   if (num)                ++b;
+
+   return b; /* 0..32 */
+}
+
+/* Main interface to file storage, 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;
+
+   if (ps->chunkpos != 0U || ps->chunktype != 0U || ps->chunklen != 0U ||
+       ps->IDAT_size == 0)
+      png_error(ps->pwrite, "storefile: incomplete write");
+
+   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;
+   pf->IDAT_size = ps->IDAT_size;
+   pf->IDAT_bits = bits_of(ps->IDAT_size);
+   /* Because the IDAT always has zlib header stuff this must be true: */
+   if (pf->IDAT_bits == 0U)
+      png_error(ps->pwrite, "storefile: 0 sized IDAT");
+   ps->new.prev = NULL;
+   ps->writepos = 0;
+   ps->chunkpos = 8;
+   ps->chunktype = 0;
+   ps->chunklen = 16;
+   ps->IDAT_size = 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)
+{
+   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,
+   size_t cbRow, png_uint_32 cRows)
+{
+   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
+   {
+      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 */
+
+      for (; rows > 0; --rows)
+      {
+         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 int
+valid_chunktype(png_uint_32 chunktype)
+{
+   /* Each byte in the chunk type must be in one of the ranges 65..90, 97..122
+    * (both inclusive), so:
+    */
+   unsigned int i;
+
+   for (i=0; i<4; ++i)
+   {
+      unsigned int c = chunktype & 0xffU;
+
+      if (!((c >= 65U && c <= 90U) || (c >= 97U && c <= 122U)))
+         return 0;
+
+      chunktype >>= 8;
+   }
+
+   return 1; /* It's valid */
+}
+
+static void PNGCBAPI
+store_write(png_structp ppIn, png_bytep pb, size_t st)
+{
+   png_const_structp pp = ppIn;
+   png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
+   size_t writepos = ps->writepos;
+   png_uint_32 chunkpos = ps->chunkpos;
+   png_uint_32 chunktype = ps->chunktype;
+   png_uint_32 chunklen = ps->chunklen;
+
+   if (ps->pwrite != pp)
+      png_error(pp, "store state damaged");
+
+   /* Technically this is legal, but in practice libpng never writes more than
+    * the maximum chunk size at once so if it happens something weird has
+    * changed inside libpng (probably).
+    */
+   if (st > 0x7fffffffU)
+      png_error(pp, "unexpected write size");
+
+   /* Now process the bytes to be written.  Do this in units of the space in the
+    * output (write) buffer or, at the start 4 bytes for the chunk type and
+    * length limited in any case by the amount of data.
+    */
+   while (st > 0)
+   {
+      if (writepos >= STORE_BUFFER_SIZE)
+         store_storenew(ps), writepos = 0;
+
+      if (chunkpos < 4)
+      {
+         png_byte b = *pb++;
+         --st;
+         chunklen = (chunklen << 8) + b;
+         ps->new.buffer[writepos++] = b;
+         ++chunkpos;
+      }
+
+      else if (chunkpos < 8)
+      {
+         png_byte b = *pb++;
+         --st;
+         chunktype = (chunktype << 8) + b;
+         ps->new.buffer[writepos++] = b;
+
+         if (++chunkpos == 8)
+         {
+            chunklen &= 0xffffffffU;
+            if (chunklen > 0x7fffffffU)
+               png_error(pp, "chunk length too great");
+
+            chunktype &= 0xffffffffU;
+            if (chunktype == CHUNK_IDAT)
+            {
+               if (chunklen > ~ps->IDAT_size)
+                  png_error(pp, "pngvalid internal image too large");
+
+               ps->IDAT_size += chunklen;
+            }
+
+            else if (!valid_chunktype(chunktype))
+               png_error(pp, "invalid chunk type");
+
+            chunklen += 12; /* for header and CRC */
+         }
+      }
+
+      else /* chunkpos >= 8 */
+      {
+         size_t cb = st;
+
+         if (cb > STORE_BUFFER_SIZE - writepos)
+            cb = STORE_BUFFER_SIZE - writepos;
+
+         if (cb  > chunklen - chunkpos/* bytes left in chunk*/)
+            cb = (size_t)/*SAFE*/(chunklen - chunkpos);
+
+         memcpy(ps->new.buffer + writepos, pb, cb);
+         chunkpos += (png_uint_32)/*SAFE*/cb;
+         pb += cb;
+         writepos += cb;
+         st -= cb;
+
+         if (chunkpos >= chunklen) /* must be equal */
+            chunkpos = chunktype = chunklen = 0;
+      }
+   } /* while (st > 0) */
+
+   ps->writepos = writepos;
+   ps->chunkpos = chunkpos;
+   ps->chunktype = chunktype;
+   ps->chunklen = chunklen;
+}
+
+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;
+}
+
+/* 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;
+}
+
+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, 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 size_t
+store_read_chunk(png_store *ps, png_bytep pb, size_t max, size_t min)
+{
+   png_uint_32 chunklen = ps->chunklen;
+   png_uint_32 chunktype = ps->chunktype;
+   png_uint_32 chunkpos = ps->chunkpos;
+   size_t st = max;
+
+   if (st > 0) do
+   {
+      if (chunkpos >= chunklen) /* end of last chunk */
+      {
+         png_byte buffer[8];
+
+         /* Read the header of the next chunk: */
+         store_read_imp(ps, buffer, 8U);
+         chunklen = png_get_uint_32(buffer) + 12U;
+         chunktype = png_get_uint_32(buffer+4U);
+         chunkpos = 0U; /* Position read so far */
+      }
+
+      if (chunktype == CHUNK_IDAT)
+      {
+         png_uint_32 IDAT_pos = ps->IDAT_pos;
+         png_uint_32 IDAT_len = ps->IDAT_len;
+         png_uint_32 IDAT_size = ps->IDAT_size;
+
+         /* The IDAT headers are constructed here; skip the input header. */
+         if (chunkpos < 8U)
+            chunkpos = 8U;
+
+         if (IDAT_pos == IDAT_len)
+         {
+            png_byte random = random_byte();
+
+            /* Make a new IDAT chunk, if IDAT_len is 0 this is the first IDAT,
+             * if IDAT_size is 0 this is the end.  At present this is set up
+             * using a random number so that there is a 25% chance before
+             * the start of the first IDAT chunk being 0 length.
+             */
+            if (IDAT_len == 0U) /* First IDAT */
+            {
+               switch (random & 3U)
+               {
+                  case 0U: IDAT_len = 12U; break; /* 0 bytes */
+                  case 1U: IDAT_len = 13U; break; /* 1 byte */
+                  default: IDAT_len = random_u32();
+                           IDAT_len %= IDAT_size;
+                           IDAT_len += 13U; /* 1..IDAT_size bytes */
+                           break;
+               }
+            }
+
+            else if (IDAT_size == 0U) /* all IDAT data read */
+            {
+               /* The last (IDAT) chunk should be positioned at the CRC now: */
+               if (chunkpos != chunklen-4U)
+                  png_error(ps->pread, "internal: IDAT size mismatch");
+
+               /* The only option here is to add a zero length IDAT, this
+                * happens 25% of the time.  Because of the check above
+                * chunklen-4U-chunkpos must be zero, we just need to skip the
+                * CRC now.
+                */
+               if ((random & 3U) == 0U)
+                  IDAT_len = 12U; /* Output another 0 length IDAT */
+
+               else
+               {
+                  /* End of IDATs, skip the CRC to make the code above load the
+                   * next chunk header next time round.
+                   */
+                  png_byte buffer[4];
+
+                  store_read_imp(ps, buffer, 4U);
+                  chunkpos += 4U;
+                  ps->IDAT_pos = IDAT_pos;
+                  ps->IDAT_len = IDAT_len;
+                  ps->IDAT_size = 0U;
+                  continue; /* Read the next chunk */
+               }
+            }
+
+            else
+            {
+               /* Middle of IDATs, use 'random' to determine the number of bits
+                * to use in the IDAT length.
+                */
+               IDAT_len = random_u32();
+               IDAT_len &= (1U << (1U + random % ps->IDAT_bits)) - 1U;
+               if (IDAT_len > IDAT_size)
+                  IDAT_len = IDAT_size;
+               IDAT_len += 12U; /* zero bytes may occur */
+            }
+
+            IDAT_pos = 0U;
+            ps->IDAT_crc = 0x35af061e; /* Ie: crc32(0UL, "IDAT", 4) */
+         } /* IDAT_pos == IDAT_len */
+
+         if (IDAT_pos < 8U) /* Return the header */ do
+         {
+            png_uint_32 b;
+            unsigned int shift;
+
+            if (IDAT_pos < 4U)
+               b = IDAT_len - 12U;
+
+            else
+               b = CHUNK_IDAT;
+
+            shift = 3U & IDAT_pos;
+            ++IDAT_pos;
+
+            if (shift < 3U)
+               b >>= 8U*(3U-shift);
+
+            *pb++ = 0xffU & b;
+         }
+         while (--st > 0 && IDAT_pos < 8);
+
+         else if (IDAT_pos < IDAT_len - 4U) /* I.e not the CRC */
+         {
+            if (chunkpos < chunklen-4U)
+            {
+               uInt avail = (uInt)-1;
+
+               if (avail > (IDAT_len-4U) - IDAT_pos)
+                  avail = (uInt)/*SAFE*/((IDAT_len-4U) - IDAT_pos);
+
+               if (avail > st)
+                  avail = (uInt)/*SAFE*/st;
+
+               if (avail > (chunklen-4U) - chunkpos)
+                  avail = (uInt)/*SAFE*/((chunklen-4U) - chunkpos);
+
+               store_read_imp(ps, pb, avail);
+               ps->IDAT_crc = crc32(ps->IDAT_crc, pb, avail);
+               pb += (size_t)/*SAFE*/avail;
+               st -= (size_t)/*SAFE*/avail;
+               chunkpos += (png_uint_32)/*SAFE*/avail;
+               IDAT_size -= (png_uint_32)/*SAFE*/avail;
+               IDAT_pos += (png_uint_32)/*SAFE*/avail;
+            }
+
+            else /* skip the input CRC */
+            {
+               png_byte buffer[4];
+
+               store_read_imp(ps, buffer, 4U);
+               chunkpos += 4U;
+            }
+         }
+
+         else /* IDAT crc */ do
+         {
+            uLong b = ps->IDAT_crc;
+            unsigned int shift = (IDAT_len - IDAT_pos); /* 4..1 */
+            ++IDAT_pos;
+
+            if (shift > 1U)
+               b >>= 8U*(shift-1U);
+
+            *pb++ = 0xffU & b;
+         }
+         while (--st > 0 && IDAT_pos < IDAT_len);
+
+         ps->IDAT_pos = IDAT_pos;
+         ps->IDAT_len = IDAT_len;
+         ps->IDAT_size = IDAT_size;
+      }
+
+      else /* !IDAT */
+      {
+         /* If there is still some pending IDAT data after the IDAT chunks have
+          * been processed there is a problem:
+          */
+         if (ps->IDAT_len > 0 && ps->IDAT_size > 0)
+            png_error(ps->pread, "internal: missing IDAT data");
+
+         if (chunktype == CHUNK_IEND && ps->IDAT_len == 0U)
+            png_error(ps->pread, "internal: missing IDAT");
+
+         if (chunkpos < 8U) /* Return the header */ do
+         {
+            png_uint_32 b;
+            unsigned int shift;
+
+            if (chunkpos < 4U)
+               b = chunklen - 12U;
+
+            else
+               b = chunktype;
+
+            shift = 3U & chunkpos;
+            ++chunkpos;
+
+            if (shift < 3U)
+               b >>= 8U*(3U-shift);
+
+            *pb++ = 0xffU & b;
+         }
+         while (--st > 0 && chunkpos < 8);
+
+         else /* Return chunk bytes, including the CRC */
+         {
+            size_t avail = st;
+
+            if (avail > chunklen - chunkpos)
+               avail = (size_t)/*SAFE*/(chunklen - chunkpos);
+
+            store_read_imp(ps, pb, avail);
+            pb += avail;
+            st -= avail;
+            chunkpos += (png_uint_32)/*SAFE*/avail;
+
+            /* Check for end of chunk and end-of-file; don't try to read a new
+             * chunk header at this point unless instructed to do so by 'min'.
+             */
+            if (chunkpos >= chunklen && max-st >= min &&
+                     store_read_buffer_avail(ps) == 0)
+               break;
+         }
+      } /* !IDAT */
+   }
+   while (st > 0);
+
+   ps->chunklen = chunklen;
+   ps->chunktype = chunktype;
+   ps->chunkpos = chunkpos;
+
+   return st; /* space left */
+}
+
+static void PNGCBAPI
+store_read(png_structp ppIn, png_bytep pb, 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_chunk(ps, pb, st, st);
+}
+
+static void
+store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
+{
+   if (ps->pread != pp || ps->current == NULL || ps->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.
+    */
+   while (store_read_buffer_avail(ps) > 0)
+   {
+      static png_uint_32 noise = 2;
+      size_t cb;
+      png_byte buffer[512];
+
+      /* Generate 15 more bits of stuff: */
+      noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
+      cb = noise & 0x1ff;
+      cb -= store_read_chunk(ps, buffer, cb, 1);
+      png_process_data(pp, pi, buffer, cb);
+   }
+}
+#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;
+
+   ps->chunkpos = 8;
+   ps->chunktype = 0;
+   ps->chunklen = 16;
+   ps->IDAT_size = 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;
+         ps->IDAT_size = pf->IDAT_size;
+         ps->IDAT_bits = pf->IDAT_bits; /* just a cache */
+         ps->IDAT_len = 0;
+         ps->IDAT_pos = 0;
+         ps->IDAT_crc = 0UL;
+         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.
+    *
+    * NOTE: this is not checked in release builds.
+    */
+   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.
+    */
+   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;
+}
+
+/* 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, 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_chunk(&pm->this, pm->buffer, 8, 8); /* 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_chunk(&pm->this, pm->buffer, 13+12, 13+12); /* 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_chunk(&pm->this, pb, cb, 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_chunk(&pm->this, pm->buffer, 8, 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)
+            {
+               size_t s = len+12-pm->buffer_count;
+               store_read_chunk(&pm->this, pm->buffer+pm->buffer_count, s, s);
+               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, 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;
+      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, png_byte colour_type,
+   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;
+   png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1);
+
+   R8(tRNS); /* makes unset fields random */
+
+   if (colour_type & 2/*RGB*/)
+   {
+      if (bit_depth == 8)
+      {
+         tRNS.red = random_u16();
+         tRNS.green = random_u16();
+         tRNS.blue = tRNS.red ^ tRNS.green;
+         tRNS.red &= mask;
+         tRNS.green &= mask;
+         tRNS.blue &= mask;
+      }
+
+      else /* bit_depth == 16 */
+      {
+         tRNS.red = random_u16();
+         tRNS.green = (png_uint_16)(tRNS.red * 257);
+         tRNS.blue = (png_uint_16)(tRNS.green * 17);
+      }
+   }
+
+   else
+   {
+      tRNS.gray = random_u16();
+      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 */
+
+#if PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED
+#   define CAN_WRITE_INTERLACE 1
+#else
+#   define CAN_WRITE_INTERLACE 0
+#endif
+
+/* 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.
+ */
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+static void
+choose_random_filter(png_structp pp, int start)
+{
+   /* Choose filters randomly except that on the very first row ensure that
+    * there is at least one previous row filter.
+    */
+   int filters = PNG_ALL_FILTERS & random_mod(256U);
+
+   /* There may be no filters; skip the setting. */
+   if (filters != 0)
+   {
+      if (start && filters < PNG_FILTER_UP)
+         filters |= PNG_FILTER_UP;
+
+      png_set_filter(pp, 0/*method*/, filters);
+   }
+}
+#else /* !WRITE_FILTER */
+#  define choose_random_filter(pp, start) ((void)0)
+#endif /* !WRITE_FILTER */
+
+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 */
+
+               choose_random_filter(pp, pass == 0 && y == 0);
+               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];
+      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;
+         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: */
+            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 code now uses filters at random, except that on the first
+                * row of an image it ensures that a previous row filter is in
+                * the set so that libpng allocates the row buffer.
+                */
+               {
+                  int filters = 8 << random_mod(PNG_FILTER_VALUE_LAST);
+
+                  if (pass == 0 && y == 0 &&
+                      (filters < PNG_FILTER_UP || w == 1U))
+                     filters |= PNG_FILTER_UP;
+
+                  png_set_filter(pp, 0/*method*/, filters);
+               }
+#           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;
+      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_warning = 0;
+
+      if (ps->expect_error)
+         ps->expect_error = 0;
+
+      else
+      {
+         /* Now write the whole image, just to make sure that the detected, or
+          * undetected, error has not created problems inside libpng.  This
+          * doesn't work if there was a png_error in png_write_info because that
+          * can abort before PLTE was written.
+          */
+         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);
+               }
+            }
+         } /* image writing */
+
+         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 throughout: */
+   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 transparent 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,
+    int iImage, int iDisplay)
+{
+   int npasses = dp->npasses;
+   int do_interlace = dp->do_interlace &&
+      dp->interlace_type == PNG_INTERLACE_ADAM7;
+   png_uint_32 height = standard_height(pp, dp->id);
+   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};
+   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*/)
+{
+   png_byte sample_depth =
+      (png_byte)(colour_type == PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
+   unsigned int max = (1U<<sample_depth)-1;
+   int swap16 = (format != 0 && format->swap16);
+   int littleendian = (format != 0 && format->littleendian);
+   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)
+      {
+         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)
+{
+   unsigned int scale = (1U<<that->sample_depth)-1;
+   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;
+      /* FALLTHROUGH */
+   case PNG_COLOR_TYPE_GRAY:
+      if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
+         dp->output_bit_depth == 4)
+         break;
+      /* FALLTHROUGH */
+   default:
+      if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
+         break;
+      /* FALLTHROUGH */
+   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 (debugonly(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);
+   }
+
+   UNUSED(limit)
+}
+
+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;
+   png_byte in_ct = dp->this.colour_type;
+   png_byte in_bd = dp->this.bit_depth;
+   png_uint_32 w = dp->this.w;
+   png_uint_32 h = dp->this.h;
+   png_byte out_ct = dp->output_colour_type;
+   png_byte out_bd = dp->output_bit_depth;
+   png_byte sample_depth =
+      (png_byte)(out_ct == PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
+   png_byte red_sBIT = dp->this.red_sBIT;
+   png_byte green_sBIT = dp->this.green_sBIT;
+   png_byte blue_sBIT = dp->this.blue_sBIT;
+   png_byte alpha_sBIT = dp->this.alpha_sBIT;
+   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, otherwise 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, 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;
+
+      ru = random_u32();
+      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 number only affects the internal limit check in pngvalid,
+          * it has no effect on the limits applied to the libpng values.
+          */
+#if DIGITIZE
+          that->pm->limit += pow( 2.0/255, data.gamma);
+#else
+          that->pm->limit += pow( 1.0/255, data.gamma);
+#endif
+      }
+   }
+
+   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)
+{
+   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
+      {
+         png_modifier *pm = display->pm;
+         unsigned int sample_depth = that->sample_depth;
+         unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
+            sample_depth);
+         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 maximum 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;
+
+         {
+            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);
+
+#if !RELEASE_BUILD
+            /* 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_warning(pp, buffer);
+               pm->limit = err;
+            }
+#endif /* !RELEASE_BUILD */
+         }
+      }
+#  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.
+             */
+            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;
+
+#if !RELEASE_BUILD
+            /* 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_warning(pp, buffer);
+               pm->limit = err;
+            }
+#endif /* !RELEASE_BUILD */
+         }
+      }
+#  endif /* !DIGITIZE */
+
+      that->bluef = that->greenf = that->redf = gray;
+      that->bluee = that->greene = that->rede = err;
+
+      /* The sBIT is the minimum 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.
+    */
+   R8(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.
+    */
+   R8(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.
+    */
+   data.filler = random_u32();
+   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))
+   {
+      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.
+    */
+   data.filler = random_u32();
+   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))
+   {
+      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.
+    */
+   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, representation.  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
+            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
+            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)
+{
+   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,
+    unsigned int id, unsigned int od,
+    const double alpha /* <0 for the alpha channel itself */,
+    const double background /* component background value */)
+{
+   unsigned int isbit = id >> vi->isbit_shift;
+   unsigned int sbit_max = vi->sbit_max;
+   unsigned int outmax = vi->outmax;
+   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 output 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);
+#           endif
+#           ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+               /* FALLTHROUGH */
+               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;
+   png_byte in_ct = dp->this.colour_type;
+   png_byte in_bd = dp->this.bit_depth;
+   png_uint_32 w = dp->this.w;
+   png_uint_32 h = dp->this.h;
+   const size_t cbRow = dp->this.cbRow;
+   png_byte out_ct = png_get_color_type(pp, pi);
+   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 parameters to the API.  (Effectively, add .5
+    * everywhere.)
+    *
+    * The behavior of the 'sbit' parameter 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.
+    */
+   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;
+   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. */
+            unsigned int in_index =
+               in_ct == 3 ? sample(std, 3, in_bd, x, 0, 0, 0) : 256;
+            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))
+            {
+               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, png_byte colour_typeIn,
+    png_byte bit_depthIn, int palette_numberIn,
+    int interlace_typeIn,
+    const double file_gammaIn, const double screen_gammaIn,
+    png_byte sbitIn, int threshold_testIn,
+    const char *name,
+    int use_input_precisionIn, int scale16In,
+    int expand16In, 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,
+   png_byte colour_type, png_byte bit_depth,
+   int palette_number,
+   int interlace_type, const double file_gamma,
+   const double screen_gamma, png_byte sbit,
+   int use_input_precision, 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,
+   png_byte colour_type, png_byte bit_depth,
+   int palette_number,
+   int interlace_type, const double file_gamma,
+   const double screen_gamma,
+   int use_input_precision, int do_background,
+   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;;)
+      {
+         /* The first two tests overflow if the pass row or column is outside
+          * the possible range for a 32-bit result.  In fact the values should
+          * never be outside the range for a 31-bit result, but checking for 32
+          * bits here ensures that if an app uses a bogus pass row or column
+          * (just so long as it fits in a 32 bit integer) it won't get a
+          * possibly dangerous overflow.
+          */
+         /* First the base 0 stuff: */
+         if (v < png_pass_rows(0xFFFFFFFFU, pass))
+         {
+            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);
+            }
+         }
+
+         if (v < png_pass_cols(0xFFFFFFFFU, pass))
+         {
+            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 practical 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 = 1; /* NOTE: this is the default! */
+
+      else if (strcmp(*argv, "--nostrict") == 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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/readpng.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/readpng.c
new file mode 100755
index 0000000..60a2d46
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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);
+
+   {
+      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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/tarith.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/tarith.c
new file mode 100755
index 0000000..c03f05a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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
+   {
+      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 positive 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.
+    */
+   size_t index = 0;
+   const char test = (char)ch;
+   int number_is_valid = png_check_fp_number(&test, 1, &c.state, &index);
+   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/ap/lib/libpng/libpng-1.6.37/contrib/libtests/timepng.c b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/timepng.c
new file mode 100755
index 0000000..23d8b17
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/libtests/timepng.c
@@ -0,0 +1,608 @@
+/* timepng.c
+ *
+ * Copyright (c) 2013,2016 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.22 [May 26, 2016]
+ *
+ * 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 or by image (possibly with transforms in the latter
+ * case).  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 <errno.h>
+#include <limits.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
+
+/* The following is to support direct compilation of this file as C++ */
+#ifdef __cplusplus
+#  define voidcast(type, value) static_cast<type>(value)
+#else
+#  define voidcast(type, value) (value)
+#endif /* __cplusplus */
+
+/* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime.  It
+ * need not be supported even when clock_gettime is available.  It returns the
+ * 'CPU' time the process has consumed.  'CPU' time is assumed to include time
+ * when the CPU is actually blocked by a pending cache fill but not time
+ * waiting for page faults.  The attempt is to get a measure of the actual time
+ * the implementation takes to read a PNG ignoring the potentially very large IO
+ * overhead.
+ */
+#if defined (CLOCK_PROCESS_CPUTIME_ID) && defined(PNG_STDIO_SUPPORTED) &&\
+    defined(PNG_EASY_ACCESS_SUPPORTED) &&\
+    (PNG_LIBPNG_VER >= 10700 ? defined(PNG_READ_PNG_SUPPORTED) :\
+     defined (PNG_SEQUENTIAL_READ_SUPPORTED) &&\
+     defined(PNG_INFO_IMAGE_SUPPORTED))
+
+typedef struct
+{
+   FILE *input;
+   FILE *output;
+}  io_data;
+
+static PNG_CALLBACK(void, read_and_copy,
+      (png_structp png_ptr, png_bytep buffer, size_t cb))
+{
+   io_data *io = (io_data*)png_get_io_ptr(png_ptr);
+
+   if (fread(buffer, cb, 1, io->input) != 1)
+      png_error(png_ptr, strerror(errno));
+
+   if (fwrite(buffer, cb, 1, io->output) != 1)
+   {
+      perror("temporary file");
+      fprintf(stderr, "temporary file PNG write failed\n");
+      exit(1);
+   }
+}
+
+static void read_by_row(png_structp png_ptr, png_infop info_ptr,
+      FILE *write_ptr, FILE *read_ptr)
+{
+   /* These don't get freed on error, this is fine; the program immediately
+    * exits.
+    */
+   png_bytep row = NULL, display = NULL;
+   io_data io_copy;
+
+   if (write_ptr != NULL)
+   {
+      /* Set up for a copy to the temporary file: */
+      io_copy.input = read_ptr;
+      io_copy.output = write_ptr;
+      png_set_read_fn(png_ptr, &io_copy, read_and_copy);
+   }
+
+   png_read_info(png_ptr, info_ptr);
+
+   {
+      size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+      row = voidcast(png_bytep,malloc(rowbytes));
+      display = voidcast(png_bytep,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 and is
+             * somewhat representative of an application that works row-by-row.
+             */
+            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);
+
+   /* Free this up: */
+   free(row);
+   free(display);
+}
+
+static PNG_CALLBACK(void, no_warnings, (png_structp png_ptr,
+         png_const_charp warning))
+{
+   (void)png_ptr;
+   (void)warning;
+}
+
+static int read_png(FILE *fp, png_int_32 transforms, FILE *write_file)
+{
+   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,
+         no_warnings);
+   png_infop info_ptr = NULL;
+
+   if (png_ptr == NULL)
+      return 0;
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      return 0;
+   }
+
+#  ifdef PNG_BENIGN_ERRORS_SUPPORTED
+      png_set_benign_errors(png_ptr, 1/*allowed*/);
+#  endif
+   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");
+
+   if (transforms < 0)
+      read_by_row(png_ptr, info_ptr, write_file, fp);
+
+   else
+      png_read_png(png_ptr, info_ptr, transforms, NULL/*params*/);
+
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+   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, png_int_32 transforms)
+{
+   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, transforms, NULL/*write*/))
+         {
+            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)
+   {
+      /* Read the file using libpng; this detects errors and also deals with
+       * files which contain data beyond the end of the file.
+       */
+      int ok = 0;
+      fpos_t pos;
+
+      if (fgetpos(fp, &pos))
+      {
+         /* Fatal error reading the start: */
+         perror("temporary file");
+         fprintf(stderr, "temporary file fgetpos error\n");
+         exit(1);
+      }
+
+      if (read_png(ip, -1/*by row*/, fp/*output*/))
+      {
+         if (ferror(ip))
+         {
+            perror(name);
+            fprintf(stderr, "%s: read error\n", name);
+         }
+
+         else
+            ok = 1; /* read ok */
+      }
+
+      else
+         fprintf(stderr, "%s: file not added\n", name);
+
+      (void)fclose(ip);
+
+      /* An error in the output is fatal; exit immediately: */
+      if (ferror(fp))
+      {
+         perror("temporary file");
+         fprintf(stderr, "temporary file write error\n");
+         exit(1);
+      }
+
+      if (ok)
+         return 1;
+
+      /* Did not read the file successfully, simply rewind the temporary
+       * file.  This must happen after the ferror check above to avoid clearing
+       * the error.
+       */
+      if (fsetpos(fp, &pos))
+      {
+         perror("temporary file");
+         fprintf(stderr, "temporary file fsetpos error\n");
+         exit(1);
+      }
+   }
+
+   else
+   {
+      /* file open error: */
+      perror(name);
+      fprintf(stderr, "%s: open failed\n", name);
+   }
+
+   return 0; /* file not added */
+}
+
+static void
+usage(FILE *fp)
+{
+   if (fp != NULL) fclose(fp);
+
+   fprintf(stderr,
+"Usage:\n"
+" timepng --assemble <assembly> {files}\n"
+"  Read the files into <assembly>, output the count.  Options are ignored.\n"
+" timepng --dissemble <assembly> <count> [options]\n"
+"  Time <count> files from <assembly>, additional files may not be given.\n"
+" Otherwise:\n"
+"  Read the files into a temporary file and time the decode\n"
+"Transforms:\n"
+"  --by-image: read by image with png_read_png\n"
+"  --<transform>: implies by-image, use PNG_TRANSFORM_<transform>\n"
+"  Otherwise: read by row using png_read_row (to a single row buffer)\n"
+   /* ISO C90 string length max 509 */);fprintf(stderr,
+"{files}:\n"
+"  PNG files to copy into the assembly and time.  Invalid files are skipped\n"
+"  with appropriate error messages.  If no files are given the list of files\n"
+"  is read from stdin with each file name terminated by a newline\n"
+"Output:\n"
+"  For --assemble the output is the name of the assembly file followed by the\n"
+"  count of the files it contains; the arguments for --dissemble.  Otherwise\n"
+"  the output is the total decode time in seconds.\n");
+
+   exit(99);
+}
+
+int main(int argc, char **argv)
+{
+   int ok = 0;
+   int err = 0;
+   int nfiles = 0;
+   int transforms = -1; /* by row */
+   const char *assembly = NULL;
+   FILE *fp;
+
+   if (argc > 2 && strcmp(argv[1], "--assemble") == 0)
+   {
+      /* Just build the test file, argv[2] is the file name. */
+      assembly = argv[2];
+      fp = fopen(assembly, "wb");
+      if (fp == NULL)
+      {
+         perror(assembly);
+         fprintf(stderr, "timepng --assemble %s: could not open for write\n",
+               assembly);
+         usage(NULL);
+      }
+
+      argv += 2;
+      argc -= 2;
+   }
+
+   else if (argc > 3 && strcmp(argv[1], "--dissemble") == 0)
+   {
+      fp = fopen(argv[2], "rb");
+
+      if (fp == NULL)
+      {
+         perror(argv[2]);
+         fprintf(stderr, "timepng --dissemble %s: could not open for read\n",
+               argv[2]);
+         usage(NULL);
+      }
+
+      nfiles = atoi(argv[3]);
+      if (nfiles <= 0)
+      {
+         fprintf(stderr,
+               "timepng --dissemble <file> <count>: %s is not a count\n",
+               argv[3]);
+         exit(99);
+      }
+#ifdef __COVERITY__
+      else
+      {
+         nfiles &= PNG_UINT_31_MAX;
+      }
+#endif
+
+      argv += 3;
+      argc -= 3;
+   }
+
+   else /* Else use a temporary file */
+   {
+#ifndef __COVERITY__
+      fp = 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[] = "timepng-XXXXXX";
+      int filedes;
+      umask(0177);
+      filedes = mkstemp(tmpfile);
+      if (filedes < 0)
+        fp = NULL;
+      else
+      {
+        fp = fdopen(filedes,"w+");
+        /* Hide the filename immediately and ensure that the file does
+         * not exist after the program ends
+         */
+        (void) unlink(tmpfile);
+      }
+#endif
+
+      if (fp == NULL)
+      {
+         perror("tmpfile");
+         fprintf(stderr, "timepng: could not open the temporary file\n");
+         exit(1); /* not a user error */
+      }
+   }
+
+   /* Handle the transforms: */
+   while (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-')
+   {
+      const char *opt = *++argv + 2;
+
+      --argc;
+
+      /* Transforms turn on the by-image processing and maybe set some
+       * transforms:
+       */
+      if (transforms == -1)
+         transforms = PNG_TRANSFORM_IDENTITY;
+
+      if (strcmp(opt, "by-image") == 0)
+      {
+         /* handled above */
+      }
+
+#        define OPT(name) else if (strcmp(opt, #name) == 0)\
+         transforms |= PNG_TRANSFORM_ ## name
+
+      OPT(STRIP_16);
+      OPT(STRIP_ALPHA);
+      OPT(PACKING);
+      OPT(PACKSWAP);
+      OPT(EXPAND);
+      OPT(INVERT_MONO);
+      OPT(SHIFT);
+      OPT(BGR);
+      OPT(SWAP_ALPHA);
+      OPT(SWAP_ENDIAN);
+      OPT(INVERT_ALPHA);
+      OPT(STRIP_FILLER);
+      OPT(STRIP_FILLER_BEFORE);
+      OPT(STRIP_FILLER_AFTER);
+      OPT(GRAY_TO_RGB);
+      OPT(EXPAND_16);
+      OPT(SCALE_16);
+
+      else
+      {
+         fprintf(stderr, "timepng %s: unrecognized transform\n", opt);
+         usage(fp);
+      }
+   }
+
+   /* Handle the files: */
+   if (argc > 1 && nfiles > 0)
+      usage(fp); /* Additional files not valid with --dissemble */
+
+   else if (argc > 1)
+   {
+      int i;
+
+      for (i=1; i<argc; ++i)
+      {
+         if (nfiles == INT_MAX)
+         {
+            fprintf(stderr, "%s: skipped, too many files\n", argv[i]);
+            break;
+         }
+
+         else if (add_one_file(fp, argv[i]))
+            ++nfiles;
+      }
+   }
+
+   else if (nfiles == 0) /* Read from stdin withoout --dissemble */
+   {
+      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 (nfiles == INT_MAX)
+            {
+               fprintf(stderr, "%s: skipped, too many files\n", filename);
+               break;
+            }
+
+            else if (add_one_file(fp, filename))
+               ++nfiles;
+         }
+
+         else
+         {
+            fprintf(stderr, "timepng: file name too long: ...%s\n",
+               filename+len-32);
+            err = 1;
+            break;
+         }
+      }
+
+      if (ferror(stdin))
+      {
+         fprintf(stderr, "timepng: stdin: read error\n");
+         err = 1;
+      }
+   }
+
+   /* Perform the test, or produce the --assemble output: */
+   if (!err)
+   {
+      if (nfiles > 0)
+      {
+         if (assembly != NULL)
+         {
+            if (fflush(fp) && !ferror(fp) && fclose(fp))
+            {
+               perror(assembly);
+               fprintf(stderr, "%s: close failed\n", assembly);
+            }
+
+            else
+            {
+               printf("%s %d\n", assembly, nfiles);
+               fflush(stdout);
+               ok = !ferror(stdout);
+            }
+         }
+
+         else
+         {
+            ok = perform_one_test(fp, nfiles, transforms);
+            (void)fclose(fp);
+         }
+      }
+
+      else
+         usage(fp);
+   }
+
+   else
+      (void)fclose(fp);
+
+   /* Exit code 0 on success. */
+   return ok == 0;
+}
+#else /* !sufficient support */
+int main(void) { return 77; }
+#endif /* !sufficient support */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/mips-msa/README b/ap/lib/libpng/libpng-1.6.37/contrib/mips-msa/README
new file mode 100755
index 0000000..10acc93
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/mips-msa/README
@@ -0,0 +1,83 @@
+OPERATING SYSTEM SPECIFIC MIPS MSA DETECTION
+--------------------------------------------
+
+Detection of the ability to execute MIPS MSA on an MIPS 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 mips/mips_init.c
+by setting the macro PNG_MIPS_MSA_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 mips/msa_init.o with the required macro definition.
+
+For any of this code to be used the MIPS MSA code must be enabled and run time
+checks must be supported.  I.e.:
+
+#if PNG_MIPS_MSA_OPT > 0
+#ifdef PNG_MIPS_MSA_CHECK_SUPPORTED
+
+This is done in a 'configure' build by passing configure the argument:
+
+   --enable-mips-msa=check
+
+Apart from the basic Linux implementation in contrib/mips-msa/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_msa(png_structp png_ptr);
+
+That function must return 1 if MIPS MSA 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/ap/lib/libpng/libpng-1.6.37/contrib/mips-msa/linux.c b/ap/lib/libpng/libpng-1.6.37/contrib/mips-msa/linux.c
new file mode 100755
index 0000000..140215c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/mips-msa/linux.c
@@ -0,0 +1,64 @@
+/* contrib/mips-msa/linux.c
+ *
+ * Copyright (c) 2016 Glenn Randers-Pehrson
+ * Written by Mandar Sahastrabuddhe, 2016.
+ * Last changed in libpng 1.6.25beta03 [August 29, 2016]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/mips-msa/README before reporting bugs
+ *
+ * STATUS: SUPPORTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_msa 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>
+#include <string.h>
+#include <stdlib.h>
+
+static int
+png_have_msa(png_structp png_ptr)
+{
+   FILE *f = fopen("/proc/cpuinfo", "rb");
+
+   char *string = "msa";
+   char word[10];
+
+   if (f != NULL)
+   {
+      while(!feof(f))
+      {
+         int ch = fgetc(f);
+         static int i = 0;
+
+         while(!(ch <= 32))
+         {
+            word[i++] = ch;
+            ch = fgetc(f);
+         }
+
+         int val = strcmp(string, word);
+
+         if (val == 0)
+            return 1;
+
+         i = 0;
+         memset(word, 0, 10);
+      }
+
+      fclose(f);
+   }
+#ifdef PNG_WARNINGS_SUPPORTED
+   else
+      png_warning(png_ptr, "/proc/cpuinfo open failed");
+#endif
+   return 0;
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/Dockerfile b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/Dockerfile
new file mode 100755
index 0000000..f5bc1a9
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/Dockerfile
@@ -0,0 +1,25 @@
+# Copyright 2016 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+
+FROM gcr.io/oss-fuzz-base/base-builder
+MAINTAINER glennrp@gmail.com
+RUN apt-get update && \
+    apt-get install -y make autoconf automake libtool
+
+RUN git clone --depth 1 https://github.com/madler/zlib.git
+RUN git clone --depth 1 https://github.com/glennrp/libpng.git
+RUN cp libpng/contrib/oss-fuzz/build.sh $SRC
+WORKDIR libpng
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/README.txt b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/README.txt
new file mode 100755
index 0000000..3b3656f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/README.txt
@@ -0,0 +1,37 @@
+Last changed in libpng 1.6.33 [September 28, 2017]
+Copyright (c) 2017 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
+
+Files in this directory are used by the oss-fuzz project
+(https://github.com/google/oss-fuzz/tree/master/projects/libpng).
+for "fuzzing" libpng.
+
+They were licensed by Google Inc, using the BSD-like Chromium license,
+which may be found at https://cs.chromium.org/chromium/src/LICENSE, or, if
+noted in the source, under the Apache-2.0 license, which may
+be found at http://www.apache.org/licenses/LICENSE-2.0 .
+If they have been modified, the derivatives are copyright Glenn Randers-Pehrson
+and are released under the same licenses as the originals.  Several of
+the original files (libpng_read_fuzzer.options, png.dict, project.yaml)
+had no licensing information; we assumed that these were under the Chromium
+license. Any new files are released under the libpng license (see png.h).
+
+The files are
+                            Original
+ Filename                   or derived   Copyright          License
+ =========================  ==========   ================   ==========
+ Dockerfile*                derived      2017, Glenn R-P    Apache 2.0
+ build.sh                   derived      2017, Glenn R-P    Apache 2.0
+ libpng_read_fuzzer.cc      derived      2017, Glenn R-P    Chromium
+ libpng_read_fuzzer.options original     2015, Chrome Devs  Chromium
+ png.dict                   original     2015, Chrome Devs  Chromium
+ README.txt (this file)     original     2017, Glenn R-P    libpng
+
+ * Dockerfile is a copy of the file used by oss-fuzz. build.sh,
+   png.dict and libpng_read_fuzzer.* are the actual files used by oss-fuzz,
+   which retrieves them from the libpng repository at Github.
+
+To do: exercise the progressive reader and the png encoder.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/build.sh b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/build.sh
new file mode 100755
index 0000000..f9e1b07
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/build.sh
@@ -0,0 +1,51 @@
+#!/bin/bash -eu
+# Copyright 2017-2018 Glenn Randers-Pehrson
+# Copyright 2016 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Last changed in libpng 1.6.35 [July 15, 2018]
+#
+# Revisions by Glenn Randers-Pehrson, 2017:
+# 1. Build only the library, not the tools (changed "make -j$(nproc) all" to
+#     "make -j$(nproc) libpng16.la").
+# 2. Disabled WARNING and WRITE options in pnglibconf.dfa.
+# 3. Build zlib alongside libpng
+################################################################################
+
+# Disable logging via library build configuration control.
+cat scripts/pnglibconf.dfa | \
+  sed -e "s/option STDIO/option STDIO disabled/" \
+      -e "s/option WARNING /option WARNING disabled/" \
+      -e "s/option WRITE enables WRITE_INT_FUNCTIONS/option WRITE disabled/" \
+> scripts/pnglibconf.dfa.temp
+mv scripts/pnglibconf.dfa.temp scripts/pnglibconf.dfa
+
+# build the libpng library.
+autoreconf -f -i
+./configure --with-libpng-prefix=OSS_FUZZ_
+make -j$(nproc) clean
+make -j$(nproc) libpng16.la
+
+# build libpng_read_fuzzer.
+$CXX $CXXFLAGS -std=c++11 -I. \
+     $SRC/libpng/contrib/oss-fuzz/libpng_read_fuzzer.cc \
+     -o $OUT/libpng_read_fuzzer \
+     -lFuzzingEngine .libs/libpng16.a -lz
+
+# add seed corpus.
+find $SRC/libpng -name "*.png" | grep -v crashers | \
+     xargs zip $OUT/libpng_read_fuzzer_seed_corpus.zip
+
+cp $SRC/libpng/contrib/oss-fuzz/*.dict \
+     $SRC/libpng/contrib/oss-fuzz/*.options $OUT/
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/libpng_read_fuzzer.cc b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/libpng_read_fuzzer.cc
new file mode 100755
index 0000000..7b30550
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/libpng_read_fuzzer.cc
@@ -0,0 +1,190 @@
+
+// libpng_read_fuzzer.cc
+// Copyright 2017-2018 Glenn Randers-Pehrson
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that may
+// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
+
+// Last changed in libpng 1.6.35 [July 15, 2018]
+
+// The modifications in 2017 by Glenn Randers-Pehrson include
+// 1. addition of a PNG_CLEANUP macro,
+// 2. setting the option to ignore ADLER32 checksums,
+// 3. adding "#include <string.h>" which is needed on some platforms
+//    to provide memcpy().
+// 4. adding read_end_info() and creating an end_info structure.
+// 5. adding calls to png_set_*() transforms commonly used by browsers.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#define PNG_CLEANUP \
+  if(png_handler.png_ptr) \
+  { \
+    if (png_handler.row_ptr) \
+      png_free(png_handler.png_ptr, png_handler.row_ptr); \
+    if (png_handler.end_info_ptr) \
+      png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
+        &png_handler.end_info_ptr); \
+    else if (png_handler.info_ptr) \
+      png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
+        nullptr); \
+    else \
+      png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
+    png_handler.png_ptr = nullptr; \
+    png_handler.row_ptr = nullptr; \
+    png_handler.info_ptr = nullptr; \
+    png_handler.end_info_ptr = nullptr; \
+  }
+
+struct BufState {
+  const uint8_t* data;
+  size_t bytes_left;
+};
+
+struct PngObjectHandler {
+  png_infop info_ptr = nullptr;
+  png_structp png_ptr = nullptr;
+  png_infop end_info_ptr = nullptr;
+  png_voidp row_ptr = nullptr;
+  BufState* buf_state = nullptr;
+
+  ~PngObjectHandler() {
+    if (row_ptr)
+      png_free(png_ptr, row_ptr);
+    if (end_info_ptr)
+      png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
+    else if (info_ptr) 
+      png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+    else
+      png_destroy_read_struct(&png_ptr, nullptr, nullptr);
+    delete buf_state;
+  }
+};
+
+void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
+  BufState* buf_state = static_cast<BufState*>(png_get_io_ptr(png_ptr));
+  if (length > buf_state->bytes_left) {
+    png_error(png_ptr, "read error");
+  }
+  memcpy(data, buf_state->data, length);
+  buf_state->bytes_left -= length;
+  buf_state->data += length;
+}
+
+static const int kPngHeaderSize = 8;
+
+// Entry point for LibFuzzer.
+// Roughly follows the libpng book example:
+// http://www.libpng.org/pub/png/book/chapter13.html
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < kPngHeaderSize) {
+    return 0;
+  }
+
+  std::vector<unsigned char> v(data, data + size);
+  if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
+    // not a PNG.
+    return 0;
+  }
+
+  PngObjectHandler png_handler;
+  png_handler.png_ptr = nullptr;
+  png_handler.row_ptr = nullptr;
+  png_handler.info_ptr = nullptr;
+  png_handler.end_info_ptr = nullptr;
+
+  png_handler.png_ptr = png_create_read_struct
+    (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+  if (!png_handler.png_ptr) {
+    return 0;
+  }
+
+  png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
+  if (!png_handler.info_ptr) {
+    PNG_CLEANUP
+    return 0;
+  }
+
+  png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
+  if (!png_handler.end_info_ptr) {
+    PNG_CLEANUP
+    return 0;
+  }
+
+  png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+#ifdef PNG_IGNORE_ADLER32
+  png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
+#endif
+
+  // Setting up reading from buffer.
+  png_handler.buf_state = new BufState();
+  png_handler.buf_state->data = data + kPngHeaderSize;
+  png_handler.buf_state->bytes_left = size - kPngHeaderSize;
+  png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
+  png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
+
+  if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
+    PNG_CLEANUP
+    return 0;
+  }
+
+  // Reading.
+  png_read_info(png_handler.png_ptr, png_handler.info_ptr);
+
+  // reset error handler to put png_deleter into scope.
+  if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
+    PNG_CLEANUP
+    return 0;
+  }
+
+  png_uint_32 width, height;
+  int bit_depth, color_type, interlace_type, compression_type;
+  int filter_type;
+
+  if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width,
+                    &height, &bit_depth, &color_type, &interlace_type,
+                    &compression_type, &filter_type)) {
+    PNG_CLEANUP
+    return 0;
+  }
+
+  // This is going to be too slow.
+  if (width && height > 100000000 / width) {
+    PNG_CLEANUP
+    return 0;
+  }
+
+  // Set several transforms that browsers typically use:
+  png_set_gray_to_rgb(png_handler.png_ptr);
+  png_set_expand(png_handler.png_ptr);
+  png_set_packing(png_handler.png_ptr);
+  png_set_scale_16(png_handler.png_ptr);
+  png_set_tRNS_to_alpha(png_handler.png_ptr);
+
+  int passes = png_set_interlace_handling(png_handler.png_ptr);
+
+  png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
+
+  png_handler.row_ptr = png_malloc(
+      png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr,
+                                            png_handler.info_ptr));
+
+  for (int pass = 0; pass < passes; ++pass) {
+    for (png_uint_32 y = 0; y < height; ++y) {
+      png_read_row(png_handler.png_ptr,
+                   static_cast<png_bytep>(png_handler.row_ptr), nullptr);
+    }
+  }
+
+  png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
+
+  PNG_CLEANUP
+  return 0;
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/libpng_read_fuzzer.options b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/libpng_read_fuzzer.options
new file mode 100755
index 0000000..2005291
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/libpng_read_fuzzer.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+dict = png.dict
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/png.dict b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/png.dict
new file mode 100755
index 0000000..3a8a113
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/oss-fuzz/png.dict
@@ -0,0 +1,39 @@
+#
+# AFL dictionary for PNG images
+# -----------------------------
+#
+# Just the basic, standard-originating sections; does not include vendor
+# extensions.
+#
+# Created by Michal Zalewski <lcamtuf@google.com>
+#
+
+header_png="\x89PNG\x0d\x0a\x1a\x0a"
+
+section_IDAT="IDAT"
+section_IEND="IEND"
+section_IHDR="IHDR"
+section_PLTE="PLTE"
+section_bKGD="bKGD"
+section_cHRM="cHRM"
+section_eXIf="eXIf"
+section_fRAc="fRAc"
+section_gAMA="gAMA"
+section_gIFg="gIFg"
+section_gIFt="gIFt"
+section_gIFx="gIFx"
+section_hIST="hIST"
+section_iCCP="iCCP"
+section_iTXt="iTXt"
+section_oFFs="oFFs"
+section_pCAL="pCAL"
+section_pHYs="pHYs"
+section_sBIT="sBIT"
+section_sCAL="sCAL"
+section_sPLT="sPLT"
+section_sRGB="sRGB"
+section_sTER="sTER"
+section_tEXt="tEXt"
+section_tIME="tIME"
+section_tRNS="tRNS"
+section_zTXt="zTXt"
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/README b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/README
new file mode 100755
index 0000000..e17fe35
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/README b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/README
new file mode 100755
index 0000000..fa979fc
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/README
@@ -0,0 +1,10 @@
+This demonstrates the use of PNG_USER_CONFIG, pngusr.h and pngusr.dfa
+
+The makefile builds a minimal read-only decoder with embedded libpng
+and zlib.
+
+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.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/makefile b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/makefile
new file mode 100755
index 0000000..4acf3c1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/makefile
@@ -0,0 +1,151 @@
+# Makefile for PngMinus (pngm2pnm)
+# Linux / Unix
+
+#CC=cc
+CC=gcc
+LD=$(CC)
+
+# If awk fails try
+# make AWK=nawk
+
+# 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=
+
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/pngminus
+
+# 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)
+
+PROGSRCS= pngm2pnm$(C)
+PROGHDRS=
+PROGDOCS=
+PROGOBJS= pngm2pnm$(O)
+
+OBJS    = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
+
+# implicit make rules -------------------------------------------------------
+
+# note: dependencies do not work on implicit rule lines
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+# dependencies
+
+all: pngm2pnm$(E)
+
+pngm2pnm$(E): $(OBJS)
+	$(LD) -o pngm2pnm$(E) $(OBJS)
+
+# 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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/pngusr.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/pngusr.dfa
new file mode 100755
index 0000000..4f40272
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/pngusr.h b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/pngusr.h
new file mode 100755
index 0000000..cbd7890
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/decoder/pngusr.h
@@ -0,0 +1,23 @@
+/* minrdpngconf.h: headers to make a minimal png-read-only library
+ *
+ * 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
+ * and license in png.h
+ *
+ * Derived from pngcrush.h, Copyright 1998-2007, Glenn Randers-Pehrson
+ */
+
+#ifndef MINRDPNGCONF_H
+#define MINRDPNGCONF_H
+
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
+
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
+
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
+
+#endif /* MINRDPNGCONF_H */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/README b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/README
new file mode 100755
index 0000000..d8d9a64
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/README
@@ -0,0 +1,10 @@
+This demonstrates the use of PNG_USER_CONFIG and pngusr.h
+
+The makefile builds a minimal write-only encoder with embedded libpng
+and zlib.
+
+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.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/makefile b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/makefile
new file mode 100755
index 0000000..41b205c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/makefile
@@ -0,0 +1,150 @@
+# Makefile for PngMinus (pnm2pngm)
+# Linux / Unix
+
+#CC=cc
+CC=gcc
+LD=$(CC)
+
+# If awk fails try
+# make AWK=nawk
+
+# 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=
+
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/pngminus
+
+# 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)
+
+PROGSRCS= pnm2pngm$(C)
+PROGHDRS=
+PROGDOCS=
+PROGOBJS= pnm2pngm$(O)
+
+OBJS    = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+# dependencies
+
+all: pnm2pngm$(E)
+
+pnm2pngm$(E): $(OBJS)
+	$(LD) -o pnm2pngm$(E) $(OBJS)
+
+# 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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/pngusr.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/pngusr.dfa
new file mode 100755
index 0000000..1fc24f3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/pngusr.h b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/pngusr.h
new file mode 100755
index 0000000..997d44f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/encoder/pngusr.h
@@ -0,0 +1,23 @@
+/* minwrpngconf.h: headers to make a minimal png-write-only library
+ *
+ * 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
+ * and license in png.h
+ *
+ * Derived from pngcrush.h, Copyright 1998-2007, Glenn Randers-Pehrson
+ */
+
+#ifndef MINWRPNGCONF_H
+#define MINWRPNGCONF_H
+
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
+
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
+
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
+
+#endif /* MINWRPNGCONF_H */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/README b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/README
new file mode 100755
index 0000000..faa8356
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/README
@@ -0,0 +1,15 @@
+This demonstrates the use of PNG_USER_CONFIG and pngusr.h
+
+The makefile builds a minimal read-only progressive decoder with
+embedded libpng, zlib and your system's X library.
+
+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 ZLIBSRC=directory
+
+If you prefer to use the shared libraries, go to contrib/gregbook
+and build the rpng2-x application there.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/makefile b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/makefile
new file mode 100755
index 0000000..b625199
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/makefile
@@ -0,0 +1,166 @@
+# Makefile for PngMinus (rpng2)
+# Linux / Unix
+
+#CC=cc
+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)
+
+#XINC = -I/usr/openwin/include		# Sun workstations (OpenWindows)
+#XLIB = -L/usr/openwin/lib -lX11
+
+XINC = -I/usr/X11R6/include		# new X distributions (X.org, etc.)
+XLIB = -L/usr/X11R6/lib -lX11
+#XLIB = -L/usr/X11R6/lib64 -lX11	# e.g., Red Hat on AMD64
+
+#XINC = -I/usr/local/include   		# FreeBSD
+#XLIB = -L/usr/local/lib -lX11
+
+#LIBS = $(XLIB)
+LIBS = $(XLIB) -lm                      #platforms that need libm
+
+CPPFLAGS=-I. $(XINC) -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
+
+C=.c
+O=.o
+L=.a
+E=
+
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/gregbook
+
+# 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)
+
+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):
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+# dependencies
+
+all: $(PROGDOCS) rpng2-x$(E)
+
+rpng2-x$(E): $(OBJS)
+	$(LD) -o rpng2-x$(E) $(OBJS) $(LIBS)
+
+# 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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/pngusr.dfa b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/pngusr.dfa
new file mode 100755
index 0000000..ac297f4
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/pngusr.h b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/pngusr.h
new file mode 100755
index 0000000..80db3bb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminim/preader/pngusr.h
@@ -0,0 +1,23 @@
+/* minrdpngconf.h: headers to make a minimal png-read-only library
+ *
+ * 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
+ * and license in png.h
+ *
+ * Derived from pngcrush.h, Copyright 1998-2007, Glenn Randers-Pehrson
+ */
+
+#ifndef MINPRDPNGCONF_H
+#define MINPRDPNGCONF_H
+
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
+
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
+
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
+
+#endif /* MINPRDPNGCONF_H */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/CHANGES.txt b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/CHANGES.txt
new file mode 100755
index 0000000..4e64e5e
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/CHANGES.txt
@@ -0,0 +1,13 @@
+
+pnm2png / png2pnm --- conversion from PBM/PGM/PPM-file to PNG-file
+copyright (C) 1999-2019 by Willem van Schaik <willem at schaik dot com>
+
+version 1.0 - 1999.10.15 - First version.
+        1.1 - 2015.07.29 - Fixed leaks (Glenn Randers-Pehrson)
+        1.2 - 2017.04.22 - Add buffer-size check
+        1.3 - 2017.08.24 - Fix potential overflow in buffer-size check
+                           (Glenn Randers-Pehrson)
+        1.4 - 2017.08.28 - Add PNGMINUS_UNUSED (Christian Hesse)
+        1.5 - 2018.08.05 - Fix buffer overflow in tokenizer (Cosmin Truta)
+        1.6 - 2018.08.05 - Improve portability and fix style (Cosmin Truta)
+        1.7 - 2019.01.22 - Change license to MIT (Willem van Schaik)
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/CMakeLists.txt b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/CMakeLists.txt
new file mode 100755
index 0000000..1f815a5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.1)
+cmake_policy(VERSION 3.1)
+
+project(PNGMINUS C)
+
+option(PNGMINUS_USE_STATIC_LIBRARIES "Use the static library builds" ON)
+
+# libpng
+add_subdirectory(../.. libpng)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/libpng)
+if(PNGMINUS_USE_STATIC_LIBRARIES)
+  set(PNGMINUS_PNG_LIBRARY png_static)
+else()
+  set(PNGMINUS_PNG_LIBRARY png)
+endif()
+
+# png2pnm
+add_executable(png2pnm png2pnm.c)
+target_link_libraries(png2pnm ${PNGMINUS_PNG_LIBRARY})
+
+# pnm2png
+add_executable(pnm2png pnm2png.c)
+target_link_libraries(pnm2png ${PNGMINUS_PNG_LIBRARY})
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/LICENSE.txt b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/LICENSE.txt
new file mode 100755
index 0000000..00878a9
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/LICENSE.txt
@@ -0,0 +1,22 @@
+
+pnm2png / png2pnm --- conversion from PBM/PGM/PPM-file to PNG-file
+
+copyright (C) 1999-2019 by Willem van Schaik <willem at schaik dot com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The software is provided "as is", without warranty of any kind, express or
+implied, including but not limited to the warranties of merchantability,
+fitness for a particular purpose and noninfringement. In no event shall the
+authors or copyight holders be liable for any claim, damages or other
+liability, whether in an action of contract, tort or otherwise, arising from,
+out of or in connection with the software or the use or other dealings in the
+software.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/Makefile b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/Makefile
new file mode 100755
index 0000000..4bba079
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/Makefile
@@ -0,0 +1,62 @@
+# Makefile for PngMinus (png2pnm and pnm2png)
+# Linux / Unix
+
+#CC = cc
+CC = gcc
+LD = $(CC)
+
+RM = rm -f
+
+PNGINC = -I../..
+PNGLIB_SHARED = -L../.. -lpng
+PNGLIB_STATIC = ../../libpng.a
+
+# Uncomment the following if you have a custom zlib build at ../../../zlib
+#ZINC = -I../../../zlib
+#ZLIB_SHARED = -L../../../zlib -lz
+#ZLIB_STATIC = ../../../zlib/libz.a
+
+# Use the system zlib otherwise
+ZLIB_SHARED = -lz
+ZLIB_STATIC = -lz
+
+CPPFLAGS = $(PNGINC) $(ZINC)
+CFLAGS =
+LDFLAGS =
+LIBS_SHARED = $(PNGLIB_SHARED) $(ZLIB_SHARED)
+LIBS_STATIC = $(PNGLIB_STATIC) $(ZLIB_STATIC)
+
+EXEEXT =
+#EXEEXT = .exe
+
+# dependencies
+
+all: png2pnm$(EXEEXT) pnm2png$(EXEEXT) png2pnm-static$(EXEEXT) pnm2png-static$(EXEEXT)
+
+png2pnm.o: png2pnm.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) png2pnm.c
+
+pnm2png.o: pnm2png.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) pnm2png.c
+
+png2pnm$(EXEEXT): png2pnm.o
+	$(LD) $(LDFLAGS) -o png2pnm$(EXEEXT) png2pnm.o $(LIBS_SHARED) -lm
+
+pnm2png$(EXEEXT): pnm2png.o
+	$(LD) $(LDFLAGS) -o pnm2png$(EXEEXT) pnm2png.o $(LIBS_SHARED) -lm
+
+png2pnm-static$(EXEEXT): png2pnm.o
+	$(LD) $(LDFLAGS) -o png2pnm-static$(EXEEXT) png2pnm.o $(LIBS_STATIC) -lm
+
+pnm2png-static$(EXEEXT): pnm2png.o
+	$(LD) $(LDFLAGS) -o pnm2png-static$(EXEEXT) pnm2png.o $(LIBS_STATIC) -lm
+
+clean:
+	$(RM) png2pnm.o
+	$(RM) pnm2png.o
+	$(RM) png2pnm$(EXEEXT)
+	$(RM) pnm2png$(EXEEXT)
+	$(RM) png2pnm-static$(EXEEXT)
+	$(RM) pnm2png-static$(EXEEXT)
+
+# End of makefile for png2pnm / pnm2png
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/README.txt b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/README.txt
new file mode 100755
index 0000000..f7f6ecb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/README.txt
@@ -0,0 +1,120 @@
+PngMinus
+--------
+(copyright Willem van Schaik, 1999-2019)
+
+
+
+Some history
+------------
+Soon after the creation of PNG in 1995, the need was felt for a set of
+pnmtopng / pngtopnm utilities. Independently Alexander Lehmann and I
+(Willem van Schaik) started such a project. Luckily we discovered this
+and merged the two, which later became part of NetPBM, available from
+SourceForge.
+
+These two utilities have many, many options and make use of most of the
+features of PNG, like gamma, alpha, sbit, text-chunks, etc. This makes
+the utilities quite complex and by now not anymore very maintainable.
+When we wrote these programs, libpng was still in an early stage.
+Therefore, lots of the functionality that we put in our software can now
+be done using transform-functions in libpng.
+
+Finally, to compile these programs, you need to have installed and
+compiled three libraries: libpng, zlib and netpbm. Especially the latter
+makes the whole setup a bit bulky. But that's unavoidable given the many
+features of pnmtopng.
+
+
+What now (1999)
+---------------
+At this moment libpng is in a very stable state and can do much of the
+work done in pnmtopng. Also, pnmtopng needs to be upgraded to the new
+interface of libpng. Hence, it is time for a rewrite from the ground up
+of pnmtopng and pngtopnm. This will happen in the near future (stay
+tuned). The new package will get a different name to distinguish it from
+the old one: PngPlus.
+
+To experiment a bit with the new interface of libpng, I started off with
+a small prototype that contains only the basic functionality. It doesn't
+have any of the options to read or write special chunks and it will do
+no gamma correction. But this makes it also a simple program that is
+quite easy to understand and can serve well as a template for other
+software developments. By now there are of course a couple of programs,
+like Greg Roelofs' rpng/wpng, that can be used just as good.
+
+
+Can and can not
+---------------
+As this is the small brother of the future PngPlus, I called this fellow
+PngMinus. Because I started this development in good-old Turbo-C, I
+avoided the use the netpbm library, which requires DOS extenders. Again,
+another reason to call it PngMinus (minus netpbm :-). So, part of the
+program are some elementary routines to read / write pgm- and ppm-files.
+It does not handle B&W pbm-files, but instead you could do pgm with bit-
+depth 1.
+
+The downside of this approach is that you can not use them on images
+that require blocks of memory bigger than 64k (the DOS version). For
+larger images you will get an out-of-memory error.
+
+As said before, PngMinus doesn't correct for gamma. When reading
+png-files you can do this just as well by piping the output of png2pnm
+to pnmgamma, one of the standard PbmPlus tools. This same scenario will
+most probably also be followed in the full-blown future PngPlus, with
+the addition of course of the possibility to create gamma-chunks when
+writing png-files.
+
+On the other hand it supports alpha-channels. When reading a png-image
+you can write the alpha-channel into a pgm-file. And when creating an
+RGB+A png-image, you just combine a ppm-file with a corresponding
+pgm-file containing the alpha-channel. When reading, transparency chunks
+are converted into an alpha-channel and from there on treated the same
+way.
+
+Finally you can opt for writing ascii or binary pgm- and ppm-files. When
+the bit-depth is 16, the format will always be ascii.
+
+
+Using it
+--------
+To distinguish them from pnmtopng and PngPlus, the utilities are named
+png2pnm and pnm2png (2 instead of to). The input- and output-files can
+be given as parameters or through redirection. Therefore the programs
+can be part of a pipe.
+
+To list the options type "png2pnm -h" or "pnm2png -h".
+
+
+Just like Scandinavian furniture
+--------------------------------
+You have to put it together yourself. I developed the software on MS-DOS
+with Turbo-C 3.0 and RedHat Linux 4.2 with gcc. In both cases I used
+libpng-1.0.4 and zlib-1.1.3. By now (2019) it is twenty years later and
+more current versions are OK.
+
+The makefile assumes that the libpng libraries can be found in ../.. and
+libz in ../../../zlib. But you can change this to for example ../libpng
+and ../zlib. The makefile creates two versions of each program, one with
+static library support and the other using shared libraries.
+
+If you create a ../pngsuite directory and then store the basn####.png
+files from PngSuite (http://www.schaik.com/pngsuite/) in there, you can
+test the proper functioning of PngMinus by running pngminus.sh.
+
+
+Warranty
+-------
+Please, remember that this was just a small experiment to learn a few
+things. It will have many unforeseen features <vbg> ... who said bugs? Use
+it when you are in need for something simple or when you want a starting
+point for developing your own stuff.
+
+
+The end
+-------
+Willem van Schaik
+mailto:willem at schaik dot com
+http://www.schaik.com/png/
+
+Oct 1999, Jan 2019
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/makevms.com b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/makevms.com
new file mode 100755
index 0000000..00561bc
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/makevms.com
@@ -0,0 +1,92 @@
+$!------------------------------------------------------------------------------
+$! make Contrib programs of libpng under OpenVMS
+$!
+$!
+$! Look for the compiler used
+$!
+$ zlibsrc = "[---.zlib]"
+$ ccopt="/include=(''zlibsrc',[--])"
+$ if f$getsyi("HW_MODEL").ge.1024
+$ then
+$  ccopt = "/prefix=all"+ccopt
+$  comp  = "__decc__=1"
+$  if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$ else
+$  if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs.""
+$   then
+$    if f$trnlnm("SYS").eqs."" then define sys sys$library:
+$    if f$search("SYS$SYSTEM:VAXC.EXE").eqs.""
+$     then
+$      comp  = "__gcc__=1"
+$      CC :== GCC
+$     else
+$      comp = "__vaxc__=1"
+$     endif
+$   else
+$    if f$trnlnm("SYS").eqs."" then define sys decc$library_include:
+$    ccopt = "/decc/prefix=all"+ccopt
+$    comp  = "__decc__=1"
+$  endif
+$ endif
+$ open/write lopt lib.opt
+$ write lopt "[--]libpng.olb/lib"
+$ write lopt "''zlibsrc'libz.olb/lib"
+$ close lopt
+$ open/write xopt x11.opt
+$ write xopt "sys$library:decw$xlibshr.exe/share"
+$ close xopt
+$ write sys$output "Compiling PNG contrib programs ..."
+$   write sys$output "Building pnm2png..."
+$   CALL MAKE pnm2png.OBJ "cc ''CCOPT' pnm2png" -
+	pnm2png.c
+$   call make pnm2png.exe -
+	"LINK pnm2png,lib.opt/opt" -
+	pnm2png.obj
+$   write sys$output "Building png2pnm..."
+$   CALL MAKE png2pnm.OBJ "cc ''CCOPT' png2pnm" -
+	png2pnm.c
+$   call make png2pnm.exe -
+	"LINK png2pnm,lib.opt/opt" -
+	png2pnm.obj
+$ exit
+$!
+$!
+$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
+$ V = 'F$Verify(0)
+$! P1 = What we are trying to make
+$! P2 = Command to make it
+$! P3 - P8  What it depends on
+$
+$ If F$Search(P1) .Eqs. "" Then Goto Makeit
+$ Time = F$CvTime(F$File(P1,"RDT"))
+$arg=3
+$Loop:
+$       Argument = P'arg
+$       If Argument .Eqs. "" Then Goto Exit
+$       El=0
+$Loop2:
+$       File = F$Element(El," ",Argument)
+$       If File .Eqs. " " Then Goto Endl
+$       AFile = ""
+$Loop3:
+$       OFile = AFile
+$       AFile = F$Search(File)
+$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
+$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
+$       Goto Loop3
+$NextEL:
+$       El = El + 1
+$       Goto Loop2
+$EndL:
+$ arg=arg+1
+$ If arg .Le. 8 Then Goto Loop
+$ Goto Exit
+$
+$Makeit:
+$ VV=F$VERIFY(0)
+$ write sys$output P2
+$ 'P2
+$ VV='F$Verify(VV)
+$Exit:
+$ If V Then Set Verify
+$ENDSUBROUTINE
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.bat b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.bat
new file mode 100755
index 0000000..a2798c3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.bat
@@ -0,0 +1,41 @@
+REM -- grayscale
+png2pnm.exe -noraw ..\pngsuite\basn0g01.png basn0g01.pgm
+png2pnm.exe -noraw ..\pngsuite\basn0g02.png basn0g02.pgm
+png2pnm.exe -noraw ..\pngsuite\basn0g04.png basn0g04.pgm
+png2pnm.exe -noraw ..\pngsuite\basn0g08.png basn0g08.pgm
+png2pnm.exe -noraw ..\pngsuite\basn0g16.png basn0g16.pgm
+REM -- full-color
+png2pnm.exe -noraw ..\pngsuite\basn2c08.png basn2c08.ppm
+png2pnm.exe -noraw ..\pngsuite\basn2c16.png basn2c16.ppm
+REM -- paletted
+png2pnm.exe -noraw ..\pngsuite\basn3p01.png basn3p01.ppm
+png2pnm.exe -noraw ..\pngsuite\basn3p02.png basn3p02.ppm
+png2pnm.exe -noraw ..\pngsuite\basn3p04.png basn3p04.ppm
+png2pnm.exe -noraw ..\pngsuite\basn3p08.png basn3p08.ppm
+REM -- gray with alpha-channel
+png2pnm.exe -noraw ..\pngsuite\basn4a08.png basn4a08.pgm
+png2pnm.exe -noraw ..\pngsuite\basn4a16.png basn4a16.pgm
+REM -- color with alpha-channel
+png2pnm.exe -noraw -alpha basn6a08.pgm ..\pngsuite\basn6a08.png basn6a08.ppm
+png2pnm.exe -noraw -alpha basn6a16.pgm ..\pngsuite\basn6a16.png basn6a16.ppm
+REM -- grayscale
+png2pnm.exe -raw ..\pngsuite\basn0g01.png rawn0g01.pgm
+png2pnm.exe -raw ..\pngsuite\basn0g02.png rawn0g02.pgm
+png2pnm.exe -raw ..\pngsuite\basn0g04.png rawn0g04.pgm
+png2pnm.exe -raw ..\pngsuite\basn0g08.png rawn0g08.pgm
+png2pnm.exe -raw ..\pngsuite\basn0g16.png rawn0g16.pgm
+REM -- full-color
+png2pnm.exe -raw ..\pngsuite\basn2c08.png rawn2c08.ppm
+png2pnm.exe -raw ..\pngsuite\basn2c16.png rawn2c16.ppm
+REM -- paletted
+png2pnm.exe -raw ..\pngsuite\basn3p01.png rawn3p01.ppm
+png2pnm.exe -raw ..\pngsuite\basn3p02.png rawn3p02.ppm
+png2pnm.exe -raw ..\pngsuite\basn3p04.png rawn3p04.ppm
+png2pnm.exe -raw ..\pngsuite\basn3p08.png rawn3p08.ppm
+REM -- gray with alpha-channel
+png2pnm.exe -raw ..\pngsuite\basn4a08.png rawn4a08.pgm
+png2pnm.exe -raw ..\pngsuite\basn4a16.png rawn4a16.pgm
+REM -- color with alpha-channel
+png2pnm.exe -noraw -alpha rawn6a08.pgm ..\pngsuite\basn6a08.png rawn6a08.ppm
+png2pnm.exe -noraw -alpha rawn6a16.pgm ..\pngsuite\basn6a16.png rawn6a16.ppm
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.c b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.c
new file mode 100755
index 0000000..5fef7ed
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.c
@@ -0,0 +1,427 @@
+/*
+ *  png2pnm.c --- conversion from PNG-file to PGM/PPM-file
+ *  copyright (C) 1999-2019 by Willem van Schaik <willem at schaik dot com>
+ *
+ *  This software is released under the MIT license. For conditions of
+ *  distribution and use, see the LICENSE file part of this package.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifndef BOOL
+#define BOOL unsigned char
+#endif
+#ifndef TRUE
+#define TRUE (BOOL) 1
+#endif
+#ifndef FALSE
+#define FALSE (BOOL) 0
+#endif
+
+/* make png2pnm verbose so we can find problems (needs to be before png.h) */
+#ifndef PNG_DEBUG
+#define PNG_DEBUG 0
+#endif
+
+#include "png.h"
+
+/* function prototypes */
+
+int main (int argc, char *argv[]);
+void usage ();
+BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
+              BOOL raw, BOOL alpha);
+
+/*
+ *  main
+ */
+
+int main (int argc, char *argv[])
+{
+  FILE *fp_rd = stdin;
+  FILE *fp_wr = stdout;
+  FILE *fp_al = NULL;
+  BOOL raw = TRUE;
+  BOOL alpha = FALSE;
+  int argi;
+
+  for (argi = 1; argi < argc; argi++)
+  {
+    if (argv[argi][0] == '-')
+    {
+      switch (argv[argi][1])
+      {
+        case 'n':
+          raw = FALSE;
+          break;
+        case 'r':
+          raw = TRUE;
+          break;
+        case 'a':
+          alpha = TRUE;
+          argi++;
+          if ((fp_al = fopen (argv[argi], "wb")) == NULL)
+          {
+            fprintf (stderr, "PNM2PNG\n");
+            fprintf (stderr, "Error:  cannot create alpha-channel file %s\n",
+                     argv[argi]);
+            exit (1);
+          }
+          break;
+        case 'h':
+        case '?':
+          usage ();
+          exit (0);
+          break;
+        default:
+          fprintf (stderr, "PNG2PNM\n");
+          fprintf (stderr, "Error:  unknown option %s\n", argv[argi]);
+          usage ();
+          exit (1);
+          break;
+      } /* end switch */
+    }
+    else if (fp_rd == stdin)
+    {
+      if ((fp_rd = fopen (argv[argi], "rb")) == NULL)
+      {
+        fprintf (stderr, "PNG2PNM\n");
+        fprintf (stderr, "Error:  file %s does not exist\n", argv[argi]);
+        exit (1);
+      }
+    }
+    else if (fp_wr == stdout)
+    {
+      if ((fp_wr = fopen (argv[argi], "wb")) == NULL)
+      {
+        fprintf (stderr, "PNG2PNM\n");
+        fprintf (stderr, "Error:  cannot create file %s\n", argv[argi]);
+        exit (1);
+      }
+    }
+    else
+    {
+      fprintf (stderr, "PNG2PNM\n");
+      fprintf (stderr, "Error:  too many parameters\n");
+      usage ();
+      exit (1);
+    }
+  } /* end for */
+
+#if defined(O_BINARY) && (O_BINARY != 0)
+  /* set stdin/stdout if required to binary */
+  if (fp_rd == stdin)
+    setmode (fileno (stdin), O_BINARY);
+  if ((raw) && (fp_wr == stdout))
+    setmode (fileno (stdout), O_BINARY);
+#endif
+
+  /* call the conversion program itself */
+  if (png2pnm (fp_rd, fp_wr, fp_al, raw, alpha) == FALSE)
+  {
+    fprintf (stderr, "PNG2PNM\n");
+    fprintf (stderr, "Error:  unsuccessful conversion of PNG-image\n");
+    exit (1);
+  }
+
+  /* close input file */
+  fclose (fp_rd);
+  /* close output file */
+  fclose (fp_wr);
+  /* close alpha file */
+  if (alpha)
+    fclose (fp_al);
+
+  return 0;
+}
+
+/*
+ *  usage
+ */
+
+void usage ()
+{
+  fprintf (stderr, "PNG2PNM\n");
+  fprintf (stderr, "   by Willem van Schaik, 1999\n");
+  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, "   -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, "   -h | -?  print this help-information\n");
+}
+
+/*
+ *  png2pnm
+ */
+
+BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
+              BOOL raw, BOOL alpha)
+{
+  png_struct    *png_ptr = NULL;
+  png_info      *info_ptr = NULL;
+  png_byte      buf[8];
+  png_byte      *png_pixels = NULL;
+  png_byte      **row_pointers = NULL;
+  png_byte      *pix_ptr = NULL;
+  png_uint_32   row_bytes;
+
+  png_uint_32   width;
+  png_uint_32   height;
+  int           bit_depth;
+  int           channels;
+  int           color_type;
+  int           alpha_present;
+  int           row, col;
+  int           ret;
+  int           i;
+  long          dep_16;
+
+  /* read and check signature in PNG file */
+  ret = fread (buf, 1, 8, png_file);
+  if (ret != 8)
+    return FALSE;
+
+  ret = png_sig_cmp (buf, 0, 8);
+  if (ret != 0)
+    return FALSE;
+
+  /* create png and info structures */
+
+  png_ptr = png_create_read_struct (png_get_libpng_ver(NULL),
+                                    NULL, NULL, NULL);
+  if (!png_ptr)
+    return FALSE; /* out of memory */
+
+  info_ptr = png_create_info_struct (png_ptr);
+  if (!info_ptr)
+  {
+    png_destroy_read_struct (&png_ptr, NULL, NULL);
+    return FALSE; /* out of memory */
+  }
+
+  if (setjmp (png_jmpbuf (png_ptr)))
+  {
+    png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+    return FALSE;
+  }
+
+  /* set up the input control for C streams */
+  png_init_io (png_ptr, png_file);
+  png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */
+
+  /* read the file information */
+  png_read_info (png_ptr, info_ptr);
+
+  /* get size and bit-depth of the PNG-image */
+  png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+                NULL, NULL, NULL);
+
+  /* set-up the transformations */
+
+  /* transform paletted images into full-color rgb */
+  if (color_type == PNG_COLOR_TYPE_PALETTE)
+    png_set_expand (png_ptr);
+  /* expand images to bit-depth 8 (only applicable for grayscale images) */
+  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+    png_set_expand (png_ptr);
+  /* transform transparency maps into full alpha-channel */
+  if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+    png_set_expand (png_ptr);
+
+#ifdef NJET
+  /* downgrade 16-bit images to 8-bit */
+  if (bit_depth == 16)
+    png_set_strip_16 (png_ptr);
+  /* transform grayscale images into full-color */
+  if (color_type == PNG_COLOR_TYPE_GRAY ||
+      color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    png_set_gray_to_rgb (png_ptr);
+  /* only if file has a file gamma, we do a correction */
+  if (png_get_gAMA (png_ptr, info_ptr, &file_gamma))
+    png_set_gamma (png_ptr, (double) 2.2, file_gamma);
+#endif
+
+  /* all transformations have been registered; now update info_ptr data,
+   * get rowbytes and channels, and allocate image memory */
+
+  png_read_update_info (png_ptr, info_ptr);
+
+  /* get the new color-type and bit-depth (after expansion/stripping) */
+  png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+                NULL, NULL, NULL);
+
+  /* check for 16-bit files */
+  if (bit_depth == 16)
+  {
+    raw = FALSE;
+#if defined(O_BINARY) && (O_BINARY != 0)
+    setmode (fileno (pnm_file), O_BINARY);
+#endif
+  }
+
+  /* calculate new number of channels and store alpha-presence */
+  if (color_type == PNG_COLOR_TYPE_GRAY)
+    channels = 1;
+  else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    channels = 2;
+  else if (color_type == PNG_COLOR_TYPE_RGB)
+    channels = 3;
+  else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+    channels = 4;
+  else
+    channels = 0; /* should never happen */
+  alpha_present = (channels - 1) % 2;
+
+  /* check if alpha is expected to be present in file */
+  if (alpha && !alpha_present)
+  {
+    fprintf (stderr, "PNG2PNM\n");
+    fprintf (stderr, "Error:  PNG-file doesn't contain alpha channel\n");
+    exit (1);
+  }
+
+  /* row_bytes is the width x number of channels x (bit-depth / 8) */
+  row_bytes = png_get_rowbytes (png_ptr, info_ptr);
+
+  if ((row_bytes == 0) ||
+      ((size_t) height > (size_t) (-1) / (size_t) row_bytes))
+  {
+    /* too big */
+    png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+    return FALSE;
+  }
+  if ((png_pixels = (png_byte *)
+       malloc ((size_t) row_bytes * (size_t) height)) == NULL)
+  {
+    png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+    return FALSE;
+  }
+
+  if ((row_pointers = (png_byte **)
+       malloc ((size_t) height * sizeof (png_byte *))) == NULL)
+  {
+    png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+    free (png_pixels);
+    return FALSE;
+  }
+
+  /* set the individual row_pointers to point at the correct offsets */
+  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 */
+  png_read_image (png_ptr, row_pointers);
+
+  /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+  png_read_end (png_ptr, info_ptr);
+
+  /* clean up after the read, and free any memory allocated - REQUIRED */
+  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+
+  /* write header of PNM file */
+
+  if ((color_type == PNG_COLOR_TYPE_GRAY) ||
+      (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
+  {
+    fprintf (pnm_file, "%s\n", (raw) ? "P5" : "P2");
+    fprintf (pnm_file, "%d %d\n", (int) width, (int) height);
+    fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L));
+  }
+  else if ((color_type == PNG_COLOR_TYPE_RGB) ||
+           (color_type == PNG_COLOR_TYPE_RGB_ALPHA))
+  {
+    fprintf (pnm_file, "%s\n", (raw) ? "P6" : "P3");
+    fprintf (pnm_file, "%d %d\n", (int) width, (int) height);
+    fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L));
+  }
+
+  /* write header of PGM file with alpha channel */
+
+  if ((alpha) &&
+      ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
+       (color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
+  {
+    fprintf (alpha_file, "%s\n", (raw) ? "P5" : "P2");
+    fprintf (alpha_file, "%d %d\n", (int) width, (int) height);
+    fprintf (alpha_file, "%ld\n", ((1L << (int) bit_depth) - 1L));
+  }
+
+  /* write data to PNM file */
+  pix_ptr = png_pixels;
+
+  for (row = 0; row < (int) height; row++)
+  {
+    for (col = 0; col < (int) width; col++)
+    {
+      for (i = 0; i < (channels - alpha_present); i++)
+      {
+        if (raw)
+        {
+          fputc ((int) *pix_ptr++, pnm_file);
+        }
+        else
+        {
+          if (bit_depth == 16)
+          {
+            dep_16 = (long) *pix_ptr++;
+            fprintf (pnm_file, "%ld ", (dep_16 << 8) + ((long) *pix_ptr++));
+          }
+          else
+          {
+            fprintf (pnm_file, "%ld ", (long) *pix_ptr++);
+          }
+        }
+      }
+      if (alpha_present)
+      {
+        if (!alpha)
+        {
+          pix_ptr++; /* alpha */
+          if (bit_depth == 16)
+            pix_ptr++;
+        }
+        else /* output alpha-channel as pgm file */
+        {
+          if (raw)
+          {
+            fputc ((int) *pix_ptr++, alpha_file);
+          }
+          else
+          {
+            if (bit_depth == 16)
+            {
+              dep_16 = (long) *pix_ptr++;
+              fprintf (alpha_file, "%ld ", (dep_16 << 8) + (long) *pix_ptr++);
+            }
+            else
+            {
+              fprintf (alpha_file, "%ld ", (long) *pix_ptr++);
+            }
+          }
+        }
+      } /* end if alpha_present */
+
+      if (!raw)
+        if (col % 4 == 3)
+          fprintf (pnm_file, "\n");
+    } /* end for col */
+
+    if (!raw)
+      if (col % 4 != 0)
+        fprintf (pnm_file, "\n");
+  } /* end for row */
+
+  if (row_pointers != NULL)
+    free (row_pointers);
+  if (png_pixels != NULL)
+    free (png_pixels);
+
+  return TRUE;
+
+} /* end of source */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.sh b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.sh
new file mode 100755
index 0000000..756126a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/png2pnm.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# -- grayscale
+./png2pnm -noraw ../pngsuite/basn0g01.png basn0g01.pgm
+./png2pnm -noraw ../pngsuite/basn0g02.png basn0g02.pgm
+./png2pnm -noraw ../pngsuite/basn0g04.png basn0g04.pgm
+./png2pnm -noraw ../pngsuite/basn0g08.png basn0g08.pgm
+./png2pnm -noraw ../pngsuite/basn0g16.png basn0g16.pgm
+# -- full-color
+./png2pnm -noraw ../pngsuite/basn2c08.png basn2c08.ppm
+./png2pnm -noraw ../pngsuite/basn2c16.png basn2c16.ppm
+# -- paletted
+./png2pnm -noraw ../pngsuite/basn3p01.png basn3p01.ppm
+./png2pnm -noraw ../pngsuite/basn3p02.png basn3p02.ppm
+./png2pnm -noraw ../pngsuite/basn3p04.png basn3p04.ppm
+./png2pnm -noraw ../pngsuite/basn3p08.png basn3p08.ppm
+# -- gray with alpha-channel
+./png2pnm -noraw ../pngsuite/basn4a08.png basn4a08.pgm
+./png2pnm -noraw ../pngsuite/basn4a16.png basn4a16.pgm
+# -- color with alpha-channel
+./png2pnm -noraw -alpha basn6a08.pgm ../pngsuite/basn6a08.png basn6a08.ppm
+./png2pnm -noraw -alpha basn6a16.pgm ../pngsuite/basn6a16.png basn6a16.ppm
+# -- grayscale
+./png2pnm -raw ../pngsuite/basn0g01.png rawn0g01.pgm
+./png2pnm -raw ../pngsuite/basn0g02.png rawn0g02.pgm
+./png2pnm -raw ../pngsuite/basn0g04.png rawn0g04.pgm
+./png2pnm -raw ../pngsuite/basn0g08.png rawn0g08.pgm
+./png2pnm -raw ../pngsuite/basn0g16.png rawn0g16.pgm
+# -- full-color
+./png2pnm -raw ../pngsuite/basn2c08.png rawn2c08.ppm
+./png2pnm -raw ../pngsuite/basn2c16.png rawn2c16.ppm
+# -- paletted
+./png2pnm -raw ../pngsuite/basn3p01.png rawn3p01.ppm
+./png2pnm -raw ../pngsuite/basn3p02.png rawn3p02.ppm
+./png2pnm -raw ../pngsuite/basn3p04.png rawn3p04.ppm
+./png2pnm -raw ../pngsuite/basn3p08.png rawn3p08.ppm
+# -- gray with alpha-channel
+./png2pnm -raw ../pngsuite/basn4a08.png rawn4a08.pgm
+./png2pnm -raw ../pngsuite/basn4a16.png rawn4a16.pgm
+# -- color with alpha-channel
+./png2pnm -noraw -alpha rawn6a08.pgm ../pngsuite/basn6a08.png rawn6a08.ppm
+./png2pnm -noraw -alpha rawn6a16.pgm ../pngsuite/basn6a16.png rawn6a16.ppm
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pngminus.bat b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pngminus.bat
new file mode 100755
index 0000000..fa60e7d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pngminus.bat
@@ -0,0 +1,4 @@
+make
+call png2pnm.bat
+call pnm2png.bat
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pngminus.sh b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pngminus.sh
new file mode 100755
index 0000000..cc9b4a5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pngminus.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+make
+sh png2pnm.sh
+sh pnm2png.sh
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.bat b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.bat
new file mode 100755
index 0000000..c02b80b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.bat
@@ -0,0 +1,41 @@
+REM -- grayscale
+pnm2png.exe basn0g01.pgm basn0g01.png
+pnm2png.exe basn0g02.pgm basn0g02.png
+pnm2png.exe basn0g04.pgm basn0g04.png
+pnm2png.exe basn0g08.pgm basn0g08.png
+pnm2png.exe basn0g16.pgm basn0g16.png
+REM -- full-color
+pnm2png.exe basn2c08.ppm basn2c08.png
+pnm2png.exe basn2c16.ppm basn2c16.png
+REM -- paletted
+pnm2png.exe basn3p01.ppm basn3p01.png
+pnm2png.exe basn3p02.ppm basn3p02.png
+pnm2png.exe basn3p04.ppm basn3p04.png
+pnm2png.exe basn3p08.ppm basn3p08.png
+REM -- gray with alpha-channel
+pnm2png.exe -alpha basn6a08.pgm basn4a08.pgm basn4a08.png
+pnm2png.exe -alpha basn6a16.pgm basn4a16.pgm basn4a16.png
+REM -- color with alpha-channel
+pnm2png.exe -alpha basn6a08.pgm basn6a08.ppm basn6a08.png
+pnm2png.exe -alpha basn6a16.pgm basn6a16.ppm basn6a16.png
+REM -- grayscale
+pnm2png.exe rawn0g01.pgm rawn0g01.png
+pnm2png.exe rawn0g02.pgm rawn0g02.png
+pnm2png.exe rawn0g04.pgm rawn0g04.png
+pnm2png.exe rawn0g08.pgm rawn0g08.png
+pnm2png.exe rawn0g16.pgm rawn0g16.png
+REM -- full-color
+pnm2png.exe rawn2c08.ppm rawn2c08.png
+pnm2png.exe rawn2c16.ppm rawn2c16.png
+REM -- paletted
+pnm2png.exe rawn3p01.ppm rawn3p01.png
+pnm2png.exe rawn3p02.ppm rawn3p02.png
+pnm2png.exe rawn3p04.ppm rawn3p04.png
+pnm2png.exe rawn3p08.ppm rawn3p08.png
+REM -- gray with alpha-channel
+pnm2png.exe -alpha rawn6a08.pgm rawn4a08.pgm rawn4a08.png
+pnm2png.exe -alpha rawn6a16.pgm rawn4a16.pgm rawn4a16.png
+REM -- color with alpha-channel
+pnm2png.exe -alpha rawn6a08.pgm rawn6a08.ppm rawn6a08.png
+pnm2png.exe -alpha rawn6a16.pgm rawn6a16.ppm rawn6a16.png
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.c b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.c
new file mode 100755
index 0000000..a2c2551
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.c
@@ -0,0 +1,620 @@
+/*
+ *  pnm2png.c --- conversion from PBM/PGM/PPM-file to PNG-file
+ *  copyright (C) 1999-2019 by Willem van Schaik <willem at schaik dot com>
+ *
+ *  This software is released under the MIT license. For conditions of
+ *  distribution and use, see the LICENSE file part of this package.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifndef BOOL
+#define BOOL unsigned char
+#endif
+#ifndef TRUE
+#define TRUE (BOOL) 1
+#endif
+#ifndef FALSE
+#define FALSE (BOOL) 0
+#endif
+
+/* make pnm2png verbose so we can find problems (needs to be before png.h) */
+#ifndef PNG_DEBUG
+#define PNG_DEBUG 0
+#endif
+
+#include "png.h"
+
+/* function prototypes */
+
+int main (int argc, char *argv[]);
+void usage ();
+BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file,
+              BOOL interlace, BOOL alpha);
+void get_token (FILE *pnm_file, char *token_buf, size_t token_buf_size);
+png_uint_32 get_data (FILE *pnm_file, int depth);
+png_uint_32 get_value (FILE *pnm_file, int depth);
+
+/*
+ *  main
+ */
+
+int main (int argc, char *argv[])
+{
+  FILE *fp_rd = stdin;
+  FILE *fp_al = NULL;
+  FILE *fp_wr = stdout;
+  BOOL interlace = FALSE;
+  BOOL alpha = FALSE;
+  int argi;
+
+  for (argi = 1; argi < argc; argi++)
+  {
+    if (argv[argi][0] == '-')
+    {
+      switch (argv[argi][1])
+      {
+        case 'i':
+          interlace = TRUE;
+          break;
+        case 'a':
+          alpha = TRUE;
+          argi++;
+          if ((fp_al = fopen (argv[argi], "rb")) == NULL)
+          {
+            fprintf (stderr, "PNM2PNG\n");
+            fprintf (stderr, "Error:  alpha-channel file %s does not exist\n",
+                     argv[argi]);
+            exit (1);
+          }
+          break;
+        case 'h':
+        case '?':
+          usage ();
+          exit (0);
+          break;
+        default:
+          fprintf (stderr, "PNM2PNG\n");
+          fprintf (stderr, "Error:  unknown option %s\n", argv[argi]);
+          usage ();
+          exit (1);
+          break;
+      } /* end switch */
+    }
+    else if (fp_rd == stdin)
+    {
+      if ((fp_rd = fopen (argv[argi], "rb")) == NULL)
+      {
+        fprintf (stderr, "PNM2PNG\n");
+        fprintf (stderr, "Error:  file %s does not exist\n", argv[argi]);
+        exit (1);
+      }
+    }
+    else if (fp_wr == stdout)
+    {
+      if ((fp_wr = fopen (argv[argi], "wb")) == NULL)
+      {
+        fprintf (stderr, "PNM2PNG\n");
+        fprintf (stderr, "Error:  cannot create PNG-file %s\n", argv[argi]);
+        exit (1);
+      }
+    }
+    else
+    {
+      fprintf (stderr, "PNM2PNG\n");
+      fprintf (stderr, "Error:  too many parameters\n");
+      usage ();
+      exit (1);
+    }
+  } /* end for */
+
+#if defined(O_BINARY) && (O_BINARY != 0)
+  /* set stdin/stdout to binary,
+   * we're reading the PNM always! in binary format
+   */
+  if (fp_rd == stdin)
+    setmode (fileno (stdin), O_BINARY);
+  if (fp_wr == stdout)
+    setmode (fileno (stdout), O_BINARY);
+#endif
+
+  /* call the conversion program itself */
+  if (pnm2png (fp_rd, fp_wr, fp_al, interlace, alpha) == FALSE)
+  {
+    fprintf (stderr, "PNM2PNG\n");
+    fprintf (stderr, "Error:  unsuccessful converting to PNG-image\n");
+    exit (1);
+  }
+
+  /* close input file */
+  fclose (fp_rd);
+  /* close output file */
+  fclose (fp_wr);
+  /* close alpha file */
+  if (alpha)
+    fclose (fp_al);
+
+  return 0;
+}
+
+/*
+ *  usage
+ */
+
+void usage ()
+{
+  fprintf (stderr, "PNM2PNG\n");
+  fprintf (stderr, "   by Willem van Schaik, 1999\n");
+  fprintf (stderr, "Usage:  pnm2png [options] <file>.<pnm> [<file>.png]\n");
+  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, "   -h | -?  print this help-information\n");
+}
+
+/*
+ *  pnm2png
+ */
+
+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;
+  volatile png_uint_32 row_bytes;
+
+  char          type_token[16];
+  char          width_token[16];
+  char          height_token[16];
+  char          maxval_token[16];
+  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;
+  volatile int  bit_depth = 0;
+  int           channels = 0;
+  int           alpha_depth = 0;
+  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;
+
+  /* read header of PNM file */
+
+  get_token (pnm_file, type_token, sizeof (type_token));
+  if (type_token[0] != 'P')
+  {
+    return FALSE;
+  }
+  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, sizeof (width_token));
+    sscanf (width_token, "%lu", &ul_width);
+    width = (png_uint_32) ul_width;
+    get_token (pnm_file, height_token, sizeof (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");
+    return FALSE;
+#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, sizeof (width_token));
+    sscanf (width_token, "%lu", &ul_width);
+    width = (png_uint_32) ul_width;
+    get_token (pnm_file, height_token, sizeof (height_token));
+    sscanf (height_token, "%lu", &ul_height);
+    height = (png_uint_32) ul_height;
+    get_token (pnm_file, maxval_token, sizeof (maxval_token));
+    sscanf (maxval_token, "%lu", &ul_maxval);
+    maxval = (png_uint_32) ul_maxval;
+
+    if (maxval <= 1)
+      bit_depth = 1;
+    else if (maxval <= 3)
+      bit_depth = 2;
+    else if (maxval <= 15)
+      bit_depth = 4;
+    else if (maxval <= 255)
+      bit_depth = 8;
+    else if (maxval <= 65535U)
+      bit_depth = 16;
+    else /* maxval > 65535U */
+      return FALSE;
+  }
+  else if ((type_token[1] == '3') || (type_token[1] == '6'))
+  {
+    raw = (type_token[1] == '6');
+    color_type = PNG_COLOR_TYPE_RGB;
+    get_token (pnm_file, width_token, sizeof (width_token));
+    sscanf (width_token, "%lu", &ul_width);
+    width = (png_uint_32) ul_width;
+    get_token (pnm_file, height_token, sizeof (height_token));
+    sscanf (height_token, "%lu", &ul_height);
+    height = (png_uint_32) ul_height;
+    get_token (pnm_file, maxval_token, sizeof (maxval_token));
+    sscanf (maxval_token, "%lu", &ul_maxval);
+    maxval = (png_uint_32) ul_maxval;
+    if (maxval <= 1)
+      bit_depth = 1;
+    else if (maxval <= 3)
+      bit_depth = 2;
+    else if (maxval <= 15)
+      bit_depth = 4;
+    else if (maxval <= 255)
+      bit_depth = 8;
+    else if (maxval <= 65535U)
+      bit_depth = 16;
+    else /* maxval > 65535U */
+      return FALSE;
+  }
+  else
+  {
+    return FALSE;
+  }
+
+  /* read header of PGM file with alpha channel */
+
+  if (alpha)
+  {
+    if (color_type == PNG_COLOR_TYPE_GRAY)
+      color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+    if (color_type == PNG_COLOR_TYPE_RGB)
+      color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+
+    get_token (alpha_file, type_token, sizeof (type_token));
+    if (type_token[0] != 'P')
+    {
+      return FALSE;
+    }
+    else if ((type_token[1] == '2') || (type_token[1] == '5'))
+    {
+      alpha_raw = (type_token[1] == '5');
+      get_token (alpha_file, width_token, sizeof (width_token));
+      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, sizeof (height_token));
+      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, sizeof (maxval_token));
+      sscanf (maxval_token, "%lu", &ul_maxval);
+      maxval = (png_uint_32) ul_maxval;
+      if (maxval <= 1)
+        alpha_depth = 1;
+      else if (maxval <= 3)
+        alpha_depth = 2;
+      else if (maxval <= 15)
+        alpha_depth = 4;
+      else if (maxval <= 255)
+        alpha_depth = 8;
+      else if (maxval <= 65535U)
+        alpha_depth = 16;
+      else /* maxval > 65535U */
+        return FALSE;
+      if (alpha_depth != bit_depth)
+        return FALSE;
+    }
+    else
+    {
+      return FALSE;
+    }
+  } /* end if alpha */
+
+  /* calculate the number of channels and store alpha-presence */
+  if (color_type == PNG_COLOR_TYPE_GRAY)
+    channels = 1;
+  else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    channels = 2;
+  else if (color_type == PNG_COLOR_TYPE_RGB)
+    channels = 3;
+  else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+    channels = 4;
+#if 0
+  else
+    channels = 0; /* cannot happen */
+#endif
+
+  alpha_present = (channels - 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 ((row_bytes == 0) ||
+      ((size_t) height > (size_t) (-1) / (size_t) row_bytes))
+  {
+    /* too big */
+    return FALSE;
+  }
+  if ((png_pixels = (png_byte *)
+       malloc ((size_t) row_bytes * (size_t) height)) == NULL)
+  {
+    /* out of memory */
+    return FALSE;
+  }
+
+  /* read data from PNM file */
+  pix_ptr = png_pixels;
+
+  for (row = 0; row < (int) height; row++)
+  {
+#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 (col = 0; col < (int) width; col++)
+      {
+        for (i = 0; i < (channels - alpha_present); i++)
+        {
+          if (raw)
+          {
+            *pix_ptr++ = get_data (pnm_file, bit_depth);
+          }
+          else
+          {
+            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);
+            }
+            else
+            {
+              tmp16 = get_value (alpha_file, bit_depth);
+              *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF);
+              *pix_ptr++ = (png_byte) (tmp16 & 0xFF);
+            }
+          }
+        } /* end if alpha */
+      } /* end if packed_bitmap */
+    } /* end for col */
+  } /* end for row */
+
+  /* prepare the standard PNG structures */
+  png_ptr = png_create_write_struct (png_get_libpng_ver(NULL),
+                                     NULL, NULL, NULL);
+  if (!png_ptr)
+  {
+    free (png_pixels);
+    return FALSE;
+  }
+  info_ptr = png_create_info_struct (png_ptr);
+  if (!info_ptr)
+  {
+    png_destroy_write_struct (&png_ptr, NULL);
+    free (png_pixels);
+    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
+
+  if (setjmp (png_jmpbuf (png_ptr)))
+  {
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    free (png_pixels);
+    return FALSE;
+  }
+
+  /* initialize the png structure */
+  png_init_io (png_ptr, png_file);
+
+  /* we're going to write more or less the same PNG as the input file */
+  png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth, color_type,
+                (!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,
+                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+  /* write the file header information */
+  png_write_info (png_ptr, info_ptr);
+
+  /* if needed we will allocate memory for an new array of row-pointers */
+  if (row_pointers == NULL)
+  {
+    if ((row_pointers = (png_byte **)
+         malloc (height * sizeof (png_byte *))) == NULL)
+    {
+      png_destroy_write_struct (&png_ptr, &info_ptr);
+      free (png_pixels);
+      return FALSE;
+    }
+  }
+
+  /* set the individual row_pointers to point at the correct offsets */
+  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 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, &info_ptr);
+
+  if (row_pointers != NULL)
+    free (row_pointers);
+  if (png_pixels != NULL)
+    free (png_pixels);
+
+  return TRUE;
+} /* end of pnm2png */
+
+/*
+ * get_token - gets the first string after whitespace
+ */
+
+void get_token (FILE *pnm_file, char *token_buf, size_t token_buf_size)
+{
+  size_t i = 0;
+  int ret;
+
+  /* remove white-space and comment lines */
+  do
+  {
+    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_buf[i] = (char) ret;
+  }
+  while ((ret == '\n') || (ret == '\r') || (ret == ' '));
+
+  /* read string */
+  do
+  {
+    ret = fgetc (pnm_file);
+    if (ret == EOF) break;
+    if (++i == token_buf_size - 1) break;
+    token_buf[i] = (char) ret;
+  }
+  while ((ret != '\n') && (ret != '\r') && (ret != ' '));
+
+  token_buf[i] = '\0';
+
+  return;
+}
+
+/*
+ *  get_data - takes first byte and converts into next pixel value,
+ *             taking as much bits as defined by bit-depth and
+ *             using the bit-depth to fill up a byte (0Ah -> AAh)
+ */
+
+png_uint_32 get_data (FILE *pnm_file, int depth)
+{
+  static int bits_left = 0;
+  static int old_value = 0;
+  static int mask = 0;
+  int i;
+  png_uint_32 ret_value;
+
+  if (mask == 0)
+    for (i = 0; i < depth; i++)
+      mask = (mask >> 1) | 0x80;
+
+  if (bits_left <= 0)
+  {
+    old_value = fgetc (pnm_file);
+    bits_left = 8;
+  }
+
+  ret_value = old_value & mask;
+  for (i = 1; i < (8 / depth); i++)
+    ret_value = ret_value || (ret_value >> depth);
+
+  old_value = (old_value << depth) & 0xFF;
+  bits_left -= depth;
+
+  return ret_value;
+}
+
+/*
+ *  get_value - takes first (numeric) string and converts into number,
+ *              using the bit-depth to fill up a byte (0Ah -> AAh)
+ */
+
+png_uint_32 get_value (FILE *pnm_file, int depth)
+{
+  static png_uint_32 mask = 0;
+  char token[16];
+  unsigned long ul_ret_value;
+  png_uint_32 ret_value;
+  int i = 0;
+
+  if (mask == 0)
+    for (i = 0; i < depth; i++)
+      mask = (mask << 1) | 0x01;
+
+  get_token (pnm_file, token, sizeof (token));
+  sscanf (token, "%lu", &ul_ret_value);
+  ret_value = (png_uint_32) ul_ret_value;
+
+  ret_value &= mask;
+
+  if (depth < 8)
+    for (i = 0; i < (8 / depth); i++)
+      ret_value = (ret_value << depth) || ret_value;
+
+  return ret_value;
+}
+
+/* end of source */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.sh b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.sh
new file mode 100755
index 0000000..2ad17a6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngminus/pnm2png.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# -- grayscale
+./pnm2png basn0g01.pgm basn0g01.png
+./pnm2png basn0g02.pgm basn0g02.png
+./pnm2png basn0g04.pgm basn0g04.png
+./pnm2png basn0g08.pgm basn0g08.png
+./pnm2png basn0g16.pgm basn0g16.png
+# -- full-color
+./pnm2png basn2c08.ppm basn2c08.png
+./pnm2png basn2c16.ppm basn2c16.png
+# -- paletted
+./pnm2png basn3p01.ppm basn3p01.png
+./pnm2png basn3p02.ppm basn3p02.png
+./pnm2png basn3p04.ppm basn3p04.png
+./pnm2png basn3p08.ppm basn3p08.png
+# -- gray with alpha-channel
+./pnm2png -alpha basn6a08.pgm basn4a08.pgm basn4a08.png
+./pnm2png -alpha basn6a16.pgm basn4a16.pgm basn4a16.png
+# -- color with alpha-channel
+./pnm2png -alpha basn6a08.pgm basn6a08.ppm basn6a08.png
+./pnm2png -alpha basn6a16.pgm basn6a16.ppm basn6a16.png
+# -- grayscale
+./pnm2png rawn0g01.pgm rawn0g01.png
+./pnm2png rawn0g02.pgm rawn0g02.png
+./pnm2png rawn0g04.pgm rawn0g04.png
+./pnm2png rawn0g08.pgm rawn0g08.png
+./pnm2png rawn0g16.pgm rawn0g16.png
+# -- full-color
+./pnm2png rawn2c08.ppm rawn2c08.png
+./pnm2png rawn2c16.ppm rawn2c16.png
+# -- paletted
+./pnm2png rawn3p01.ppm rawn3p01.png
+./pnm2png rawn3p02.ppm rawn3p02.png
+./pnm2png rawn3p04.ppm rawn3p04.png
+./pnm2png rawn3p08.ppm rawn3p08.png
+# -- gray with alpha-channel
+./pnm2png -alpha rawn6a08.pgm rawn4a08.pgm rawn4a08.png
+./pnm2png -alpha rawn6a16.pgm rawn4a16.pgm rawn4a16.png
+# -- color with alpha-channel
+./pnm2png -alpha rawn6a08.pgm rawn6a08.ppm rawn6a08.png
+./pnm2png -alpha rawn6a16.pgm rawn6a16.ppm rawn6a16.png
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/README b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/README
new file mode 100755
index 0000000..d236b02
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/README
@@ -0,0 +1,107 @@
+
+pngsuite
+--------
+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, modify, and distribute these images for any
+purpose and without fee is hereby granted.
+
+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 "i*.png" images are the same images, but interlaced.
+
+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
+    ------------ ------ --------------
+    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
+
+    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)
+ Filter 0 was used 32 times
+Testing basn0g02.png: PASS (448 zero samples)
+ Filter 0 was used 32 times
+Testing basn0g04.png: PASS (520 zero samples)
+ Filter 0 was used 32 times
+Testing basn0g08.png: PASS (3 zero samples)
+ Filter 1 was used 9 times
+ Filter 4 was used 23 times
+Testing basn0g16.png: PASS (1 zero samples)
+ Filter 1 was used 1 times
+ Filter 2 was used 31 times
+Testing basn2c08.png: PASS (6 zero samples)
+ Filter 1 was used 5 times
+ Filter 4 was used 27 times
+Testing basn2c16.png: PASS (592 zero samples)
+ Filter 1 was used 1 times
+ Filter 4 was used 31 times
+Testing basn3p01.png: PASS (512 zero samples)
+ Filter 0 was used 32 times
+Testing basn3p02.png: PASS (448 zero samples)
+ Filter 0 was used 32 times
+Testing basn3p04.png: PASS (544 zero samples)
+ Filter 0 was used 32 times
+Testing basn3p08.png: PASS (4 zero samples)
+ Filter 0 was used 32 times
+Testing basn4a08.png: PASS (32 zero samples)
+ Filter 1 was used 1 times
+ Filter 4 was used 31 times
+Testing basn4a16.png: PASS (64 zero samples)
+ Filter 0 was used 1 times
+ Filter 1 was used 2 times
+ Filter 2 was used 1 times
+ Filter 4 was used 28 times
+Testing basn6a08.png: PASS (160 zero samples)
+ Filter 1 was used 1 times
+ Filter 4 was used 31 times
+Testing basn6a16.png: PASS (1072 zero samples)
+ Filter 1 was used 4 times
+ Filter 4 was used 28 times
+libpng passes test
+
+Willem van Schaik
+<willem at schaik.com>
+October 1999
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/bad_interlace_conversions.txt b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/bad_interlace_conversions.txt
new file mode 100755
index 0000000..8afbde7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/bad_interlace_conversions.txt
@@ -0,0 +1,9 @@
+basn0g01.png
+basn0g02.png
+basn0g04.png
+basn3p01.png
+basn3p02.png
+basn3p04.png
+ftbbn0g01.png
+ftbbn0g02.png
+ftbbn0g04.png
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g01.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g01.png
new file mode 100755
index 0000000..e31e1c7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g01.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g02.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g02.png
new file mode 100755
index 0000000..68809dd
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g02.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g04.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g04.png
new file mode 100755
index 0000000..6fa089c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g04.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g08.png
new file mode 100755
index 0000000..bf522ee
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g16.png
new file mode 100755
index 0000000..318ebca
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn0g16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn2c08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn2c08.png
new file mode 100755
index 0000000..21d2f91
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn2c08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn2c16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn2c16.png
new file mode 100755
index 0000000..1bd4a4d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn2c16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p01.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p01.png
new file mode 100755
index 0000000..a21db59
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p01.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p02.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p02.png
new file mode 100755
index 0000000..1d0ab61
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p02.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p04.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p04.png
new file mode 100755
index 0000000..6dc6eac
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p04.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p08.png
new file mode 100755
index 0000000..0e07f48
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn4a08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn4a08.png
new file mode 100755
index 0000000..3bb0dd0
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn4a08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn4a16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn4a16.png
new file mode 100755
index 0000000..6dbee9f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn4a16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn6a08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn6a08.png
new file mode 100755
index 0000000..6106230
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn6a08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn6a16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn6a16.png
new file mode 100755
index 0000000..a9bf3cb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/basn6a16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g01.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g01.png
new file mode 100755
index 0000000..ba746ff
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g01.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g02.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g02.png
new file mode 100755
index 0000000..3d83bd6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g02.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g04.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g04.png
new file mode 100755
index 0000000..39a7050
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn0g04.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn2c16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn2c16.png
new file mode 100755
index 0000000..dd3168e
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn2c16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn3p08.png
new file mode 100755
index 0000000..0ede357
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbbn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbgn2c16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbgn2c16.png
new file mode 100755
index 0000000..85cec39
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbgn2c16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbgn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbgn3p08.png
new file mode 100755
index 0000000..8cf2e6f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbgn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbrn2c08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbrn2c08.png
new file mode 100755
index 0000000..5cca0d6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbrn2c08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbwn0g16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbwn0g16.png
new file mode 100755
index 0000000..99bdeed
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbwn0g16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbwn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbwn3p08.png
new file mode 100755
index 0000000..eacab7a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbwn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbyn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbyn3p08.png
new file mode 100755
index 0000000..656db09
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftbyn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n0g08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n0g08.png
new file mode 100755
index 0000000..333465f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n0g08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n2c08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n2c08.png
new file mode 100755
index 0000000..fc6e42c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n2c08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n3p08.png
new file mode 100755
index 0000000..69a69e5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp0n3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp1n3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp1n3p08.png
new file mode 100755
index 0000000..a6c9f35
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ftp1n3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn0g08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn0g08.png
new file mode 100755
index 0000000..90b5305
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn0g08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn0g16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn0g16.png
new file mode 100755
index 0000000..c82f230
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn0g16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn2c08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn2c08.png
new file mode 100755
index 0000000..05d4688
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn2c08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn2c16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn2c16.png
new file mode 100755
index 0000000..399f1be
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn2c16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn3p08.png
new file mode 100755
index 0000000..6df8370
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn4a08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn4a08.png
new file mode 100755
index 0000000..bda8e3c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn4a08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn4a16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn4a16.png
new file mode 100755
index 0000000..500f912
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn4a16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn6a08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn6a08.png
new file mode 100755
index 0000000..258f940
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn6a08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn6a16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn6a16.png
new file mode 100755
index 0000000..e4de69f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/ibasn6a16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbbn2c16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbbn2c16.png
new file mode 100755
index 0000000..64a9cdf
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbbn2c16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbbn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbbn3p08.png
new file mode 100755
index 0000000..47d6eeb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbbn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbgn2c16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbgn2c16.png
new file mode 100755
index 0000000..64a9cdf
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbgn2c16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbgn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbgn3p08.png
new file mode 100755
index 0000000..47d6eeb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbgn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbrn2c08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbrn2c08.png
new file mode 100755
index 0000000..08ebbae
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbrn2c08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbwn0g16.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbwn0g16.png
new file mode 100755
index 0000000..4b7537e
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbwn0g16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbwn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbwn3p08.png
new file mode 100755
index 0000000..47d6eeb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbwn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbyn3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbyn3p08.png
new file mode 100755
index 0000000..47d6eeb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftbyn3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n0g08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n0g08.png
new file mode 100755
index 0000000..aa826b8
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n0g08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n2c08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n2c08.png
new file mode 100755
index 0000000..b12bd0a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n2c08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n3p08.png
new file mode 100755
index 0000000..37aa0cb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp0n3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp1n3p08.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp1n3p08.png
new file mode 100755
index 0000000..47d6eeb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/iftp1n3p08.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/README b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/README
new file mode 100755
index 0000000..f171eee
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/README
@@ -0,0 +1,2 @@
+
+These images fail the "pngimage-quick" and "pngimage-full" tests.
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g01.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g01.png
new file mode 100755
index 0000000..828fa76
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g01.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g02.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g02.png
new file mode 100755
index 0000000..7fc17e3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g02.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g04.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g04.png
new file mode 100755
index 0000000..1beade2
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn0g04.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p01.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p01.png
new file mode 100755
index 0000000..f91e723
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p01.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p02.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p02.png
new file mode 100755
index 0000000..0f9ce76
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p02.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p04.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p04.png
new file mode 100755
index 0000000..b2ca720
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/ibasn3p04.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g01.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g01.png
new file mode 100755
index 0000000..6eb27d1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g01.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g02.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g02.png
new file mode 100755
index 0000000..46ba497
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g02.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g04.png b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g04.png
new file mode 100755
index 0000000..e9db0ad
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/pngsuite/interlaced/iftbbn0g04.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/README b/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/README
new file mode 100755
index 0000000..4970a52
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/README
@@ -0,0 +1,81 @@
+OPERATING SYSTEM SPECIFIC POWERPC DETECTION
+--------------------------------------------
+
+Detection of the ability to execute POWERPC on processor requires
+operating system support.  (The information is not available in user mode.)
+
+Currently only this feature is supported only for linux platform.
+
+HOW TO USE THIS
+---------------
+
+This directory contains C code fragments that can be included in powerpc/powerpc_init.c
+by setting the macro PNG_POWERPC_VSX_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 POWERPC code must be enabled and run time
+checks must be supported.  I.e.:
+
+#if PNG_POWERPC_VSX_OPT > 0
+#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED
+
+This is done in a 'configure' build by passing configure the argument:
+
+   --enable-powerpc-vsx=check
+
+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_vsx(png_structp png_ptr);
+
+That function must return 1 if POWERPC_VSX 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/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/linux.c b/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/linux.c
new file mode 100755
index 0000000..32ed9d7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/linux.c
@@ -0,0 +1,57 @@
+/* contrib/powerpc-vsx/linux.c
+ *
+ * Copyright (c) 2017 Glenn Randers-Pehrson
+ * Written by Vadim Barkov, 2017.
+ * Last changed in libpng 1.6.29 [March 16, 2017]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * STATUS: TESTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_vsx 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>
+#include <string.h>
+#include <stdlib.h>
+#include "png.h"
+
+#ifndef MAXLINE
+#  define MAXLINE 1024
+#endif
+
+static int
+png_have_vsx(png_structp png_ptr)
+{
+   FILE *f;
+
+   const char *string = "altivec supported";
+   char input[MAXLINE];
+   char *token = NULL;
+
+   PNG_UNUSED(png_ptr)
+
+   f = fopen("/proc/cpuinfo", "r");
+   if (f != NULL)
+   {
+      memset(input,0,MAXLINE);
+      while(fgets(input,MAXLINE,f) != NULL)
+      {
+         token = strstr(input,string);
+         if(token != NULL)
+            return 1;
+      }
+   }
+#ifdef PNG_WARNINGS_SUPPORTED
+   else
+      png_warning(png_ptr, "/proc/cpuinfo open failed");
+#endif
+   return 0;
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/linux_aux.c b/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/linux_aux.c
new file mode 100755
index 0000000..7828aca
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/powerpc-vsx/linux_aux.c
@@ -0,0 +1,34 @@
+/* contrib/powerpc-vsx/linux_aux.c
+ *
+ * Copyright (c) 2017 Glenn Randers-Pehrson
+ * Written by Vadim Barkov, 2017.
+ * Last changed in libpng 1.6.29 [March 16, 2017]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * STATUS: TESTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_vsx implemented for Linux by using the auxiliary vector mechanism.
+ *
+ * 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 "sys/auxv.h"
+#include "png.h"
+
+static int
+png_have_vsx(png_structp png_ptr)
+{
+   unsigned long auxv = getauxval(AT_HWCAP);
+
+   PNG_UNUSED(png_ptr)
+
+   if(auxv & (PPC_FEATURE_HAS_ALTIVEC|PPC_FEATURE_HAS_VSX))
+      return 1;
+   else
+      return 0;
+}
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/bad_iCCP.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/bad_iCCP.png
new file mode 100755
index 0000000..0b7b863
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/bad_iCCP.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/badadler.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/badadler.png
new file mode 100755
index 0000000..2bcbcc9
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/badadler.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/badcrc.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/badcrc.png
new file mode 100755
index 0000000..45d0268
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/badcrc.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/empty_ancillary_chunks.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/empty_ancillary_chunks.png
new file mode 100755
index 0000000..d76477c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/empty_ancillary_chunks.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_IDAT.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_IDAT.png
new file mode 100755
index 0000000..efee66c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_IDAT.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_bKGD_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_bKGD_chunk.png
new file mode 100755
index 0000000..fd571fd
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_bKGD_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_cHRM_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_cHRM_chunk.png
new file mode 100755
index 0000000..698e7c4
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_cHRM_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_eXIf_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_eXIf_chunk.png
new file mode 100755
index 0000000..7d41601
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_eXIf_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_gAMA_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_gAMA_chunk.png
new file mode 100755
index 0000000..b089c18
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_gAMA_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_hIST_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_hIST_chunk.png
new file mode 100755
index 0000000..6d6fcb5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_hIST_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_iCCP_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_iCCP_chunk.png
new file mode 100755
index 0000000..fbcb761
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_iCCP_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_iTXt_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_iTXt_chunk.png
new file mode 100755
index 0000000..0982620
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_iTXt_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_juNK_unsafe_to_copy.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_juNK_unsafe_to_copy.png
new file mode 100755
index 0000000..0cb106f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_juNK_unsafe_to_copy.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_juNk_safe_to_copy.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_juNk_safe_to_copy.png
new file mode 100755
index 0000000..85252b7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_juNk_safe_to_copy.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_pCAL_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_pCAL_chunk.png
new file mode 100755
index 0000000..bc12a8f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_pCAL_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_pHYs_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_pHYs_chunk.png
new file mode 100755
index 0000000..343b977
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_pHYs_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sCAL_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sCAL_chunk.png
new file mode 100755
index 0000000..61f2f82
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sCAL_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sPLT_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sPLT_chunk.png
new file mode 100755
index 0000000..75d383a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sPLT_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sRGB_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sRGB_chunk.png
new file mode 100755
index 0000000..bc1db33
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sRGB_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sTER_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sTER_chunk.png
new file mode 100755
index 0000000..39219fe
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_sTER_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_tEXt_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_tEXt_chunk.png
new file mode 100755
index 0000000..947fc37
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_tEXt_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_tIME_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_tIME_chunk.png
new file mode 100755
index 0000000..16452fb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_tIME_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_zTXt_chunk.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_zTXt_chunk.png
new file mode 100755
index 0000000..52f0953
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/crashers/huge_zTXt_chunk.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-1.8-tRNS.png
new file mode 100755
index 0000000..0fc6cb7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-1.8.png
new file mode 100755
index 0000000..5db4d79
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-linear-tRNS.png
new file mode 100755
index 0000000..5beb9d1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-linear.png
new file mode 100755
index 0000000..36a89a5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-sRGB-tRNS.png
new file mode 100755
index 0000000..80cec4b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-sRGB.png
new file mode 100755
index 0000000..484fd18
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-tRNS.png
new file mode 100755
index 0000000..a988db9
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1.png
new file mode 100755
index 0000000..71e37a3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-1.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-1.8-tRNS.png
new file mode 100755
index 0000000..06cc0c1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-1.8.png
new file mode 100755
index 0000000..06cc0c1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-linear-tRNS.png
new file mode 100755
index 0000000..beb4e96
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-linear.png
new file mode 100755
index 0000000..beb4e96
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-sRGB-tRNS.png
new file mode 100755
index 0000000..315db86
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-sRGB.png
new file mode 100755
index 0000000..315db86
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-tRNS.png
new file mode 100755
index 0000000..4826d61
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16.png
new file mode 100755
index 0000000..4826d61
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-1.8-tRNS.png
new file mode 100755
index 0000000..a1c6854
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-1.8.png
new file mode 100755
index 0000000..4cc811c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-linear-tRNS.png
new file mode 100755
index 0000000..3c29ec6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-linear.png
new file mode 100755
index 0000000..6ca6fb7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-sRGB-tRNS.png
new file mode 100755
index 0000000..b418c8f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-sRGB.png
new file mode 100755
index 0000000..a6bde60
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-tRNS.png
new file mode 100755
index 0000000..8e04cb5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2.png
new file mode 100755
index 0000000..9d85a2a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-2.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-1.8-tRNS.png
new file mode 100755
index 0000000..402d699
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-1.8.png
new file mode 100755
index 0000000..bc8da98
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-linear-tRNS.png
new file mode 100755
index 0000000..45c063d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-linear.png
new file mode 100755
index 0000000..0bd068d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-sRGB-tRNS.png
new file mode 100755
index 0000000..be85210
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-sRGB.png
new file mode 100755
index 0000000..38a7922
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-tRNS.png
new file mode 100755
index 0000000..14c4f1f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4.png
new file mode 100755
index 0000000..16bcada
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-4.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-1.8-tRNS.png
new file mode 100755
index 0000000..2b38924
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-1.8.png
new file mode 100755
index 0000000..4bba74c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-linear-tRNS.png
new file mode 100755
index 0000000..a801abd
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-linear.png
new file mode 100755
index 0000000..91be367
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-sRGB-tRNS.png
new file mode 100755
index 0000000..f54f23d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-sRGB.png
new file mode 100755
index 0000000..2ae6377
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-tRNS.png
new file mode 100755
index 0000000..842245f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8.png
new file mode 100755
index 0000000..23af27e
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-1.8.png
new file mode 100755
index 0000000..e0d36b7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-linear.png
new file mode 100755
index 0000000..26aa32c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-sRGB.png
new file mode 100755
index 0000000..f1e811b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16.png
new file mode 100755
index 0000000..6898797
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-1.8.png
new file mode 100755
index 0000000..68883a5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-linear.png
new file mode 100755
index 0000000..cdc0703
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-sRGB.png
new file mode 100755
index 0000000..7c37c88
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8.png
new file mode 100755
index 0000000..eb0a924
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/gray-alpha-8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/makepngs.sh b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/makepngs.sh
new file mode 100755
index 0000000..eb1c15f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/makepngs.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Make a set of test PNG files, MAKEPNG is the name of the makepng executable
+# built from contrib/libtests/makepng.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
+
+# The arguments say whether to build all the files or whether just to build the
+# ones that extend the code-coverage of libpng from the existing test files in
+# contrib/pngsuite.
+test -n "$MAKEPNG" || MAKEPNG=./makepng
+opts=
+
+mp(){
+   ${MAKEPNG} $opts $1 "$3" "$4" "$3-$4$2.png"
+}
+
+mpg(){
+   if test "$1" = "none"
+   then
+      mp "" "" "$2" "$3"
+   else
+      mp "--$1" "-$1" "$2" "$3"
+   fi
+}
+
+mptrans(){
+   if test "$1" = "none"
+   then
+      mp "--tRNS" "-tRNS" "$2" "$3"
+   else
+      mp "--tRNS --$1" "-$1-tRNS" "$2" "$3"
+   fi
+}
+
+case "$1" in
+   --small)
+      opts="--small";;&
+
+   --all|--small)
+      for g in none sRGB linear 1.8
+      do
+         for c in gray palette
+         do
+            for b in 1 2 4
+            do
+               mpg "$g" "$c" "$b"
+               mptrans "$g" "$c" "$b"
+            done
+         done
+
+         mpg "$g" palette 8
+         mptrans "$g" palette 8
+
+         for b in 8 16
+         do
+            for c in gray gray-alpha rgb rgb-alpha
+            do
+               mpg "$g" "$c" "$b"
+            done
+            for c in gray rgb
+            do
+               mptrans "$g" "$c" "$b"
+            done
+         done
+      done;;
+
+   --coverage)
+      # Comments below indicate cases known to be required and not duplicated
+      # in other (required) cases; the aim is to get a minimal set that gives
+      # the maximum code coverage.
+      mpg none gray-alpha 8 # required: code coverage, sRGB opaque component
+      mpg none palette 8 # required: basic palette read
+      mpg 1.8 gray 2 # required: tests gamma threshold code
+      mpg 1.8 palette 2 # required: code coverage
+      mpg 1.8 palette 4 # required: code coverage
+      mpg 1.8 palette 8 # error limits only
+      mpg linear palette 8 # error limits only
+      mpg linear rgb-alpha 16 # error limits only
+      mpg sRGB palette 1 # required: code coverage
+      mpg sRGB rgb-alpha 16 # required: code coverage: pngread.c:2422 untested
+      :;;
+
+   *)
+      echo "$0 $1: unknown argument, usage:" >&2
+      echo "  $0 [--all|--coverage|--small]" >&2
+      exit 1
+esac
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-1.8-tRNS.png
new file mode 100755
index 0000000..7bf6041
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-1.8.png
new file mode 100755
index 0000000..73965b7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-linear-tRNS.png
new file mode 100755
index 0000000..9b0133d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-linear.png
new file mode 100755
index 0000000..bf232fd
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-sRGB-tRNS.png
new file mode 100755
index 0000000..1c6f262
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-sRGB.png
new file mode 100755
index 0000000..fbadc09
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-tRNS.png
new file mode 100755
index 0000000..8f1642b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1.png
new file mode 100755
index 0000000..a27d136
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-1.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-1.8-tRNS.png
new file mode 100755
index 0000000..9d2ab16
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-1.8.png
new file mode 100755
index 0000000..cdcf283
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-linear-tRNS.png
new file mode 100755
index 0000000..d346d40
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-linear.png
new file mode 100755
index 0000000..5c2f6d3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-sRGB-tRNS.png
new file mode 100755
index 0000000..e5a491a7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-sRGB.png
new file mode 100755
index 0000000..a3bf9a2
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-tRNS.png
new file mode 100755
index 0000000..c34a698
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2.png
new file mode 100755
index 0000000..eaf4536
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-2.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-1.8-tRNS.png
new file mode 100755
index 0000000..20cb465
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-1.8.png
new file mode 100755
index 0000000..c2318ff
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-linear-tRNS.png
new file mode 100755
index 0000000..4d96a47
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-linear.png
new file mode 100755
index 0000000..dfc5a86
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-sRGB-tRNS.png
new file mode 100755
index 0000000..92d23e7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-sRGB.png
new file mode 100755
index 0000000..26ea9e5
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-tRNS.png
new file mode 100755
index 0000000..429f6b9
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4.png
new file mode 100755
index 0000000..82f3489
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-4.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-1.8-tRNS.png
new file mode 100755
index 0000000..c10bb56
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-1.8.png
new file mode 100755
index 0000000..cc09ee6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-linear-tRNS.png
new file mode 100755
index 0000000..27f5a33
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-linear.png
new file mode 100755
index 0000000..40d413c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-sRGB-tRNS.png
new file mode 100755
index 0000000..b8863ec
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-sRGB.png
new file mode 100755
index 0000000..90f8912
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-tRNS.png
new file mode 100755
index 0000000..9660e67
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8.png
new file mode 100755
index 0000000..2502d30
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/palette-8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-1.8-tRNS.png
new file mode 100755
index 0000000..03251b1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-1.8.png
new file mode 100755
index 0000000..03251b1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-linear-tRNS.png
new file mode 100755
index 0000000..3dfa6a6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-linear.png
new file mode 100755
index 0000000..3dfa6a6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-sRGB-tRNS.png
new file mode 100755
index 0000000..2cf2209
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-sRGB.png
new file mode 100755
index 0000000..2cf2209
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-tRNS.png
new file mode 100755
index 0000000..2864352
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16.png
new file mode 100755
index 0000000..2864352
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-1.8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-1.8-tRNS.png
new file mode 100755
index 0000000..afff9df
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-1.8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-1.8.png
new file mode 100755
index 0000000..d4bb12a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-linear-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-linear-tRNS.png
new file mode 100755
index 0000000..5a2ed9f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-linear-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-linear.png
new file mode 100755
index 0000000..7c42b08
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-sRGB-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-sRGB-tRNS.png
new file mode 100755
index 0000000..f8b4ecb
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-sRGB-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-sRGB.png
new file mode 100755
index 0000000..4acae64
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-tRNS.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-tRNS.png
new file mode 100755
index 0000000..cdb1642
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8-tRNS.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8.png
new file mode 100755
index 0000000..e69c71b
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-1.8.png
new file mode 100755
index 0000000..ad65d15
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-linear.png
new file mode 100755
index 0000000..1f39900
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-sRGB.png
new file mode 100755
index 0000000..d9cea63
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16.png
new file mode 100755
index 0000000..5926239
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-16.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-1.8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-1.8.png
new file mode 100755
index 0000000..f4d08ec
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-1.8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-linear.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-linear.png
new file mode 100755
index 0000000..b4f8bc8
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-linear.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-sRGB.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-sRGB.png
new file mode 100755
index 0000000..f06e7f8
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8-sRGB.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8.png b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8.png
new file mode 100755
index 0000000..e15d491
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/testpngs/rgb-alpha-8.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/tools/README.txt b/ap/lib/libpng/libpng-1.6.37/contrib/tools/README.txt
new file mode 100755
index 0000000..f53be6d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/tools/README.txt
@@ -0,0 +1,27 @@
+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 at acm.org>
+    Glenn Randers-Pehrson <glennrp at users.sourceforge.net>
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/tools/checksum-icc.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/checksum-icc.c
new file mode 100755
index 0000000..581e708
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/tools/chkfmt b/ap/lib/libpng/libpng-1.6.37/contrib/tools/chkfmt
new file mode 100755
index 0000000..95181fd
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/tools/chkfmt
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+# chkfmt
+#
+# COPYRIGHT: Written by John Cunningham Bowler, 2010.
+# 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.
+#
+# 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 substitute 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/ap/lib/libpng/libpng-1.6.37/contrib/tools/cvtcolor.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/cvtcolor.c
new file mode 100755
index 0000000..e6793c7
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/tools/genpng.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/genpng.c
new file mode 100755
index 0000000..0b3f981
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/tools/genpng.c
@@ -0,0 +1,881 @@
+/*- genpng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2015.
+ * Revised by Glenn Randers-Pehrson, 2017, to add buffer-size check.
+ * 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.
+ *
+ * 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;
+      }
+
+#if 1
+     /* TO do: determine whether this guard against overflow is necessary.
+      * This comment in png.h indicates that it should be safe: "libpng will
+      * refuse to process an image where such an overflow would occur", but
+      * I don't see where the image gets rejected when the buffer is too
+      * large before the malloc is attempted.
+      */
+      if (image.height > ((size_t)(-1))/(8*image.width)) {
+         fprintf(stderr, "genpng: image buffer would be too big");
+         return 1;
+      }
+#endif
+
+      /* 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/ap/lib/libpng/libpng-1.6.37/contrib/tools/intgamma.sh b/ap/lib/libpng/libpng-1.6.37/contrib/tools/intgamma.sh
new file mode 100755
index 0000000..41c5d6d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/tools/makesRGB.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/makesRGB.c
new file mode 100755
index 0000000..d0c0ca9
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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("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("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("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/ap/lib/libpng/libpng-1.6.37/contrib/tools/png-fix-itxt.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/png-fix-itxt.c
new file mode 100755
index 0000000..c7654c1
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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/ap/lib/libpng/libpng-1.6.37/contrib/tools/pngcp.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/pngcp.c
new file mode 100755
index 0000000..16d4e7f
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/tools/pngcp.c
@@ -0,0 +1,2453 @@
+/* pngcp.c
+ *
+ * Copyright (c) 2016 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.24 [August 4, 2016]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This is an example of copying a PNG without changes using the png_read_png
+ * and png_write_png interfaces.  A considerable number of options are provided
+ * to manipulate the compression of the PNG data and other compressed chunks.
+ *
+ * For a more extensive example that uses the transforms see
+ * contrib/libtests/pngimage.c in the libpng distribution.
+ */
+#include "pnglibconf.h" /* To find how libpng was configured. */
+
+#ifdef PNG_PNGCP_TIMING_SUPPORTED
+   /* WARNING:
+    *
+    * This test is here to allow POSIX.1b extensions to be used if enabled in
+    * the compile; specifically the code requires_POSIX_C_SOURCE support of
+    * 199309L or later to enable clock_gettime use.
+    *
+    * IF this causes problems THEN compile with a strict ANSI C compiler and let
+    * this code turn on the POSIX features that it minimally requires.
+    *
+    * IF this does not work there is probably a bug in your ANSI C compiler or
+    * your POSIX implementation.
+    */
+#  define _POSIX_C_SOURCE 199309L
+#else /* No timing support required */
+#  define _POSIX_SOURCE 1
+#endif
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+#  include <config.h>
+#endif
+
+#include <stdio.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
+
+#if PNG_LIBPNG_VER < 10700
+   /* READ_PNG and WRITE_PNG were not defined, so: */
+#  ifdef PNG_INFO_IMAGE_SUPPORTED
+#     ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#        define PNG_READ_PNG_SUPPORTED
+#     endif /* SEQUENTIAL_READ */
+#     ifdef PNG_WRITE_SUPPORTED
+#        define PNG_WRITE_PNG_SUPPORTED
+#     endif /* WRITE */
+#  endif /* INFO_IMAGE */
+#endif /* pre 1.7.0 */
+
+#if (defined(PNG_READ_PNG_SUPPORTED)) && (defined(PNG_WRITE_PNG_SUPPORTED))
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <zlib.h>
+
+#ifndef PNG_SETJMP_SUPPORTED
+#  include <setjmp.h> /* because png.h did *not* include this */
+#endif
+
+#ifdef __cplusplus
+#  define voidcast(type, value) static_cast<type>(value)
+#else
+#  define voidcast(type, value) (value)
+#endif /* __cplusplus */
+
+#ifdef __GNUC__
+   /* Many versions of GCC erroneously report that local variables unmodified
+    * within the scope of a setjmp may be clobbered.  This hacks round the
+    * problem (sometimes) without harming other compilers.
+    */
+#  define gv volatile
+#else
+#  define gv
+#endif
+
+/* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime.  It
+ * need not be supported even when clock_gettime is available.  It returns the
+ * 'CPU' time the process has consumed.  'CPU' time is assumed to include time
+ * when the CPU is actually blocked by a pending cache fill but not time
+ * waiting for page faults.  The attempt is to get a measure of the actual time
+ * the implementation takes to read a PNG ignoring the potentially very large IO
+ * overhead.
+ */
+#ifdef PNG_PNGCP_TIMING_SUPPORTED
+#  include <time.h>   /* clock_gettime and associated definitions */
+#  ifndef CLOCK_PROCESS_CPUTIME_ID
+      /* Prevent inclusion of the spurious code: */
+#     undef PNG_PNGCP_TIMING_SUPPORTED
+#  endif
+#endif /* PNGCP_TIMING */
+
+/* So if the timing feature has been activated: */
+
+/* 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 STRICT          0x010 /* Fail on warnings as well as errors */
+#define LOG             0x020 /* Log pass/fail to stdout */
+#define CONTINUE        0x040 /* Continue on APP_FAIL errors */
+#define SIZES           0x080 /* Report input and output sizes */
+#define SEARCH          0x100 /* Search IDAT compression options */
+#define NOWRITE         0x200 /* Do not write an output file */
+#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#  define IGNORE_INDEX  0x400 /* Ignore out of range palette indices (BAD!) */
+#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+#     define FIX_INDEX  0x800 /* 'Fix' out of range palette indices (OK) */
+#  endif /* GET_PALETTE_MAX */
+#endif /* CHECK_FOR_INVALID_INDEX */
+#define OPTION     0x80000000 /* Used for handling options */
+#define LIST       0x80000001 /* Used for handling options */
+
+/* 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)
+
+/* OPTION DEFINITIONS */
+static const char range_lo[] = "low";
+static const char range_hi[] = "high";
+static const char all[] = "all";
+#define RANGE(lo,hi) { range_lo, lo }, { range_hi, hi }
+typedef struct value_list
+{
+   const char *name;  /* the command line name of the value */
+   int         value; /* the actual value to use */
+}  value_list;
+
+static const value_list
+#ifdef PNG_SW_COMPRESS_png_level
+vl_compression[] =
+{
+   /* Overall compression control.  The order controls the search order for
+    * 'all'.  Since the search is for the smallest the order used is low memory
+    * then high speed.
+    */
+   { "low-memory",      PNG_COMPRESSION_LOW_MEMORY },
+   { "high-speed",      PNG_COMPRESSION_HIGH_SPEED },
+   { "high-read-speed", PNG_COMPRESSION_HIGH_READ_SPEED },
+   { "low",             PNG_COMPRESSION_LOW },
+   { "medium",          PNG_COMPRESSION_MEDIUM },
+   { "old",             PNG_COMPRESSION_COMPAT },
+   { "high",            PNG_COMPRESSION_HIGH },
+   { all, 0 }
+},
+#endif /* SW_COMPRESS_png_level */
+
+#if defined(PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED) ||\
+    defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED)
+vl_strategy[] =
+{
+   /* This controls the order of search. */
+   { "huffman", Z_HUFFMAN_ONLY },
+   { "RLE", Z_RLE },
+   { "fixed", Z_FIXED }, /* the remainder do window searches */
+   { "filtered", Z_FILTERED },
+   { "default", Z_DEFAULT_STRATEGY },
+   { all, 0 }
+},
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+vl_windowBits_text[] =
+{
+   { "default", MAX_WBITS/*from zlib*/ },
+   { "minimum", 8 },
+   RANGE(8, MAX_WBITS/*from zlib*/),
+   { all, 0 }
+},
+#endif /* text compression */
+vl_level[] =
+{
+   { "default", Z_DEFAULT_COMPRESSION /* this is -1 */ },
+   { "none", Z_NO_COMPRESSION },
+   { "speed", Z_BEST_SPEED },
+   { "best", Z_BEST_COMPRESSION },
+   { "0", Z_NO_COMPRESSION },
+   RANGE(1, 9), /* this deliberately excludes '0' */
+   { all, 0 }
+},
+vl_memLevel[] =
+{
+   { "max", MAX_MEM_LEVEL }, /* zlib maximum */
+   { "1", 1 }, /* zlib minimum */
+   { "default", 8 }, /* zlib default */
+   { "2", 2 },
+   { "3", 3 },
+   { "4", 4 },
+   { "5", 5 }, /* for explicit testing */
+   RANGE(6, MAX_MEM_LEVEL/*zlib*/), /* exclude 5 and below: zlib bugs */
+   { all, 0 }
+},
+#endif /* WRITE_CUSTOMIZE_*COMPRESSION */
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+vl_filter[] =
+{
+   { all,      PNG_ALL_FILTERS   },
+   { "off",    PNG_NO_FILTERS    },
+   { "none",   PNG_FILTER_NONE   },
+   { "sub",    PNG_FILTER_SUB    },
+   { "up",     PNG_FILTER_UP     },
+   { "avg",    PNG_FILTER_AVG    },
+   { "paeth",  PNG_FILTER_PAETH  }
+},
+#endif /* WRITE_FILTER */
+#ifdef PNG_PNGCP_TIMING_SUPPORTED
+#  define PNGCP_TIME_READ  1
+#  define PNGCP_TIME_WRITE 2
+vl_time[] =
+{
+   { "both",  PNGCP_TIME_READ+PNGCP_TIME_WRITE },
+   { "off",   0 },
+   { "read",  PNGCP_TIME_READ },
+   { "write", PNGCP_TIME_WRITE }
+},
+#endif /* PNGCP_TIMING */
+vl_IDAT_size[] = /* for png_set_IDAT_size */
+{
+   { "default", 0x7FFFFFFF },
+   { "minimal", 1 },
+   RANGE(1, 0x7FFFFFFF)
+},
+#ifndef PNG_SW_IDAT_size
+   /* Pre 1.7 API: */
+#  define png_set_IDAT_size(p,v) png_set_compression_buffer_size(p, v)
+#endif /* !SW_IDAT_size */
+#define SL 8 /* stack limit in display, below */
+vl_log_depth[] = { { "on", 1 }, { "off", 0 }, RANGE(0, SL) },
+vl_on_off[] = { { "on", 1 }, { "off", 0 } };
+
+#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+static value_list
+vl_windowBits_IDAT[] =
+{
+   { "default", MAX_WBITS },
+   { "small", 9 },
+   RANGE(8, MAX_WBITS), /* modified by set_windowBits_hi */
+   { all, 0 }
+};
+#endif /* IDAT compression */
+
+typedef struct option
+{
+   const char       *name;         /* name of the option */
+   png_uint_32       opt;          /* an option, or OPTION or LIST */
+   png_byte          search;       /* Search on --search */
+   png_byte          value_count;  /* length of the list of values: */
+   const value_list *values;       /* values for OPTION or LIST */
+}  option;
+
+static const option options[] =
+{
+   /* struct display options, these are set when the command line is read */
+#  define S(n,v) { #n, v, 0, 2, vl_on_off },
+   S(verbose,  VERBOSE)
+   S(warnings, WARNINGS)
+   S(errors,   ERRORS)
+   S(quiet,    QUIET)
+   S(strict,   STRICT)
+   S(log,      LOG)
+   S(continue, CONTINUE)
+   S(sizes,    SIZES)
+   S(search,   SEARCH)
+   S(nowrite,  NOWRITE)
+#  ifdef IGNORE_INDEX
+      S(ignore-palette-index, IGNORE_INDEX)
+#  endif /* IGNORE_INDEX */
+#  ifdef FIX_INDEX
+      S(fix-palette-index, FIX_INDEX)
+#  endif /* FIX_INDEX */
+#  undef S
+
+   /* OPTION settings, these and LIST settings are read on demand */
+#  define VLNAME(name) vl_ ## name
+#  define VLSIZE(name) voidcast(png_byte,\
+                           (sizeof VLNAME(name))/(sizeof VLNAME(name)[0]))
+#  define VL(oname, name, type, search)\
+   { oname, type, search, VLSIZE(name), VLNAME(name) },
+#  define VLO(oname, name, search) VL(oname, name, OPTION, search)
+
+#  ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+#     define VLCIDAT(name) VLO(#name, name, 1/*search*/)
+#     ifdef PNG_SW_COMPRESS_level
+#        define VLCiCCP(name) VLO("ICC-profile-" #name, name, 0/*search*/)
+#     else
+#        define VLCiCCP(name)
+#     endif
+#  else
+#     define VLCIDAT(name)
+#     define VLCiCCP(name)
+#  endif /* WRITE_CUSTOMIZE_COMPRESSION */
+
+#  ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+#     define VLCzTXt(name) VLO("text-" #name, name, 0/*search*/)
+#  else
+#     define VLCzTXt(name)
+#  endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
+
+#  define VLC(name) VLCIDAT(name) VLCiCCP(name) VLCzTXt(name)
+
+#  ifdef PNG_SW_COMPRESS_png_level
+      /* The libpng compression level isn't searched because it justs sets the
+       * other things that are searched!
+       */
+      VLO("compression", compression, 0)
+      VLO("text-compression", compression, 0)
+      VLO("ICC-profile-compression", compression, 0)
+#  endif /* SW_COMPRESS_png_level */
+   VLC(strategy)
+   VLO("windowBits", windowBits_IDAT, 1)
+#  ifdef PNG_SW_COMPRESS_windowBits
+      VLO("ICC-profile-windowBits", windowBits_text/*sic*/, 0)
+#  endif
+   VLO("text-windowBits", windowBits_text, 0)
+   VLC(level)
+   VLC(memLevel)
+   VLO("IDAT-size", IDAT_size, 0)
+   VLO("log-depth", log_depth, 0)
+
+#  undef VLO
+
+   /* LIST settings */
+#  define VLL(name, search) VL(#name, name, LIST, search)
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+   VLL(filter, 0)
+#endif /* WRITE_FILTER */
+#ifdef PNG_PNGCP_TIMING_SUPPORTED
+   VLL(time, 0)
+#endif /* PNGCP_TIMING */
+#  undef VLL
+#  undef VL
+};
+
+#ifdef __cplusplus
+   static const size_t option_count((sizeof options)/(sizeof options[0]));
+#else /* !__cplusplus */
+#  define option_count ((sizeof options)/(sizeof options[0]))
+#endif /* !__cplusplus */
+
+static const char *
+cts(int ct)
+{
+   switch (ct)
+   {
+      case PNG_COLOR_TYPE_PALETTE:     return "P";
+      case PNG_COLOR_TYPE_GRAY:        return "G";
+      case PNG_COLOR_TYPE_GRAY_ALPHA:  return "GA";
+      case PNG_COLOR_TYPE_RGB:         return "RGB";
+      case PNG_COLOR_TYPE_RGB_ALPHA:   return "RGBA";
+      default:                         return "INVALID";
+   }
+}
+
+struct display
+{
+   jmp_buf          error_return;      /* Where to go to on error */
+   unsigned int     errset;            /* error_return is set */
+
+   const char      *operation;         /* What is happening */
+   const char      *filename;          /* The name of the original file */
+   const char      *output_file;       /* The name of the output file */
+
+   /* Used on both read and write: */
+   FILE            *fp;
+
+   /* Used on a read, both the original read and when validating a written
+    * image.
+    */
+   png_alloc_size_t read_size;
+   png_structp      read_pp;
+   png_infop        ip;
+#  if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
+      png_textp     text_ptr; /* stash of text chunks */
+      int           num_text;
+      int           text_stashed;
+#  endif /* pre 1.7 */
+
+#  ifdef PNG_PNGCP_TIMING_SUPPORTED
+      struct timespec   read_time;
+      struct timespec   read_time_total;
+      struct timespec   write_time;
+      struct timespec   write_time_total;
+#  endif /* PNGCP_TIMING */
+
+   /* Used to write a new image (the original info_ptr is used) */
+#  define MAX_SIZE ((png_alloc_size_t)(-1))
+   png_alloc_size_t write_size;
+   png_alloc_size_t best_size;
+   png_structp      write_pp;
+
+   /* Base file information */
+   png_alloc_size_t size;
+   png_uint_32      w;
+   png_uint_32      h;
+   int              bpp;
+   png_byte         ct;
+   int              no_warnings;       /* Do not output libpng warnings */
+   int              min_windowBits;    /* The windowBits range is 8..8 */
+
+   /* Options handling */
+   png_uint_32      results;             /* A mask of errors seen */
+   png_uint_32      options;             /* See display_log below */
+   png_byte         entry[option_count]; /* The selected entry+1 of an option
+                                          * that appears on the command line, or
+                                          * 0 if it was not given. */
+   int              value[option_count]; /* Corresponding value */
+
+   /* Compression exhaustive testing */
+   /* Temporary variables used only while testing a single collection of
+    * settings:
+    */
+   unsigned int     csp;               /* next stack entry to use */
+   unsigned int     nsp;               /* highest active entry+1 found so far */
+
+   /* Values used while iterating through all the combinations of settings for a
+    * single file:
+    */
+   unsigned int     tsp;               /* nsp from the last run; this is the
+                                        * index+1 of the highest active entry on
+                                        * this run; this entry will be advanced.
+                                        */
+   int              opt_string_start;  /* Position in buffer for the first
+                                        * searched option; non-zero if earlier
+                                        * options were set on the command line.
+                                        */
+   struct stack
+   {
+      png_alloc_size_t best_size;      /* Best so far for this option */
+      png_alloc_size_t lo_size;
+      png_alloc_size_t hi_size;
+      int              lo, hi;         /* For binary chop of a range */
+      int              best_val;       /* Best value found so far */
+      int              opt_string_end; /* End of the option string in 'curr' */
+      png_byte         opt;            /* The option being tested */
+      png_byte         entry;          /* The next value entry to be tested */
+      png_byte         end;            /* This is the last entry */
+   }                stack[SL];         /* Stack of entries being tested */
+   char             curr[32*SL];       /* current options being tested */
+   char             best[32*SL];       /* best options */
+
+   char             namebuf[FILENAME_MAX]; /* output file name */
+};
+
+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->operation = "internal error";
+   dp->filename = "command line";
+   dp->output_file = "no output file";
+   dp->options = WARNINGS; /* default to !verbose, !quiet */
+   dp->fp = NULL;
+   dp->read_pp = NULL;
+   dp->ip = NULL;
+   dp->write_pp = NULL;
+   dp->min_windowBits = -1; /* this is an OPTIND, so -1 won't match anything */
+#  if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
+      dp->text_ptr = NULL;
+      dp->num_text = 0;
+      dp->text_stashed = 0;
+#  endif /* pre 1.7 */
+}
+
+static void
+display_clean_read(struct display *dp)
+{
+   if (dp->read_pp != NULL)
+      png_destroy_read_struct(&dp->read_pp, NULL, NULL);
+
+   if (dp->fp != NULL)
+   {
+      FILE *fp = dp->fp;
+      dp->fp = NULL;
+      (void)fclose(fp);
+   }
+}
+
+static void
+display_clean_write(struct display *dp)
+{
+   if (dp->fp != NULL)
+   {
+      FILE *fp = dp->fp;
+      dp->fp = NULL;
+      (void)fclose(fp);
+   }
+
+   if (dp->write_pp != NULL)
+      png_destroy_write_struct(&dp->write_pp, dp->tsp > 0 ? NULL : &dp->ip);
+}
+
+static void
+display_clean(struct display *dp)
+{
+   display_clean_read(dp);
+   display_clean_write(dp);
+   dp->output_file = NULL;
+
+#  if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
+      /* This is actually created and used by the write code, but only
+       * once; it has to be retained for subsequent writes of the same file.
+       */
+      if (dp->text_stashed)
+      {
+         dp->text_stashed = 0;
+         dp->num_text = 0;
+         free(dp->text_ptr);
+         dp->text_ptr = NULL;
+      }
+#  endif /* pre 1.7 */
+
+   /* 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. */
+   display_clean(dp);
+}
+
+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, "pngcp: 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(pngcp)"; 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(pngcp)"; 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(pngcp)"; break;
+      }
+
+      fprintf(stderr, "%s: %s: %s",
+         dp->filename != NULL ? dp->filename : "<stdin>", lp, dp->operation);
+
+      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)))
+   {
+      if (dp->errset)
+         longjmp(dp->error_return, level);
+
+      else
+         exit(99);
+   }
+}
+
+#if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
+static void
+text_stash(struct display *dp)
+{
+   /* libpng 1.6 and earlier fixed a bug whereby text chunks were written
+    * multiple times by png_write_png; the issue was that png_write_png passed
+    * the same png_info to both png_write_info and png_write_end.  Rather than
+    * fixing it by recording the information in the png_struct, or by recording
+    * where to write the chunks, the fix made was to change the 'compression'
+    * field of the chunk to invalid values, rendering the png_info somewhat
+    * useless.
+    *
+    * The only fix for this given that we use the png_info more than once is to
+    * make a copy of the text chunks and png_set_text it each time.  This adds a
+    * text chunks, so they get replicated, but only the new set gets written
+    * each time.  This uses memory like crazy but there is no way to delete the
+    * useless chunks from the png_info.
+    *
+    * To make this slightly more efficient only the top level structure is
+    * copied; since the old strings are actually preserved (in 1.6 and earlier)
+    * this happens to work.
+    */
+   png_textp chunks = NULL;
+
+   dp->num_text = png_get_text(dp->write_pp, dp->ip, &chunks, NULL);
+
+   if (dp->num_text > 0)
+   {
+      dp->text_ptr = voidcast(png_textp, malloc(dp->num_text * sizeof *chunks));
+
+      if (dp->text_ptr == NULL)
+         display_log(dp, APP_ERROR, "text chunks: stash malloc failed");
+
+      else
+         memcpy(dp->text_ptr, chunks, dp->num_text * sizeof *chunks);
+   }
+
+   dp->text_stashed = 1; /* regardless of whether there are chunks or not */
+}
+
+#define text_stash(dp) if (!dp->text_stashed) text_stash(dp)
+
+static void
+text_restore(struct display *dp)
+{
+   /* libpng makes a copy, so this is fine: */
+   if (dp->text_ptr != NULL)
+      png_set_text(dp->write_pp, dp->ip, dp->text_ptr, dp->num_text);
+}
+
+#define text_restore(dp) if (dp->text_stashed) text_restore(dp)
+
+#else
+#define text_stash(dp) ((void)0)
+#define text_restore(dp) ((void)0)
+#endif /* pre 1.7 */
+
+/* OPTIONS:
+ *
+ * The command handles options of the forms:
+ *
+ *    --option
+ *       Turn an option on (Option)
+ *    --no-option
+ *       Turn an option off (Option)
+ *    --option=value
+ *       Set an option to a value (Value)
+ *    --option=val1,val2,val3
+ *       Set an option to a bitmask constructed from the values (List)
+ */
+static png_byte
+option_index(struct display *dp, const char *opt, size_t len)
+   /* Return the index (in options[]) of the given option, outputs an error if
+    * it does not exist.  Takes the name of the option and a length (number of
+    * characters in the name).
+    */
+{
+   png_byte j;
+
+   for (j=0; j<option_count; ++j)
+      if (strncmp(options[j].name, opt, len) == 0 && options[j].name[len] == 0)
+         return j;
+
+   /* If the setjmp buffer is set the code is asking for an option index; this
+    * is bad.  Otherwise this is the command line option parsing.
+    */
+   display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR,
+         "%.*s: unknown option", (int)/*SAFE*/len, opt);
+   abort(); /* NOT REACHED */
+}
+
+/* This works for an option name (no quotes): */
+#define OPTIND(dp, name) option_index(dp, #name, (sizeof #name)-1)
+
+static int
+get_option(struct display *dp, const char *opt, int *value)
+{
+   png_byte i = option_index(dp, opt, strlen(opt));
+
+   if (dp->entry[i]) /* option was set on command line */
+   {
+      *value = dp->value[i];
+      return 1;
+   }
+
+   else
+      return 0;
+}
+
+static int
+set_opt_string_(struct display *dp, unsigned int sp, png_byte opt,
+      const char *entry_name)
+   /* Add the appropriate option string to dp->curr. */
+{
+   int offset, add;
+
+   if (sp > 0)
+      offset = dp->stack[sp-1].opt_string_end;
+
+   else
+      offset = dp->opt_string_start;
+
+   if (entry_name == range_lo)
+      add = sprintf(dp->curr+offset, " --%s=%d", options[opt].name,
+            dp->value[opt]);
+
+   else
+      add = sprintf(dp->curr+offset, " --%s=%s", options[opt].name, entry_name);
+
+   if (add < 0)
+      display_log(dp, INTERNAL_ERROR, "sprintf failed");
+
+   assert(offset+add < (int)/*SAFE*/sizeof dp->curr);
+   return offset+add;
+}
+
+static void
+set_opt_string(struct display *dp, unsigned int sp)
+   /* Add the appropriate option string to dp->curr. */
+{
+   dp->stack[sp].opt_string_end = set_opt_string_(dp, sp, dp->stack[sp].opt, 
+      options[dp->stack[sp].opt].values[dp->stack[sp].entry].name);
+}
+
+static void
+record_opt(struct display *dp, png_byte opt, const char *entry_name)
+   /* Record this option in dp->curr; called for an option not being searched,
+    * the caller passes in the name of the value, or range_lo to use the
+    * numerical value.
+    */
+{
+   unsigned int sp = dp->csp; /* stack entry of next searched option */
+
+   if (sp >= dp->tsp)
+   {
+      /* At top of stack; add the opt string for this entry to the previous
+       * searched entry or the start of the dp->curr buffer if there is nothing
+       * on the stack yet (sp == 0).
+       */
+      int offset = set_opt_string_(dp, sp, opt, entry_name);
+
+      if (sp > 0)
+         dp->stack[sp-1].opt_string_end = offset;
+
+      else
+         dp->opt_string_start = offset;
+   }
+
+   /* else do nothing: option already recorded */
+}
+
+static int
+opt_list_end(struct display *dp, png_byte opt, png_byte entry)
+{
+   if (options[opt].values[entry].name == range_lo)
+      return entry+1U >= options[opt].value_count /* missing range_hi */ ||
+         options[opt].values[entry+1U].name != range_hi /* likewise */ ||
+         options[opt].values[entry+1U].value <= dp->value[opt] /* range end */;
+
+   else
+      return entry+1U >= options[opt].value_count /* missing 'all' */ ||
+         options[opt].values[entry+1U].name == all /* last entry */;
+}
+
+static void
+push_opt(struct display *dp, unsigned int sp, png_byte opt, int search)
+   /* Push a new option onto the stack, initializing the new stack entry
+    * appropriately; this does all the work of next_opt (setting end/nsp) for
+    * the first entry in the list.
+    */
+{
+   png_byte entry;
+   const char *entry_name;
+
+   assert(sp == dp->tsp && sp < SL);
+
+   /* The starting entry is entry 0 unless there is a range in which case it is
+    * the entry corresponding to range_lo:
+    */
+   entry = options[opt].value_count;
+   assert(entry > 0U);
+
+   do
+   {
+      entry_name = options[opt].values[--entry].name;
+      if (entry_name == range_lo)
+         break;
+   }
+   while (entry > 0U);
+
+   dp->tsp = sp+1U;
+   dp->stack[sp].best_size =
+      dp->stack[sp].lo_size =
+      dp->stack[sp].hi_size = MAX_SIZE;
+
+   if (search && entry_name == range_lo) /* search this range */
+   {
+      dp->stack[sp].lo = options[opt].values[entry].value;
+      /* check for a mal-formed RANGE above: */
+      assert(entry+1 < options[opt].value_count &&
+             options[opt].values[entry+1].name == range_hi);
+      dp->stack[sp].hi = options[opt].values[entry+1].value;
+   }
+
+   else
+   {
+      /* next_opt will just iterate over the range. */
+      dp->stack[sp].lo = INT_MAX;
+      dp->stack[sp].hi = INT_MIN; /* Prevent range chop */
+   }
+
+   dp->stack[sp].opt = opt;
+   dp->stack[sp].entry = entry;
+   dp->stack[sp].best_val = dp->value[opt] = options[opt].values[entry].value;
+
+   set_opt_string(dp, sp);
+
+   /* This works for the search case too; if the range has only one entry 'end'
+    * will be marked here.
+    */
+   if (opt_list_end(dp, opt, entry))
+   {
+      dp->stack[sp].end = 1;
+      /* Skip the warning if pngcp did this itself.  See the code in
+       * set_windowBits_hi.
+       */
+      if (opt != dp->min_windowBits)
+         display_log(dp, APP_WARNING, "%s: only testing one value",
+               options[opt].name);
+   }
+
+   else
+   {
+      dp->stack[sp].end = 0;
+      dp->nsp = dp->tsp;
+   }
+
+   /* Do a lazy cache of the text chunks for libpng 1.6 and earlier; this is
+    * because they can only be written once(!) so if we are going to re-use the
+    * png_info we need a copy.
+    */
+   text_stash(dp);
+}
+
+static void
+next_opt(struct display *dp, unsigned int sp)
+   /* Return the next value for this option.  When called 'sp' is expected to be
+    * the topmost stack entry - only the topmost entry changes each time round -
+    * and there must be a valid entry to return.  next_opt will set dp->nsp to
+    * sp+1 if more entries are available, otherwise it will not change it and
+    * set dp->stack[s].end to true.
+    */
+{
+   int search = 0;
+   png_byte entry, opt;
+   const char *entry_name;
+
+   /* dp->stack[sp] must be the top stack entry and it must be active: */
+   assert(sp+1U == dp->tsp && !dp->stack[sp].end);
+
+   opt = dp->stack[sp].opt;
+   entry = dp->stack[sp].entry;
+   assert(entry+1U < options[opt].value_count);
+   entry_name = options[opt].values[entry].name;
+   assert(entry_name != NULL);
+
+   /* For ranges increment the value but don't change the entry, for all other
+    * cases move to the next entry and load its value:
+    */
+   if (entry_name == range_lo) /* a range */
+   {
+      /* A range can be iterated over or searched.  The default iteration option
+       * is indicated by hi < lo on the stack, otherwise the range being search
+       * is [lo..hi] (inclusive).
+       */
+      if (dp->stack[sp].lo > dp->stack[sp].hi)
+         dp->value[opt]++;
+
+      else
+      {
+         /* This is the best size found for this option value: */
+         png_alloc_size_t best_size = dp->stack[sp].best_size;
+         int lo = dp->stack[sp].lo;
+         int hi = dp->stack[sp].hi;
+         int val = dp->value[opt];
+
+         search = 1; /* end is determined here */
+         assert(best_size < MAX_SIZE);
+
+         if (val == lo)
+         {
+            /* Finding the best for the low end of the range: */
+            dp->stack[sp].lo_size = best_size;
+            assert(hi > val);
+
+            if (hi == val+1) /* only 2 entries */
+               dp->stack[sp].end = 1;
+
+            val = hi;
+         }
+
+         else if (val == hi)
+         {
+            dp->stack[sp].hi_size = best_size;
+            assert(val > lo+1); /* else 'end' set above */
+
+            if (val == lo+2) /* only three entries to test */
+               dp->stack[sp].end = 1;
+
+            val = (lo + val)/2;
+         }
+
+         else
+         {
+            png_alloc_size_t lo_size = dp->stack[sp].lo_size;
+            png_alloc_size_t hi_size = dp->stack[sp].hi_size;
+
+            /* lo and hi should have been tested. */
+            assert(lo_size < MAX_SIZE && hi_size < MAX_SIZE);
+
+            /* These cases arise with the 'probe' handling below when there is a
+             * dip or peak in the size curve.
+             */
+            if (val < lo) /* probing a new lo */
+            {
+               /* Swap lo and val: */
+               dp->stack[sp].lo = val;
+               dp->stack[sp].lo_size = best_size;
+               val = lo;
+               best_size = lo_size;
+               lo = dp->stack[sp].lo;
+               lo_size = dp->stack[sp].lo_size;
+            }
+
+            else if (val > hi) /* probing a new hi */
+            {
+               /* Swap hi and val: */
+               dp->stack[sp].hi = val;
+               dp->stack[sp].hi_size = best_size;
+               val = hi;
+               best_size = hi_size;
+               hi = dp->stack[sp].hi;
+               hi_size = dp->stack[sp].hi_size;
+            }
+
+            /* The following should be true or something got messed up above. */
+            assert(lo < val && val < hi);
+
+            /* If there are only four entries (lo, val, hi plus one more) just
+             * test the remaining entry.
+             */
+            if (hi == lo+3)
+            {
+               /* Because of the 'probe' code val can either be lo+1 or hi-1; we
+                * need to test the other.
+                */
+               val = lo + ((val == lo+1) ? 2 : 1);
+               assert(lo < val && val < hi);
+               dp->stack[sp].end = 1;
+            }
+
+            else
+            {
+               /* There are at least 2 entries still untested between lo and hi,
+                * i.e. hi >= lo+4.  'val' is the midpoint +/- 0.5
+                *
+                * Separate out the four easy cases when lo..val..hi are
+                * monotonically decreased or (more weird) increasing:
+                */
+               assert(hi > lo+3);
+
+               if (lo_size <= best_size && best_size <= hi_size)
+               {
+                  /* Select the low range; testing this first favours the low
+                   * range over the high range when everything comes out equal.
+                   * Because of the probing 'val' may be lo+1.  In that case end
+                   * the search and set 'val' to lo+2.
+                   */
+                  if (val == lo+1)
+                  {
+                     ++val;
+                     dp->stack[sp].end = 1;
+                  }
+
+                  else
+                  {
+                     dp->stack[sp].hi = hi = val;
+                     dp->stack[sp].hi_size = best_size;
+                     val = (lo + val) / 2;
+                  }
+               }
+
+               else if (lo_size >= best_size && best_size >= hi_size)
+               {
+                  /* Monotonically decreasing size; this is the expected case.
+                   * Select the high end of the range.  As above, val may be
+                   * hi-1.
+                   */
+                  if (val == hi-1)
+                  {
+                     --val;
+                     dp->stack[sp].end = 1;
+                  }
+
+                  else
+                  {
+                     dp->stack[sp].lo = lo = val;
+                     dp->stack[sp].lo_size = best_size;
+                     val = (val + hi) / 2;
+                  }
+               }
+
+               /* If both those tests failed 'best_size' is either greater than
+                * or less than both lo_size and hi_size.  There is a peak or dip
+                * in the curve of sizes from lo to hi and val is on the peak or
+                * dip.
+                *
+                * Because the ranges being searched as so small (level is 1..9,
+                * windowBits 8..15, memLevel 1..9) there will only be at most
+                * three untested values between lo..val and val..hi, so solve
+                * the problem by probing down from hi or up from lo, whichever
+                * is the higher.
+                *
+                * This is the place where 'val' is set to outside the range
+                * lo..hi, described as 'probing', though maybe 'narrowing' would
+                * be more accurate.
+                */
+               else if (lo_size <= hi_size) /* down from hi */
+               {
+                  dp->stack[sp].hi = val;
+                  dp->stack[sp].hi_size = best_size;
+                  val = --hi;
+               }
+
+               else /* up from low */
+               {
+                  dp->stack[sp].lo = val;
+                  dp->stack[sp].lo_size = best_size;
+                  val = ++lo;
+               }
+
+               /* lo and hi are still the true range limits, check for the end
+                * condition.
+                */
+               assert(hi > lo+1);
+               if (hi <= lo+2)
+                  dp->stack[sp].end = 1;
+            }
+         }
+
+         assert(val != dp->stack[sp].best_val); /* should be a new value */
+         dp->value[opt] = val;
+         dp->stack[sp].best_size = MAX_SIZE;
+      }
+   }
+
+   else
+   {
+      /* Increment 'entry' */
+      dp->value[opt] = options[opt].values[++entry].value;
+      dp->stack[sp].entry = entry;
+   }
+
+   set_opt_string(dp, sp);
+
+   if (!search && opt_list_end(dp, opt, entry)) /* end of list */
+      dp->stack[sp].end = 1;
+
+   else if (!dp->stack[sp].end) /* still active after all these tests */
+      dp->nsp = dp->tsp;
+}
+
+static int
+compare_option(const struct display *dp, unsigned int sp)
+{
+   int opt = dp->stack[sp].opt;
+
+   /* If the best so far is numerically less than the current value the
+    * current set of options is invariably worse.
+    */
+   if (dp->stack[sp].best_val < dp->value[opt])
+      return -1;
+
+   /* Lists of options are searched out of numerical order (currently only
+    * strategy), so only return +1 here when a range is being searched.
+    */
+   else if (dp->stack[sp].best_val > dp->value[opt])
+   {
+      if (dp->stack[sp].lo <= dp->stack[sp].hi /*searching*/)
+         return 1;
+
+      else
+         return -1;
+   }
+
+   else
+      return 0; /* match; current value is the best one */
+}
+
+static int
+advance_opt(struct display *dp, png_byte opt, int search)
+{
+   unsigned int sp = dp->csp++; /* my stack entry */
+
+   assert(sp >= dp->nsp); /* nsp starts off zero */
+
+   /* If the entry was active in the previous run dp->stack[sp] is already
+    * set up and dp->tsp will be greater than sp, otherwise a new entry
+    * needs to be created.
+    *
+    * dp->nsp is handled this way:
+    *
+    * 1) When an option is pushed onto the stack dp->nsp and dp->tsp are
+    *    both set (by push_opt) to the next stack entry *unless* there is
+    *    only one entry in the new list, in which case dp->stack[sp].end
+    *    is set.
+    *
+    * 2) For the top stack entry next_opt is called.  The entry must be
+    *    active (dp->stack[sp].end is not set) and either 'nsp' or 'end'
+    *    will be updated as appropriate.
+    *
+    * 3) For lower stack entries nsp is set unless the stack entry is
+    *    already at the end.  This means that when all the higher entries
+    *    are popped this entry will be too.
+    */
+   if (sp >= dp->tsp)
+   {
+      push_opt(dp, sp, opt, search); /* This sets tsp to sp+1 */
+      return 1; /* initialized */
+   }
+
+   else
+   {
+      int ret = 0; /* unchanged */
+
+      /* An option that is already on the stack; update best_size and best_val
+       * if appropriate.  On the first run there are no previous values and
+       * dp->write_size will be MAX_SIZE, however on the first run dp->tsp
+       * starts off as 0.
+       */
+      assert(dp->write_size > 0U && dp->write_size < MAX_SIZE);
+
+      if (dp->stack[sp].best_size > dp->write_size ||
+          (dp->stack[sp].best_size == dp->write_size &&
+           compare_option(dp, sp) > 0))
+      {
+         dp->stack[sp].best_size = dp->write_size;
+         dp->stack[sp].best_val = dp->value[opt];
+      }
+
+      if (sp+1U >= dp->tsp)
+      {
+         next_opt(dp, sp);
+         ret = 1; /* advanced */
+      }
+
+      else if (!dp->stack[sp].end) /* Active, not at top of stack */
+         dp->nsp = sp+1U;
+
+      return ret; /* advanced || unchanged */
+   }
+}
+
+static int
+getallopts_(struct display *dp, png_byte opt, int *value, int record)
+   /* Like getop but iterate over all the values if the option was set to "all".
+    */
+{
+   if (dp->entry[opt]) /* option was set on command line */
+   {
+      /* Simple, single value, entries don't have a stack frame and have a fixed
+       * value (it doesn't change once set on the command line).  Otherwise the
+       * value (entry) selected from the command line is 'all':
+       */
+      const char *entry_name = options[opt].values[dp->entry[opt]-1].name;
+
+      if (entry_name == all)
+         (void)advance_opt(dp, opt, 0/*do not search; iterate*/);
+
+      else if (record)
+         record_opt(dp, opt, entry_name);
+
+      *value = dp->value[opt];
+      return 1; /* set */
+   }
+
+   else
+      return 0; /* not set */
+}
+
+static int
+getallopts(struct display *dp, const char *opt_str, int *value)
+{
+   return getallopts_(dp, option_index(dp, opt_str, strlen(opt_str)), value, 0);
+}
+
+static int
+getsearchopts(struct display *dp, const char *opt_str, int *value)
+   /* As above except that if the option was not set try a search */
+{
+   png_byte istrat;
+   png_byte opt = option_index(dp, opt_str, strlen(opt_str));
+   int record = options[opt].search;
+   const char *entry_name;
+
+   /* If it was set on the command line honour the setting, including 'all'
+    * which will override the built in search:
+    */
+   if (getallopts_(dp, opt, value, record))
+      return 1;
+
+   else if (!record) /* not a search option */
+      return 0; /* unset and not searched */
+
+   /* Otherwise decide what to do here. */
+   istrat = OPTIND(dp, strategy);
+   entry_name = range_lo; /* record the value, not the name */
+
+   if (opt == istrat) /* search all strategies */
+      (void)advance_opt(dp, opt, 0/*iterate*/), record=0;
+
+   else if (opt == OPTIND(dp, level))
+   {
+      /* Both RLE and HUFFMAN don't benefit from level increases */
+      if (dp->value[istrat] == Z_RLE || dp->value[istrat] == Z_HUFFMAN_ONLY)
+         dp->value[opt] = 1;
+
+      else /* fixed, filtered or default */
+         (void)advance_opt(dp, opt, 1/*search*/), record=0;
+   }
+
+   else if (opt == OPTIND(dp, windowBits))
+   {
+      /* Changing windowBits for strategies that do not search the window is
+       * pointless.  Huffman-only does not search, RLE only searches backwards
+       * one byte, so given that the maximum string length is 258, a windowBits
+       * of 9 is always sufficient.
+       */
+      if (dp->value[istrat] == Z_HUFFMAN_ONLY)
+         dp->value[opt] = 8;
+
+      else if (dp->value[istrat] == Z_RLE)
+         dp->value[opt] = 9;
+
+      else /* fixed, filtered or default */
+         (void)advance_opt(dp, opt, 1/*search*/), record=0;
+   }
+
+   else if (opt == OPTIND(dp, memLevel))
+   {
+#     if 0
+         (void)advance_opt(dp, opt, 0/*all*/), record=0;
+#     else
+         dp->value[opt] = MAX_MEM_LEVEL;
+#     endif
+   }
+
+   else /* something else */
+      assert(0=="reached");
+
+   if (record)
+      record_opt(dp, opt, entry_name);
+
+   /* One of the above searched options: */
+   *value = dp->value[opt];
+   return 1;
+}
+
+static int
+find_val(struct display *dp, png_byte opt, const char *str, size_t len)
+   /* Like option_index but sets (index+i) of the entry in options[opt] that
+    * matches str[0..len-1] into dp->entry[opt] as well as returning the actual
+    * value.
+    */
+{
+   int rlo = INT_MAX, rhi = INT_MIN;
+   png_byte j, irange = 0;
+
+   for (j=1U; j<=options[opt].value_count; ++j)
+   {
+      if (strncmp(options[opt].values[j-1U].name, str, len) == 0 &&
+          options[opt].values[j-1U].name[len] == 0)
+      {
+         dp->entry[opt] = j;
+         return options[opt].values[j-1U].value;
+      }
+      else if (options[opt].values[j-1U].name == range_lo)
+         rlo = options[opt].values[j-1U].value, irange = j;
+      else if (options[opt].values[j-1U].name == range_hi)
+         rhi = options[opt].values[j-1U].value;
+   }
+
+   /* No match on the name, but there may be a range. */
+   if (irange > 0)
+   {
+      char *ep = NULL;
+      long l = strtol(str, &ep, 0);
+
+      if (ep == str+len && l >= rlo && l <= rhi)
+      {
+         dp->entry[opt] = irange; /* range_lo */
+         return (int)/*SAFE*/l;
+      }
+   }
+
+   display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR,
+         "%s: unknown value setting '%.*s'", options[opt].name,
+         (int)/*SAFE*/len, str);
+   abort(); /* NOT REACHED */
+}
+
+static int
+opt_check(struct display *dp, const char *arg)
+{
+   assert(dp->errset == 0);
+
+   if (arg != NULL && arg[0] == '-' && arg[1] == '-')
+   {
+      int i = 0, negate = (strncmp(arg+2, "no-", 3) == 0), val;
+      png_byte j;
+
+      if (negate)
+         arg += 5; /* --no- */
+
+      else
+         arg += 2; /* -- */
+
+      /* Find the length (expect arg\0 or arg=) */
+      while (arg[i] != 0 && arg[i] != '=') ++i;
+
+      /* So arg[0..i-1] is the argument name, this does not return if this isn't
+       * a valid option name.
+       */
+      j = option_index(dp, arg, i);
+
+      /* It matcheth an option; check the remainder. */
+      if (arg[i] == 0) /* no specified value, use the default */
+      {
+         val = options[j].values[negate].value;
+         dp->entry[j] = (png_byte)/*SAFE*/(negate + 1U);
+      }
+
+      else
+      {
+         const char *list = arg + (i+1);
+
+         /* Expect a single value here unless this is a list, in which case
+          * multiple values are combined.
+          */
+         if (options[j].opt != LIST)
+         {
+            /* find_val sets 'dp->entry[j]' to a non-zero value: */
+            val = find_val(dp, j, list, strlen(list));
+
+            if (negate)
+            {
+               if (options[j].opt < OPTION)
+                  val = !val;
+
+               else
+               {
+                  display_log(dp, USER_ERROR,
+                        "%.*s: option=arg cannot be negated", i, arg);
+                  abort(); /* NOT REACHED */
+               }
+            }
+         }
+
+         else /* multiple options separated by ',' characters */
+         {
+            /* --no-option negates list values from the default, which should
+             * therefore be 'all'.  Notice that if the option list is empty in
+             * this case nothing will be removed and therefore --no-option= is
+             * the same as --option.
+             */
+            if (negate)
+               val = options[j].values[0].value;
+
+            else
+               val = 0;
+
+            while (*list != 0) /* allows option= which sets 0 */
+            {
+               /* A value is terminated by the end of the list or a ','
+                * character.
+                */
+               int v, iv;
+
+               iv = 0; /* an index into 'list' */
+               while (list[++iv] != 0 && list[iv] != ',') {}
+
+               v = find_val(dp, j, list, iv);
+
+               if (negate)
+                  val &= ~v;
+
+               else
+                  val |= v;
+
+               list += iv;
+               if (*list != 0)
+                  ++list; /* skip the ',' */
+            }
+         }
+      }
+
+      /* 'val' is the new value, store it for use later and debugging: */
+      dp->value[j] = val;
+
+      if (options[j].opt < LEVEL_MASK)
+      {
+         /* The handling for error levels is to set the level. */
+         if (val) /* Set this level */
+            dp->options = (dp->options & ~LEVEL_MASK) | options[j].opt;
+
+         else
+            display_log(dp, USER_ERROR,
+      "%.*s: messages cannot be turned off individually; set a message level",
+                  i, arg);
+      }
+
+      else if (options[j].opt < OPTION)
+      {
+         if (val)
+            dp->options |= options[j].opt;
+
+         else
+            dp->options &= ~options[j].opt;
+      }
+
+      return 1; /* this is an option */
+   }
+
+   else
+      return 0; /* not an option */
+}
+
+#ifdef PNG_PNGCP_TIMING_SUPPORTED
+static void
+set_timer(struct display *dp, struct timespec *timer)
+{
+   /* Do the timing using clock_gettime and the per-process timer. */
+   if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, timer))
+   {
+      display_log(dp, APP_ERROR,
+            "CLOCK_PROCESS_CPUTIME_ID: %s: timing disabled\n", strerror(errno));
+      dp->value[OPTIND(dp,time)] = 0; /* i.e. off */
+   }
+}
+
+static void
+start_timer(struct display *dp, int what)
+{
+   if ((dp->value[OPTIND(dp,time)] & what) != 0)
+      set_timer(dp, what == PNGCP_TIME_READ ? &dp->read_time : &dp->write_time);
+}
+
+static void
+end_timer(struct display *dp, int what)
+{
+   if ((dp->value[OPTIND(dp,time)] & what) != 0)
+   {
+      struct timespec t, tmp;
+
+      set_timer(dp, &t);
+
+      if (what == PNGCP_TIME_READ)
+         tmp = dp->read_time;
+
+      else
+         tmp = dp->write_time;
+
+      t.tv_sec -= tmp.tv_sec;
+      t.tv_nsec -= tmp.tv_nsec;
+
+      if (t.tv_nsec < 0)
+      {
+         --(t.tv_sec);
+         t.tv_nsec += 1000000000L;
+      }
+
+      if (what == PNGCP_TIME_READ)
+         dp->read_time = t, tmp = dp->read_time_total;
+
+      else
+         dp->write_time = t, tmp = dp->write_time_total;
+
+      tmp.tv_sec += t.tv_sec;
+      tmp.tv_nsec += t.tv_nsec;
+
+      if (tmp.tv_nsec >= 1000000000L)
+      {
+         ++(tmp.tv_sec);
+         tmp.tv_nsec -= 1000000000L;
+      }
+
+      if (what == PNGCP_TIME_READ)
+         dp->read_time_total = tmp;
+
+      else
+         dp->write_time_total = tmp;
+   }
+}
+
+static void
+print_time(const char *what, struct timespec t)
+{
+   printf("%s %.2lu.%.9ld", what, (unsigned long)t.tv_sec, t.tv_nsec);
+}
+#else /* !PNGCP_TIMING */
+#define start_timer(dp, what) ((void)0)
+#define end_timer(dp, what) ((void)0)
+#endif /* !PNGCP_TIMING */
+
+/* The following is used in main to verify that the final argument is a
+ * directory:
+ */
+static int
+checkdir(const char *pathname)
+{
+   struct stat buf;
+   return stat(pathname, &buf) == 0 && S_ISDIR(buf.st_mode);
+}
+
+/* Work out whether a path is valid (if not a display_log occurs), a directory
+ * (1 is returned) or a file *or* non-existent (0 is returned).
+ *
+ * Used for a write path.
+ */
+static int
+isdir(struct display *dp, const char *pathname)
+{
+   if (pathname == NULL)
+      return 0; /* stdout */
+
+   else if (pathname[0] == 0)
+      return 1; /* empty string */
+
+   else
+   {
+      struct stat buf;
+      int ret = stat(pathname, &buf);
+
+      if (ret == 0) /* the entry exists */
+      {
+         if (S_ISDIR(buf.st_mode))
+            return 1;
+
+         /* Else expect an object that exists and can be written: */
+         if (access(pathname, W_OK) != 0)
+            display_log(dp, USER_ERROR, "%s: cannot be written (%s)", pathname,
+                  strerror(errno));
+
+         return 0; /* file (exists, can be written) */
+      }
+
+      else /* an error */
+      {
+         /* Non-existence is fine, other errors are not: */
+         if (errno != ENOENT)
+            display_log(dp, USER_ERROR, "%s: invalid output name (%s)",
+                  pathname, strerror(errno));
+
+         return 0; /* file (does not exist) */
+      }
+   }
+}
+
+static void
+makename(struct display *dp, const char *dir, const char *infile)
+{
+   /* Make a name for an output file (and check it). */
+   dp->namebuf[0] = 0;
+
+   if (dir == NULL || infile == NULL)
+      display_log(dp, INTERNAL_ERROR, "NULL name to makename");
+
+   else
+   {
+      size_t dsize = strlen(dir);
+
+      if (dsize <= (sizeof dp->namebuf)-2) /* Allow for name + '/' + '\0' */
+      {
+         size_t isize = strlen(infile);
+         size_t istart = isize-1;
+
+         /* This should fail before here: */
+         if (infile[istart] == '/')
+            display_log(dp, INTERNAL_ERROR, "infile with trailing /");
+
+         memcpy(dp->namebuf, dir, dsize);
+         if (dsize > 0 && dp->namebuf[dsize-1] != '/')
+            dp->namebuf[dsize++] = '/';
+
+         /* Find the rightmost non-/ character: */
+         while (istart > 0 && infile[istart-1] != '/')
+            --istart;
+
+         isize -= istart;
+         infile += istart;
+
+         if (dsize+isize < (sizeof dp->namebuf)) /* dsize + infile + '\0' */
+         {
+            memcpy(dp->namebuf+dsize, infile, isize+1);
+
+            if (isdir(dp, dp->namebuf))
+               display_log(dp, USER_ERROR, "%s: output file is a directory",
+                     dp->namebuf);
+         }
+
+         else
+         {
+            dp->namebuf[dsize] = 0; /* allowed for: -2 at start */
+            display_log(dp, USER_ERROR, "%s%s: output file name too long",
+                  dp->namebuf, infile);
+         }
+      }
+
+      else
+         display_log(dp, USER_ERROR, "%s: output directory name too long", dir);
+   }
+}
+
+/* error handler callbacks for libpng */
+static void PNGCBAPI
+display_warning(png_structp pp, png_const_charp warning)
+{
+   struct display *dp = get_dp(pp);
+
+   /* This is used to prevent repeated warnings while searching */
+   if (!dp->no_warnings)
+      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_start_read(struct display *dp, const char *filename)
+{
+   if (filename != NULL)
+   {
+      dp->filename = filename;
+      dp->fp = fopen(filename, "rb");
+   }
+
+   else
+   {
+      dp->filename = "<stdin>";
+      dp->fp = stdin;
+   }
+
+   dp->w = dp->h = 0U;
+   dp->bpp = 0U;
+   dp->size = 0U;
+   dp->read_size = 0U;
+
+   if (dp->fp == NULL)
+      display_log(dp, USER_ERROR, "file open failed (%s)", strerror(errno));
+}
+
+static void PNGCBAPI
+read_function(png_structp pp, png_bytep data, size_t size)
+{
+   struct display *dp = get_dp(pp);
+
+   if (size == 0U || fread(data, size, 1U, dp->fp) == 1U)
+      dp->read_size += size;
+
+   else
+   {
+      if (feof(dp->fp))
+         display_log(dp, LIBPNG_ERROR, "PNG file truncated");
+      else
+         display_log(dp, LIBPNG_ERROR, "PNG file read failed (%s)",
+               strerror(errno));
+   }
+}
+
+static void
+read_png(struct display *dp, const char *filename)
+{
+   display_clean_read(dp); /* safety */
+   display_start_read(dp, filename);
+
+   dp->read_pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, dp,
+      display_error, display_warning);
+   if (dp->read_pp == NULL)
+      display_log(dp, LIBPNG_ERROR, "failed to create read struct");
+
+#  ifdef PNG_BENIGN_ERRORS_SUPPORTED
+      png_set_benign_errors(dp->read_pp, 1/*allowed*/);
+#  endif /* BENIGN_ERRORS */
+
+#  ifdef FIX_INDEX
+      if ((dp->options & FIX_INDEX) != 0)
+         png_set_check_for_invalid_index(dp->read_pp, 1/*on, no warning*/);
+#     ifdef IGNORE_INDEX
+         else
+#     endif /* IGNORE_INDEX */
+#  endif /* FIX_INDEX */
+#  ifdef IGNORE_INDEX
+      if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */
+         png_set_check_for_invalid_index(dp->read_pp, -1/*off completely*/);
+#  endif /* IGNORE_INDEX */
+
+   /* The png_read_png API requires us to make the info struct, but it does the
+    * call to png_read_info.
+    */
+   dp->ip = png_create_info_struct(dp->read_pp);
+   if (dp->ip == NULL)
+      png_error(dp->read_pp, "failed to create info struct");
+
+   /* Set the IO handling */
+   png_set_read_fn(dp->read_pp, dp, read_function);
+
+#  ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      png_set_keep_unknown_chunks(dp->read_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL,
+            0);
+#  endif /* HANDLE_AS_UNKNOWN */
+
+#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
+      /* Remove the user limits, if any */
+      png_set_user_limits(dp->read_pp, 0x7fffffff, 0x7fffffff);
+#  endif /* SET_USER_LIMITS */
+
+   /* Now read the PNG. */
+   start_timer(dp, PNGCP_TIME_READ);
+   png_read_png(dp->read_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
+   end_timer(dp, PNGCP_TIME_READ);
+   dp->w = png_get_image_width(dp->read_pp, dp->ip);
+   dp->h = png_get_image_height(dp->read_pp, dp->ip);
+   dp->ct = png_get_color_type(dp->read_pp, dp->ip);
+   dp->bpp = png_get_bit_depth(dp->read_pp, dp->ip) *
+             png_get_channels(dp->read_pp, dp->ip);
+   {
+      /* png_get_rowbytes should never return 0 because the value is set by the
+       * first call to png_set_IHDR, which should have happened by now, but just
+       * in case:
+       */
+      png_alloc_size_t rb = png_get_rowbytes(dp->read_pp, dp->ip);
+
+      if (rb == 0)
+         png_error(dp->read_pp, "invalid row byte count from libpng");
+
+      /* The size calc can overflow. */
+      if ((MAX_SIZE-dp->h)/rb < dp->h)
+         png_error(dp->read_pp, "image too large");
+
+      dp->size = rb * dp->h + dp->h/*filter byte*/;
+   }
+
+#ifdef FIX_INDEX
+   if (dp->ct == PNG_COLOR_TYPE_PALETTE && (dp->options & FIX_INDEX) != 0)
+   {
+      int max = png_get_palette_max(dp->read_pp, dp->ip);
+      png_colorp palette = NULL;
+      int num = -1;
+
+      if (png_get_PLTE(dp->read_pp, dp->ip, &palette, &num) != PNG_INFO_PLTE
+          || max < 0 || num <= 0 || palette == NULL)
+         display_log(dp, LIBPNG_ERROR, "invalid png_get_PLTE result");
+
+      if (max >= num)
+      {
+         /* 'Fix' the palette. */
+         int i;
+         png_color newpal[256];
+
+         for (i=0; i<num; ++i)
+            newpal[i] = palette[i];
+
+         /* Fill in any remainder with a warning color: */
+         for (; i<=max; ++i)
+         {
+            newpal[i].red = 0xbe;
+            newpal[i].green = 0xad;
+            newpal[i].blue = 0xed;
+         }
+
+         png_set_PLTE(dp->read_pp, dp->ip, newpal, i);
+      }
+   }
+#endif /* FIX_INDEX */
+
+   display_clean_read(dp);
+   dp->operation = "none";
+}
+
+static void
+display_start_write(struct display *dp, const char *filename)
+{
+   assert(dp->fp == NULL);
+
+   if ((dp->options & NOWRITE) != 0)
+      dp->output_file = "<no write>";
+
+   else
+   {
+      if (filename != NULL)
+      {
+         dp->output_file = filename;
+         dp->fp = fopen(filename, "wb");
+      }
+
+      else
+      {
+         dp->output_file = "<stdout>";
+         dp->fp = stdout;
+      }
+
+      if (dp->fp == NULL)
+         display_log(dp, USER_ERROR, "%s: file open failed (%s)",
+               dp->output_file, strerror(errno));
+   }
+}
+
+static void PNGCBAPI
+write_function(png_structp pp, png_bytep data, size_t size)
+{
+   struct display *dp = get_dp(pp);
+
+   /* The write fail is classed as a USER_ERROR, so --quiet does not turn it
+    * off, this seems more likely to be correct.
+    */
+   if (dp->fp == NULL || fwrite(data, size, 1U, dp->fp) == 1U)
+   {
+      dp->write_size += size;
+      if (dp->write_size < size || dp->write_size == MAX_SIZE)
+         png_error(pp, "IDAT size overflow");
+   }
+
+   else
+      display_log(dp, USER_ERROR, "%s: PNG file write failed (%s)",
+            dp->output_file, strerror(errno));
+}
+
+/* Compression option, 'method' is never set: there is no choice.
+ *
+ * IMPORTANT: the order of the entries in this macro determines the preference
+ * order when two different combos of two of these options produce an IDAT of
+ * the same size.  The logic here is to put the things that affect the decoding
+ * of the PNG image ahead of those that are relevant only to the encoding.
+ */
+#define SET_COMPRESSION\
+   SET(strategy, strategy);\
+   SET(windowBits, window_bits);\
+   SET(level, level);\
+   SET(memLevel, mem_level);
+
+#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+static void
+search_compression(struct display *dp)
+{
+   /* Like set_compression below but use a more restricted search than 'all' */
+   int val;
+
+#  define SET(name, func) if (getsearchopts(dp, #name, &val))\
+      png_set_compression_ ## func(dp->write_pp, val);
+   SET_COMPRESSION
+#  undef SET
+}
+
+static void
+set_compression(struct display *dp)
+{
+   int val;
+
+#  define SET(name, func) if (getallopts(dp, #name, &val))\
+      png_set_compression_ ## func(dp->write_pp, val);
+   SET_COMPRESSION
+#  undef SET
+}
+
+#ifdef PNG_SW_COMPRESS_level /* 1.7.0+ */
+static void
+set_ICC_profile_compression(struct display *dp)
+{
+   int val;
+
+#  define SET(name, func) if (getallopts(dp, "ICC-profile-" #name, &val))\
+      png_set_ICC_profile_compression_ ## func(dp->write_pp, val);
+   SET_COMPRESSION
+#  undef SET
+}
+#else
+#  define set_ICC_profile_compression(dp) ((void)0)
+#endif
+#else
+#  define search_compression(dp) ((void)0)
+#  define set_compression(dp) ((void)0)
+#  define set_ICC_profile_compression(dp) ((void)0)
+#endif /* WRITE_CUSTOMIZE_COMPRESSION */
+
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+static void
+set_text_compression(struct display *dp)
+{
+   int val;
+
+#  define SET(name, func) if (getallopts(dp, "text-" #name, &val))\
+      png_set_text_compression_ ## func(dp->write_pp, val);
+   SET_COMPRESSION
+#  undef SET
+}
+#else
+#  define set_text_compression(dp) ((void)0)
+#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
+
+static void
+write_png(struct display *dp, const char *destname)
+{
+   display_clean_write(dp); /* safety */
+   display_start_write(dp, destname);
+
+   dp->write_pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, dp,
+      display_error, display_warning);
+
+   if (dp->write_pp == NULL)
+      display_log(dp, LIBPNG_ERROR, "failed to create write png_struct");
+
+#  ifdef PNG_BENIGN_ERRORS_SUPPORTED
+      png_set_benign_errors(dp->write_pp, 1/*allowed*/);
+#  endif /* BENIGN_ERRORS */
+
+   png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/);
+
+#ifdef IGNORE_INDEX
+   if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */
+      png_set_check_for_invalid_index(dp->write_pp, -1/*off completely*/);
+#endif /* IGNORE_INDEX */
+
+   /* Restore the text chunks when using libpng 1.6 or less; this is a macro
+    * which expands to nothing in 1.7+  In earlier versions it tests
+    * dp->text_stashed, which is only set (below) *after* the first write.
+    */
+   text_restore(dp);
+
+#  ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      png_set_keep_unknown_chunks(dp->write_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL,
+            0);
+#  endif /* HANDLE_AS_UNKNOWN */
+
+#  ifdef PNG_SET_USER_LIMITS_SUPPORTED
+      /* Remove the user limits, if any */
+      png_set_user_limits(dp->write_pp, 0x7fffffff, 0x7fffffff);
+#  endif
+
+   /* OPTION HANDLING */
+   /* compression outputs, IDAT and zTXt/iTXt: */
+   dp->tsp = dp->nsp;
+   dp->nsp = dp->csp = 0;
+#  ifdef PNG_SW_COMPRESS_png_level
+      {
+         int val;
+
+         /* This sets everything, but then the following options just override
+          * the specific settings for ICC profiles and text.
+          */
+         if (getallopts(dp, "compression", &val))
+            png_set_compression(dp->write_pp, val);
+
+         if (getallopts(dp, "ICC-profile-compression", &val))
+            png_set_ICC_profile_compression(dp->write_pp, val);
+
+         if (getallopts(dp, "text-compression", &val))
+            png_set_text_compression(dp->write_pp, val);
+      }
+#  endif /* png_level support */
+   if (dp->options & SEARCH)
+      search_compression(dp);
+   else
+      set_compression(dp);
+   set_ICC_profile_compression(dp);
+   set_text_compression(dp);
+
+   {
+      int val;
+
+      /* The permitted range is 1..0x7FFFFFFF, so the cast is safe */
+      if (get_option(dp, "IDAT-size", &val))
+         png_set_IDAT_size(dp->write_pp, val);
+   }
+
+   /* filter handling */
+#  ifdef PNG_WRITE_FILTER_SUPPORTED
+      {
+         int val;
+
+         if (get_option(dp, "filter", &val))
+            png_set_filter(dp->write_pp, PNG_FILTER_TYPE_BASE, val);
+      }
+#  endif /* WRITE_FILTER */
+
+   /* This just uses the 'read' info_struct directly, it contains the image. */
+   dp->write_size = 0U;
+   start_timer(dp, PNGCP_TIME_WRITE);
+   png_write_png(dp->write_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
+   end_timer(dp, PNGCP_TIME_WRITE);
+
+   /* Make sure the file was written ok: */
+   if (dp->fp != NULL)
+   {
+      FILE *fp = dp->fp;
+      dp->fp = NULL;
+      if (fclose(fp))
+         display_log(dp, APP_ERROR, "%s: write failed (%s)",
+               destname == NULL ? "stdout" : destname, strerror(errno));
+   }
+
+   /* Clean it on the way out - if control returns to the caller then the
+    * written_file contains the required data.
+    */
+   display_clean_write(dp);
+   dp->operation = "none";
+}
+
+static void
+set_windowBits_hi(struct display *dp)
+{
+   /* windowBits is in the range 8..15 but zlib maps '8' to '9' so it is only
+    * worth using if the data size is 256 byte or less.
+    */
+   int wb = MAX_WBITS; /* for large images */
+   int i = VLSIZE(windowBits_IDAT);
+
+   while (wb > 8 && dp->size <= 1U<<(wb-1)) --wb;
+
+   while (--i >= 0) if (VLNAME(windowBits_IDAT)[i].name == range_hi) break;
+
+   assert(i > 1); /* vl_windowBits_IDAT always has a RANGE() */
+   VLNAME(windowBits_IDAT)[i].value = wb;
+
+   assert(VLNAME(windowBits_IDAT)[--i].name == range_lo);
+   VLNAME(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8;
+
+   /* If wb == 8 then any search has been restricted to just one windowBits
+    * entry.  Record that here to avoid producing a spurious app-level warning
+    * above.
+    */
+   if (wb == 8)
+      dp->min_windowBits = OPTIND(dp, windowBits);
+}
+
+static int
+better_options(const struct display *dp)
+{
+   /* Are these options better than the best found so far?  Normally the
+    * options are tested in preference order, best first, however when doing a
+    * search operation on a range the range values are tested out of order.  In
+    * that case preferable options will get tested later.
+    *
+    * This function looks through the stack from the bottom up looking for an
+    * option that does not match the current best value.  When it finds one it
+    * checks to see if it is more or less desirable and returns true or false
+    * as appropriate.
+    *
+    * Notice that this means that the order options are pushed onto the stack
+    * conveys a priority; lower/earlier options are more important than later
+    * ones.
+    */
+   unsigned int sp;
+
+   for (sp=0; sp<dp->csp; ++sp)
+   {
+      int c = compare_option(dp, sp);
+
+      if (c < 0)
+         return 0; /* worse */
+
+      else if (c > 0)
+         return 1; /* better */
+   }
+
+   assert(0 && "unreached");
+}
+
+static void
+print_search_results(struct display *dp)
+{
+   assert(dp->filename != NULL);
+   printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu with '%s'\n",
+      dp->filename, (unsigned long)dp->w, (unsigned long)dp->h, dp->bpp,
+      cts(dp->ct), (unsigned long)dp->size, (unsigned long)dp->read_size,
+      (unsigned long)dp->best_size, dp->best);
+   fflush(stdout);
+}
+
+static void
+log_search(struct display *dp, unsigned int log_depth)
+{
+   /* Log, and reset, the search so far: */
+   if (dp->nsp/*next entry to change*/ <= log_depth)
+   {
+      print_search_results(dp);
+      /* Start again with this entry: */
+      dp->best_size = MAX_SIZE;
+   }
+}
+
+static void
+cp_one_file(struct display *dp, const char *filename, const char *destname)
+{
+   unsigned int log_depth;
+
+   dp->filename = filename;
+   dp->operation = "read";
+   dp->no_warnings = 0;
+
+   /* Read it then write it: */
+   if (filename != NULL && access(filename, R_OK) != 0)
+      display_log(dp, USER_ERROR, "%s: invalid file name (%s)",
+            filename, strerror(errno));
+
+   read_png(dp, filename);
+
+   /* But 'destname' may be a directory. */
+   dp->operation = "write";
+
+   /* Limit the upper end of the windowBits range for this file */
+   set_windowBits_hi(dp);
+
+   /* For logging, depth to log: */
+   {
+      int val;
+
+      if (get_option(dp, "log-depth", &val) && val >= 0)
+         log_depth = (unsigned int)/*SAFE*/val;
+
+      else
+         log_depth = 0U;
+   }
+
+   if (destname != NULL) /* else stdout */
+   {
+      if (isdir(dp, destname))
+      {
+         makename(dp, destname, filename);
+         destname = dp->namebuf;
+      }
+
+      else if (access(destname, W_OK) != 0 && errno != ENOENT)
+         display_log(dp, USER_ERROR, "%s: invalid output name (%s)", destname,
+               strerror(errno));
+   }
+
+   dp->nsp = 0;
+   dp->curr[0] = 0; /* acts as a flag for the caller */
+   dp->opt_string_start = 0;
+   dp->best[0] = 0; /* safety */
+   dp->best_size = MAX_SIZE;
+   write_png(dp, destname);
+
+   /* Initialize the 'best' fields: */
+   strcpy(dp->best, dp->curr);
+   dp->best_size = dp->write_size;
+
+   if (dp->nsp > 0) /* iterating over lists */
+   {
+      char *tmpname, tmpbuf[(sizeof dp->namebuf) + 4];
+      assert(dp->curr[0] == ' ' && dp->tsp > 0);
+
+      /* Cancel warnings on subsequent writes */
+      log_search(dp, log_depth);
+      dp->no_warnings = 1;
+
+      /* Make a temporary name for the subsequent tests: */
+      if (destname != NULL)
+      {
+         strcpy(tmpbuf, destname);
+         strcat(tmpbuf, ".tmp"); /* space for .tmp allocated above */
+         tmpname = tmpbuf;
+      }
+
+      else
+         tmpname = NULL; /* stdout */
+
+      /* Loop to find the best option. */
+      do
+      {
+         write_png(dp, tmpname);
+
+         /* And compare the sizes (the write function makes sure write_size
+          * doesn't overflow.)
+          */
+         assert(dp->csp > 0);
+
+         if (dp->write_size < dp->best_size ||
+             (dp->write_size == dp->best_size && better_options(dp)))
+         {
+            if (destname != NULL && rename(tmpname, destname) != 0)
+               display_log(dp, APP_ERROR, "rename %s %s failed (%s)", tmpname,
+                     destname, strerror(errno));
+
+            strcpy(dp->best, dp->curr);
+            dp->best_size = dp->write_size;
+         }
+
+         else if (tmpname != NULL && unlink(tmpname) != 0)
+            display_log(dp, APP_WARNING, "unlink %s failed (%s)", tmpname,
+                  strerror(errno));
+
+         log_search(dp, log_depth);
+      }
+      while (dp->nsp > 0);
+
+      /* Do this for the 'sizes' option so that it reports the correct size. */
+      dp->write_size = dp->best_size;
+   }
+}
+
+static int
+cppng(struct display *dp, const char *file, const char *gv dest)
+   /* Exists solely to isolate the setjmp clobbers which some versions of GCC
+    * erroneously generate.
+    */
+{
+   int ret = setjmp(dp->error_return);
+
+   if (ret == 0)
+   {
+      dp->errset = 1;
+      cp_one_file(dp, file, dest);
+      dp->errset = 0;
+      return 0;
+   }
+
+   else
+   {
+      dp->errset = 0;
+
+      if (ret < ERRORS) /* shouldn't longjmp on warnings */
+         display_log(dp, INTERNAL_ERROR, "unexpected return code %d", ret);
+
+      return ret;
+   }
+}
+
+int
+main(int argc, char **argv)
+{
+   /* For each file on the command line test it with a range of transforms */
+   int option_end;
+   struct display d;
+
+   display_init(&d);
+
+   d.operation = "options";
+   for (option_end = 1;
+        option_end < argc && opt_check(&d, argv[option_end]);
+        ++option_end)
+   {
+   }
+
+   /* Do a quick check on the directory target case; when there are more than
+    * two arguments the last one must be a directory.
+    */
+   if (!(d.options & NOWRITE) && option_end+2 < argc && !checkdir(argv[argc-1]))
+   {
+      fprintf(stderr,
+            "pngcp: %s: directory required with more than two arguments\n",
+            argv[argc-1]);
+      return 99;
+   }
+
+   {
+      int errors = 0;
+      int i = option_end;
+
+      /* Do this at least once; if there are no arguments stdin/stdout are used.
+       */
+      d.operation = "files";
+      do
+      {
+         const char *infile = NULL;
+         const char *outfile = NULL;
+         int ret;
+
+         if (i < argc)
+         {
+            infile = argv[i++];
+            if (!(d.options & NOWRITE) && i < argc)
+               outfile = argv[argc-1];
+         }
+
+         ret = cppng(&d, infile, outfile);
+
+         if (ret)
+         {
+            if (ret > QUIET) /* abort on user or internal error */
+               return 99;
+
+            /* An error: the output is meaningless */
+         }
+
+         else if (d.best[0] != 0)
+         {
+            /* This result may already have been output, in which case best_size
+             * has been reset.
+             */
+            if (d.best_size < MAX_SIZE)
+               print_search_results(&d);
+         }
+
+         else if (d.options & SIZES)
+         {
+            printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu [0x%lx]\n",
+                  infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
+                  cts(d.ct), (unsigned long)d.size, (unsigned long)d.read_size,
+                  (unsigned long)d.write_size, (unsigned long)d.results);
+            fflush(stdout);
+         }
+
+         /* Here on any return, including failures, except user/internal issues
+          */
+         {
+            int pass = (d.options & STRICT) ?
+               RESULT_STRICT(d.results) : RESULT_RELAXED(d.results);
+
+            if (!pass)
+               ++errors;
+
+            if (d.options & LOG)
+            {
+               int j;
+
+               printf("%s: pngcp", pass ? "PASS" : "FAIL");
+
+               for (j=1; j<option_end; ++j)
+                  printf(" %s", argv[j]);
+
+               if (infile != NULL)
+                  printf(" %s", infile);
+
+#              ifdef PNG_PNGCP_TIMING_SUPPORTED
+                  /* When logging output the files for each file, if enabled. */
+                  if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0)
+                     print_time(" read", d.read_time);
+
+                  if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0)
+                     print_time(" write", d.write_time);
+#              endif /* PNGCP_TIMING */
+
+               printf("\n");
+               fflush(stdout);
+            }
+         }
+
+         display_clean(&d);
+      }
+      while (i+!(d.options & NOWRITE) < argc);
+         /* I.e. for write cases after the first time through the loop require
+          * there to be at least two arguments left and for the last one to be a
+          * directory (this was checked above).
+          */
+
+      /* Release allocated memory */
+      display_destroy(&d);
+
+#     ifdef PNG_PNGCP_TIMING_SUPPORTED
+         {
+            int output = 0;
+
+            if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0)
+               print_time("read", d.read_time_total), output = 1;
+
+            if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0)
+            {
+               if (output) putchar(' ');
+               print_time("write", d.write_time_total);
+               output = 1;
+            }
+
+            if (output) putchar('\n');
+         }
+#     endif /* PNGCP_TIMING */
+
+      return errors != 0;
+   }
+}
+#else /* !READ_PNG || !WRITE_PNG */
+int
+main(void)
+{
+   fprintf(stderr, "pngcp: no support for png_read/write_image\n");
+   return 77;
+}
+#endif /* !READ_PNG || !WRITE_PNG */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/tools/pngfix.c b/ap/lib/libpng/libpng-1.6.37/contrib/tools/pngfix.c
new file mode 100755
index 0000000..2fa5d13
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/tools/pngfix.c
@@ -0,0 +1,4049 @@
+/* pngfix.c
+ *
+ * Last changed in libpng 1.6.31 [July 27, 2017]
+ * Copyright (c) 2014-2017 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
+ *
+ * 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));
+
+      val >>= 16;
+      if (val > 0)
+         a_digits = uarb_mult_digit(acc+1, a_digits-1, num, n_digits,
+            (png_uint_16)val) + 1;
+
+      /* Because n_digits and val are >0 the following must be true: */
+      assert(a_digits > 0);
+   }
+
+   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 if 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, int start)
+   /* 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 (start)
+         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, 1/*start*/);
+
+      /* 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 evaluated 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;
+               }
+               /* FALLTHROUGH */
+
+            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 (;;)
+      {
+         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;
+                  /* FALLTHROUGH */
+
+               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 checksum");
+            break;
+
+
+         case ZLIB_OK:
+            /* Truncated stream; unrecoverable, gets converted to ZLIB_FATAL */
+            zlib.z.msg = PNGZ_MSG_CAST("[truncated]");
+            zlib_message(&zlib, 0/*expected*/);
+            /* FALLTHROUGH */
+
+         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.
+    */
+{
+   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 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);
+            }
+            /* FALLTHROUGH */
+
+         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, 0/*end*/);
+                     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 ancillary 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 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/ap/lib/libpng/libpng-1.6.37/contrib/tools/reindent b/ap/lib/libpng/libpng-1.6.37/contrib/tools/reindent
new file mode 100755
index 0000000..f4df309
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/tools/reindent
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# reindent a libpng C source
+
+# COPYRIGHT: Written by Glenn Randers-Pehrson, 2016.
+# 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.
+
+# Usage:
+# reindent inputtabsize outputtabsize inputcontinuestring outputcontinuestring
+#
+# Assumes that continued lines begin with indentation plus one space, and
+# that continued comments begin with indentation plus " *".
+#
+# eg, to change libpng coding style from 3-space indentation with 4-space
+# continuations to 4-space indentation with 2-space continuations:
+#
+#  reindent 3 4 "\t " "  " < example.c > example.c_4_2
+# and to restore the file back to libpng coding style
+#  reindent 4 3 "  " "    " < example.c_4_2 > example.c_3_4
+
+unexpand --first-only --t $1 | \
+   sed -e "/^	*$3[^\*]/{s/$3/$4/}" | \
+   expand -t $2
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/tools/sRGB.h b/ap/lib/libpng/libpng-1.6.37/contrib/tools/sRGB.h
new file mode 100755
index 0000000..d1ece51
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/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
+ * https://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/ap/lib/libpng/libpng-1.6.37/contrib/visupng/PngFile.c b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/PngFile.c
new file mode 100755
index 0000000..cf943f3
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/PngFile.c
@@ -0,0 +1,454 @@
+/*-------------------------------------
+ *  PNGFILE.C -- Image File Functions
+ *-------------------------------------
+ *
+ * Copyright 2000,2017 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"
+#include "cexcept.h"
+
+define_exception_type(const char *);
+extern struct exception_context the_exception_context[1];
+struct exception_context the_exception_context[1];
+png_const_charp msg;
+
+static OPENFILENAME ofn;
+
+static png_structp png_ptr = NULL;
+static png_infop info_ptr = NULL;
+
+
+/* cexcept interface */
+
+static void
+png_cexcept_error(png_structp png_ptr, png_const_charp msg)
+{
+   if(png_ptr)
+     ;
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+   fprintf(stderr, "libpng error: %s\n", msg);
+#endif
+   {
+      Throw msg;
+   }
+}
+
+/* Windows open-file functions */
+
+void PngFileInitialize (HWND hwnd)
+{
+    static TCHAR szFilter[] = TEXT ("PNG Files (*.PNG)\0*.png\0")
+        TEXT ("All Files (*.*)\0*.*\0\0");
+
+    ofn.lStructSize       = sizeof (OPENFILENAME);
+    ofn.hwndOwner         = hwnd;
+    ofn.hInstance         = NULL;
+    ofn.lpstrFilter       = szFilter;
+    ofn.lpstrCustomFilter = NULL;
+    ofn.nMaxCustFilter    = 0;
+    ofn.nFilterIndex      = 0;
+    ofn.lpstrFile         = NULL;          /* Set in Open and Close functions */
+    ofn.nMaxFile          = MAX_PATH;
+    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.nFileOffset       = 0;
+    ofn.nFileExtension    = 0;
+    ofn.lpstrDefExt       = TEXT ("png");
+    ofn.lCustData         = 0;
+    ofn.lpfnHook          = NULL;
+    ofn.lpTemplateName    = NULL;
+}
+
+BOOL PngFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName)
+{
+    ofn.hwndOwner         = hwnd;
+    ofn.lpstrFile         = pstrFileName;
+    ofn.lpstrFileTitle    = pstrTitleName;
+    ofn.Flags             = OFN_HIDEREADONLY;
+
+    return GetOpenFileName (&ofn);
+}
+
+BOOL PngFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName)
+{
+    ofn.hwndOwner         = hwnd;
+    ofn.lpstrFile         = pstrFileName;
+    ofn.lpstrFileTitle    = pstrTitleName;
+    ofn.Flags             = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
+
+    return GetSaveFileName (&ofn);
+}
+
+/* PNG image handler functions */
+
+BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData,
+                   int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor)
+{
+    static FILE        *pfFile;
+    png_byte            pbSig[8];
+    int                 iBitDepth;
+    int                 iColorType;
+    double              dGamma;
+    png_color_16       *pBackground;
+    png_uint_32         ulChannels;
+    png_uint_32         ulRowBytes;
+    png_byte           *pbImageData = *ppbImageData;
+    static png_byte   **ppbRowPointers = NULL;
+    int                 i;
+
+    /* open the PNG input file */
+
+    if (!pstrFileName)
+    {
+        *ppbImageData = pbImageData = NULL;
+        return FALSE;
+    }
+
+    if (!(pfFile = fopen(pstrFileName, "rb")))
+    {
+        *ppbImageData = pbImageData = NULL;
+        return FALSE;
+    }
+
+    /* first check the eight byte PNG signature */
+
+    fread(pbSig, 1, 8, pfFile);
+    if (png_sig_cmp(pbSig, 0, 8))
+    {
+        *ppbImageData = pbImageData = NULL;
+        return FALSE;
+    }
+
+    /* 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);
+    if (!png_ptr)
+    {
+        *ppbImageData = pbImageData = NULL;
+        return FALSE;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        *ppbImageData = pbImageData = NULL;
+        return FALSE;
+    }
+
+    Try
+    {
+
+        /* 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 */
+
+        png_read_info(png_ptr, info_ptr);
+
+        /* 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 */
+        /* 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)
+            png_set_expand(png_ptr);
+        if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+            png_set_expand(png_ptr);
+        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 */
+        if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
+        {
+            png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+            pBkgColor->red   = (byte) pBackground->red;
+            pBkgColor->green = (byte) pBackground->green;
+            pBkgColor->blue  = (byte) pBackground->blue;
+        }
+        else
+        {
+            pBkgColor = NULL;
+        }
+
+        /* 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 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 */
+
+        png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth,
+            &iColorType, NULL, NULL, NULL);
+
+
+        /* 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 */
+
+        if (pbImageData)
+        {
+            free (pbImageData);
+            pbImageData = NULL;
+        }
+        if ((*piHeight) > ((size_t)(-1))/ulRowBytes) {
+        {
+            png_error(png_ptr, "Visual PNG: image is too big");
+        }
+        if ((pbImageData = (png_byte *) malloc(ulRowBytes * (*piHeight)
+                            * sizeof(png_byte))) == NULL)
+        {
+            png_error(png_ptr, "Visual PNG: out of memory");
+        }
+        *ppbImageData = pbImageData;
+
+        /* 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 */
+
+        for (i = 0; i < (*piHeight); i++)
+            ppbRowPointers[i] = pbImageData + i * ulRowBytes;
+
+        /* 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) */
+
+        png_read_end(png_ptr, NULL);
+
+        /* and we're done */
+
+        free (ppbRowPointers);
+        ppbRowPointers = NULL;
+
+        /* yepp, done */
+    }
+
+    Catch (msg)
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+        *ppbImageData = pbImageData = NULL;
+
+        if(ppbRowPointers)
+            free (ppbRowPointers);
+
+        fclose(pfFile);
+
+        return FALSE;
+    }
+
+    fclose (pfFile);
+
+    return TRUE;
+}
+
+
+BOOL PngSaveImage (PTSTR pstrFileName, png_byte *pDiData,
+                   int iWidth, int iHeight, png_color bkgColor)
+{
+    const int           ciBitDepth = 8;
+    const int           ciChannels = 3;
+
+    static FILE        *pfFile;
+    png_uint_32         ulRowBytes;
+    static png_byte   **ppbRowPointers = NULL;
+    int                 i;
+
+    /* open the PNG output file */
+
+    if (!pstrFileName)
+        return FALSE;
+
+    if (!(pfFile = fopen(pstrFileName, "wb")))
+        return FALSE;
+
+    /* 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);
+    if (!png_ptr)
+    {
+        fclose(pfFile);
+        return FALSE;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        fclose(pfFile);
+        png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+        return FALSE;
+    }
+
+    Try
+    {
+        /* 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 */
+
+        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 */
+
+        png_write_info(png_ptr, info_ptr);
+
+        /* swap the BGR pixels in the DiData structure to RGB */
+
+        png_set_bgr(png_ptr);
+
+        /* row_bytes is the width x number of channels */
+
+        ulRowBytes = iWidth * ciChannels;
+
+        /* 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 */
+
+        for (i = 0; i < iHeight; i++)
+            ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2);
+
+        /* 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) */
+
+        png_write_end(png_ptr, info_ptr);
+
+        /* and we're done */
+
+        free (ppbRowPointers);
+        ppbRowPointers = NULL;
+
+        /* clean up after the write, and free any memory allocated */
+
+        png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+
+        /* yepp, done */
+    }
+
+    Catch (msg)
+    {
+        png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+
+        if(ppbRowPointers)
+            free (ppbRowPointers);
+
+        fclose(pfFile);
+
+        return FALSE;
+    }
+
+    fclose (pfFile);
+
+    return TRUE;
+}
+
+#ifndef PNG_STDIO_SUPPORTED
+
+static void
+png_read_data(png_structp png_ptr, png_bytep data, size_t length)
+{
+   size_t check;
+
+   /* fread() returns 0 on error, so it is OK to store this in a size_t
+    * instead of an int, which is what fread() actually returns.
+    */
+   check = fread(data, 1, length, (FILE *)png_ptr->io_ptr);
+
+   if (check != length)
+   {
+      png_error(png_ptr, "Read Error");
+   }
+}
+
+static void
+png_write_data(png_structp png_ptr, png_bytep data, size_t length)
+{
+   png_uint_32 check;
+
+   check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr));
+   if (check != length)
+   {
+      png_error(png_ptr, "Write Error");
+   }
+}
+
+static void
+png_flush(png_structp png_ptr)
+{
+   FILE *io_ptr;
+   io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr));
+   if (io_ptr != NULL)
+      fflush(io_ptr);
+}
+
+#endif
+
+/*-----------------
+ *  end of source
+ *-----------------
+ */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/PngFile.h b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/PngFile.h
new file mode 100755
index 0000000..1b7b9be
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/PngFile.h
@@ -0,0 +1,30 @@
+/*------------------------------------------*/
+/*  PNGFILE.H -- Header File for pngfile.c*/
+/*------------------------------------------*/
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+
+void PngFileInitialize (HWND hwnd) ;
+BOOL PngFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ;
+BOOL PngFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ;
+
+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);
+
+#ifndef PNG_STDIO_SUPPORTED
+static void png_read_data(png_structp png_ptr, png_bytep data, size_t length);
+static void png_write_data(png_structp png_ptr, png_bytep data, size_t length);
+static void png_flush(png_structp png_ptr);
+#endif
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/README.txt b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/README.txt
new file mode 100755
index 0000000..72c5cba
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/README.txt
@@ -0,0 +1,61 @@
+Microsoft Developer Studio Build File, Format Version 6.00 for VisualPng
+------------------------------------------------------------------------
+
+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
+
+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
+binaries, go to "http://www.schaik.com/png/visualpng.html".
+
+------------------------------------------------------------------------
+
+Assumes that
+
+   libpng DLLs and LIBs are in ..\..\projects\msvc\win32\libpng
+   zlib DLLs and LIBs are in   ..\..\projects\msvc\win32\zlib
+   libpng header files are in  ..\..\..\libpng
+   zlib header files are in    ..\..\..\zlib
+   the pngsuite images are in  ..\pngsuite
+
+To build:
+
+1) On the main menu Select "Build|Set Active configuration".
+   Choose the configuration that corresponds to the library you want to test.
+   This library must have been built using the libpng MS project located in
+   the "..\..\mscv" subdirectory.
+
+2) Select "Build|Clean"
+
+3) Select "Build|Rebuild All"
+
+4) After compiling and linking VisualPng will be started to view an image
+   from the PngSuite directory.  Press Ctrl-N (and Ctrl-V) for other images.
+
+
+To install:
+
+When distributing VisualPng (or a further development) the following options
+are available:
+
+1) Build the program with the configuration "Win32 LIB" and you only need to
+   include the executable from the ./lib directory in your distribution.
+
+2) Build the program with the configuration "Win32 DLL" and you need to put
+   in your distribution the executable from the ./dll directory and the dll's
+   libpng1.dll, zlib.dll and msvcrt.dll.  These need to be in the user's PATH.
+
+
+Willem van Schaik
+Calgary, June 6th 2000
+
+P.S. VisualPng was written based on preliminary work of:
+
+    - Simon-Pierre Cadieux
+    - Glenn Randers-Pehrson
+    - Greg Roelofs
+
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.c b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.c
new file mode 100755
index 0000000..6baa3b6
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.c
@@ -0,0 +1,978 @@
+/*------------------------------------
+ *  VisualPng.C -- Shows a PNG image
+ *------------------------------------
+ *
+ * Copyright 2000,2017 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
+ */
+
+/* switches */
+
+/* defines */
+
+#define PROGNAME  "VisualPng"
+#define LONGNAME  "Win32 Viewer for PNG-files"
+#define VERSION   "1.0 of 2000 June 07"
+
+/* constants */
+
+#define MARGIN 8
+
+/* standard includes */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <zlib.h>
+
+/* application includes */
+
+#include "png.h"
+#include "pngfile.h"
+#include "resource.h"
+
+/* macros */
+
+/* function prototypes */
+
+LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
+BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
+
+BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
+
+BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
+        int *pFileIndex);
+
+BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
+        PTSTR pstrPrevName, PTSTR pstrNextName);
+
+BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
+        png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
+        png_color *pBkgColor);
+
+BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
+        BYTE **ppDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched);
+
+BOOL InitBitmap (
+        BYTE *pDiData, int cxWinSize, int cyWinSize);
+
+BOOL FillBitmap (
+        BYTE *pDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched);
+
+/* a few global variables */
+
+static char *szProgName = PROGNAME;
+static char *szAppName = LONGNAME;
+static char *szIconName = PROGNAME;
+static char szCmdFileName [MAX_PATH];
+
+/* MAIN routine */
+
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                    PSTR szCmdLine, int iCmdShow)
+{
+    HACCEL   hAccel;
+    HWND     hwnd;
+    MSG      msg;
+    WNDCLASS wndclass;
+    int ixBorders, iyBorders;
+
+    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc   = WndProc;
+    wndclass.cbClsExtra    = 0;
+    wndclass.cbWndExtra    = 0;
+    wndclass.hInstance     = hInstance;
+    wndclass.hIcon         = LoadIcon (hInstance, szIconName) ;
+    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
+    wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */
+    wndclass.lpszMenuName  = szProgName;
+    wndclass.lpszClassName = szProgName;
+
+    if (!RegisterClass (&wndclass))
+    {
+        MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"),
+            szProgName, MB_ICONERROR);
+        return 0;
+    }
+
+    /* if filename given on commandline, store it */
+    if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
+        if (szCmdLine[0] == '"')
+            strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
+        else
+            strcpy (szCmdFileName, szCmdLine);
+    else
+        strcpy (szCmdFileName, "");
+
+    /* 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?  */
+
+    hwnd = CreateWindow (szProgName, szAppName,
+        WS_OVERLAPPEDWINDOW,
+        CW_USEDEFAULT, CW_USEDEFAULT,
+        512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
+/*      CW_USEDEFAULT, CW_USEDEFAULT, */
+        NULL, NULL, hInstance, NULL);
+
+    ShowWindow (hwnd, iCmdShow);
+    UpdateWindow (hwnd);
+
+    hAccel = LoadAccelerators (hInstance, szProgName);
+
+    while (GetMessage (&msg, NULL, 0, 0))
+    {
+        if (!TranslateAccelerator (hwnd, hAccel, &msg))
+        {
+            TranslateMessage (&msg);
+            DispatchMessage (&msg);
+        }
+    }
+    return msg.wParam;
+}
+
+LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,
+        LPARAM lParam)
+{
+    static HINSTANCE          hInstance ;
+    static HDC                hdc;
+    static PAINTSTRUCT        ps;
+    static HMENU              hMenu;
+
+    static BITMAPFILEHEADER  *pbmfh;
+    static BITMAPINFOHEADER  *pbmih;
+    static BYTE              *pbImage;
+    static int                cxWinSize, cyWinSize;
+    static int                cxImgSize, cyImgSize;
+    static int                cImgChannels;
+    static png_color          bkgColor = {127, 127, 127};
+
+    static BOOL               bStretched = TRUE;
+
+    static BYTE              *pDib = NULL;
+    static BYTE              *pDiData = NULL;
+
+    static TCHAR              szImgPathName [MAX_PATH];
+    static TCHAR              szTitleName [MAX_PATH];
+
+    static TCHAR             *pPngFileList = NULL;
+    static int                iPngFileCount;
+    static int                iPngFileIndex;
+
+    BOOL                      bOk;
+
+    switch (message)
+    {
+    case WM_CREATE:
+        hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
+        PngFileInitialize (hwnd);
+
+        strcpy (szImgPathName, "");
+
+        /* 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 */
+
+            BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
+                          &iPngFileIndex);
+
+            /* load the image from file */
+
+            if (!LoadImageFile (hwnd, szImgPathName,
+                &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
+                return 0;
+
+            /* invalidate the client area for later update */
+
+            InvalidateRect (hwnd, NULL, TRUE);
+
+            /* display the PNG into the DIBitmap */
+
+            DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+        }
+
+        return 0;
+
+    case WM_SIZE:
+        cxWinSize = LOWORD (lParam);
+        cyWinSize = HIWORD (lParam);
+
+        /* invalidate the client area for later update */
+
+        InvalidateRect (hwnd, NULL, TRUE);
+
+        /* display the PNG into the DIBitmap */
+
+        DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+            pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+
+        return 0;
+
+    case WM_INITMENUPOPUP:
+        hMenu = GetMenu (hwnd);
+
+        if (pbImage)
+            EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED);
+        else
+            EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED);
+
+        return 0;
+
+    case WM_COMMAND:
+        hMenu = GetMenu (hwnd);
+
+        switch (LOWORD (wParam))
+        {
+        case IDM_FILE_OPEN:
+
+            /* 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 */
+
+            BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
+                          &iPngFileIndex);
+
+            /* load the image from file */
+
+            if (!LoadImageFile (hwnd, szImgPathName,
+                &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
+                return 0;
+
+            /* invalidate the client area for later update */
+
+            InvalidateRect (hwnd, NULL, TRUE);
+
+            /* display the PNG into the DIBitmap */
+
+            DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+
+            return 0;
+
+        case IDM_FILE_SAVE:
+
+            /* show the File Save dialog box */
+
+            if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
+                return 0;
+
+            /* save the PNG to a disk file */
+
+            SetCursor (LoadCursor (NULL, IDC_WAIT));
+            ShowCursor (TRUE);
+
+            bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize,
+                  bkgColor);
+
+            ShowCursor (FALSE);
+            SetCursor (LoadCursor (NULL, IDC_ARROW));
+
+            if (!bOk)
+                MessageBox (hwnd, TEXT ("Error in saving the PNG image"),
+                szProgName, MB_ICONEXCLAMATION | MB_OK);
+            return 0;
+
+        case IDM_FILE_NEXT:
+
+            /* read next entry in the directory */
+
+            if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
+                NULL, szImgPathName))
+            {
+                if (strcmp (szImgPathName, "") == 0)
+                    return 0;
+
+                /* load the image from file */
+
+                if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
+                        &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
+                    return 0;
+
+                /* invalidate the client area for later update */
+
+                InvalidateRect (hwnd, NULL, TRUE);
+
+                /* 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 */
+
+            if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
+                szImgPathName, NULL))
+            {
+
+                if (strcmp (szImgPathName, "") == 0)
+                    return 0;
+
+                /* load the image from file */
+
+                if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
+                    &cyImgSize, &cImgChannels, &bkgColor))
+                    return 0;
+
+                /* invalidate the client area for later update */
+
+                InvalidateRect (hwnd, NULL, TRUE);
+
+                /* display the PNG into the DIBitmap */
+
+                DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                    pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+            }
+
+            return 0;
+
+        case IDM_FILE_EXIT:
+
+            /* more cleanup needed... */
+
+            /* free image buffer */
+
+            if (pDib != NULL)
+            {
+                free (pDib);
+                pDib = NULL;
+            }
+
+            /* free file-list */
+
+            if (pPngFileList != NULL)
+            {
+                free (pPngFileList);
+                pPngFileList = NULL;
+            }
+
+            /* let's go ... */
+
+            exit (0);
+
+            return 0;
+
+        case IDM_OPTIONS_STRETCH:
+            bStretched = !bStretched;
+            if (bStretched)
+                CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED);
+            else
+                CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
+
+            /* invalidate the client area for later update */
+
+            InvalidateRect (hwnd, NULL, TRUE);
+
+            /* display the PNG into the DIBitmap */
+
+            DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+
+            return 0;
+
+        case IDM_HELP_ABOUT:
+            DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
+            return 0;
+
+        } /* end switch */
+
+        break;
+
+    case WM_PAINT:
+        hdc = BeginPaint (hwnd, &ps);
+
+        if (pDib)
+            SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0,
+                0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS);
+
+        EndPaint (hwnd, &ps);
+        return 0;
+
+    case WM_DESTROY:
+        if (pbmfh)
+        {
+            free (pbmfh);
+            pbmfh = NULL;
+        }
+
+        PostQuitMessage (0);
+        return 0;
+    }
+
+    return DefWindowProc (hwnd, message, wParam, lParam);
+}
+
+BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,
+                            WPARAM wParam, LPARAM lParam)
+{
+     switch (message)
+     {
+     case WM_INITDIALOG :
+          ShowWindow (hDlg, SW_HIDE);
+          CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER));
+          ShowWindow (hDlg, SW_SHOW);
+          return TRUE ;
+
+     case WM_COMMAND :
+          switch (LOWORD (wParam))
+          {
+          case IDOK :
+          case IDCANCEL :
+               EndDialog (hDlg, 0) ;
+               return TRUE ;
+          }
+          break ;
+     }
+     return FALSE ;
+}
+
+/*---------------
+ *  CenterAbout
+ *---------------
+ */
+BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
+{
+   RECT    rChild, rParent, rWorkArea;
+   int     wChild, hChild, wParent, hParent;
+   int     xNew, yNew;
+   BOOL  bResult;
+
+   /* 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 */
+   GetWindowRect (hwndParent, &rParent);
+   wParent = rParent.right - rParent.left;
+   hParent = rParent.bottom - rParent.top;
+
+   /* Get the limits of the 'workarea' */
+   bResult = SystemParametersInfo(
+      SPI_GETWORKAREA,  /* system parameter to query or set */
+      sizeof(RECT),
+      &rWorkArea,
+      0);
+   if (!bResult) {
+      rWorkArea.left = rWorkArea.top = 0;
+      rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
+      rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
+   }
+
+   /* Calculate new X position, then adjust for workarea */
+   xNew = rParent.left + ((wParent - wChild) /2);
+   if (xNew < rWorkArea.left) {
+      xNew = rWorkArea.left;
+   } else if ((xNew+wChild) > rWorkArea.right) {
+      xNew = rWorkArea.right - wChild;
+   }
+
+   /* Calculate new Y position, then adjust for workarea */
+   yNew = rParent.top  + ((hParent - hChild) /2);
+   if (yNew < rWorkArea.top) {
+      yNew = rWorkArea.top;
+   } else if ((yNew+hChild) > rWorkArea.bottom) {
+      yNew = rWorkArea.bottom - hChild;
+   }
+
+   /* Set it, and return */
+   return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
+          SWP_NOZORDER);
+}
+
+/*----------------
+ *  BuildPngList
+ *----------------
+ */
+BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
+     int *pFileIndex)
+{
+    static TCHAR              szImgPathName [MAX_PATH];
+    static TCHAR              szImgFileName [MAX_PATH];
+    static TCHAR              szImgFindName [MAX_PATH];
+
+    WIN32_FIND_DATA           finddata;
+    HANDLE                    hFind;
+
+    static TCHAR              szTmp [MAX_PATH];
+    BOOL                      bOk;
+    int                       i, ii;
+    int                       j, jj;
+
+    /* free previous file-list */
+
+    if (*ppFileList != NULL)
+    {
+        free (*ppFileList);
+        *ppFileList = NULL;
+    }
+
+    /* extract foldername, filename and search-name */
+
+    strcpy (szImgPathName, pstrPathName);
+    strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
+
+    strcpy (szImgFindName, szImgPathName);
+    *(strrchr (szImgFindName, '\\') + 1) = '\0';
+    strcat (szImgFindName, "*.png");
+
+    /* first cycle: count number of files in directory for memory allocation */
+
+    *pFileCount = 0;
+
+    hFind = FindFirstFile(szImgFindName, &finddata);
+    bOk = (hFind != (HANDLE) -1);
+
+    while (bOk)
+    {
+        *pFileCount += 1;
+        bOk = FindNextFile(hFind, &finddata);
+    }
+    FindClose(hFind);
+
+    /* allocation memory for file-list */
+
+    *ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
+
+    /* second cycle: read directory and store filenames in file-list */
+
+    hFind = FindFirstFile(szImgFindName, &finddata);
+    bOk = (hFind != (HANDLE) -1);
+
+    i = 0;
+    ii = 0;
+    while (bOk)
+    {
+        strcpy (*ppFileList + ii, szImgPathName);
+        strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName);
+
+        if (strcmp(pstrPathName, *ppFileList + ii) == 0)
+            *pFileIndex = i;
+
+        ii += MAX_PATH;
+        i++;
+
+        bOk = FindNextFile(hFind, &finddata);
+    }
+    FindClose(hFind);
+
+    /* finally we must sort the file-list */
+
+    for (i = 0; i < *pFileCount - 1; i++)
+    {
+        ii = i * MAX_PATH;
+        for (j = i+1; j < *pFileCount; j++)
+        {
+            jj = j * MAX_PATH;
+            if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0)
+            {
+                strcpy (szTmp, *ppFileList + jj);
+                strcpy (*ppFileList + jj, *ppFileList + ii);
+                strcpy (*ppFileList + ii, szTmp);
+
+                /* check if this was the current image that we moved */
+
+                if (*pFileIndex == i)
+                    *pFileIndex = j;
+                else
+                    if (*pFileIndex == j)
+                        *pFileIndex = i;
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+/*----------------
+ *  SearchPngList
+ *----------------
+ */
+
+BOOL SearchPngList (
+        TCHAR *pFileList, int FileCount, int *pFileIndex,
+        PTSTR pstrPrevName, PTSTR pstrNextName)
+{
+    if (FileCount > 0)
+    {
+        /* get previous entry */
+
+        if (pstrPrevName != NULL)
+        {
+            if (*pFileIndex > 0)
+                *pFileIndex -= 1;
+            else
+                *pFileIndex = FileCount - 1;
+
+            strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
+        }
+
+        /* get next entry */
+
+        if (pstrNextName != NULL)
+        {
+            if (*pFileIndex < FileCount - 1)
+                *pFileIndex += 1;
+            else
+                *pFileIndex = 0;
+
+            strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
+        }
+
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+/*-----------------
+ *  LoadImageFile
+ *-----------------
+ */
+
+BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
+                png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
+                int *piChannels, png_color *pBkgColor)
+{
+    static TCHAR szTmp [MAX_PATH];
+
+    /* if there's an existing PNG, free the memory */
+
+    if (*ppbImage)
+    {
+        free (*ppbImage);
+        *ppbImage = NULL;
+    }
+
+    /* Load the entire PNG into memory */
+
+    SetCursor (LoadCursor (NULL, IDC_WAIT));
+    ShowCursor (TRUE);
+
+    PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels,
+                  pBkgColor);
+
+    ShowCursor (FALSE);
+    SetCursor (LoadCursor (NULL, IDC_ARROW));
+
+    if (*ppbImage != NULL)
+    {
+        sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1);
+        SetWindowText (hwnd, szTmp);
+    }
+    else
+    {
+        MessageBox (hwnd, TEXT ("Error in loading the PNG image"),
+            szProgName, MB_ICONEXCLAMATION | MB_OK);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*----------------
+ *  DisplayImage
+ *----------------
+ */
+BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
+        BYTE **ppDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched)
+{
+    BYTE                       *pDib = *ppDib;
+    BYTE                       *pDiData = *ppDiData;
+    /* 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 Independent bitmap */
+
+    wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
+
+    if (pDib)
+    {
+        free (pDib);
+        pDib = NULL;
+    }
+
+    if (cyWinSize > ((size_t)(-1))/wDIRowBytes) {
+    {
+        MessageBox (hwnd, TEXT ("Visual PNG: image is too big");
+    }
+    if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) +
+        wDIRowBytes * cyWinSize)))
+    {
+        MessageBox (hwnd, TEXT ("Error in displaying the PNG image"),
+            szProgName, MB_ICONEXCLAMATION | MB_OK);
+        *ppDib = pDib = NULL;
+        return FALSE;
+    }
+    *ppDib = pDib;
+    memset (pDib, 0, sizeof(BITMAPINFOHEADER));
+
+    /* initialize the dib-structure */
+
+    pbmih = (BITMAPINFOHEADER *) pDib;
+    pbmih->biSize = sizeof(BITMAPINFOHEADER);
+    pbmih->biWidth = cxWinSize;
+    pbmih->biHeight = -((long) cyWinSize);
+    pbmih->biPlanes = 1;
+    pbmih->biBitCount = 24;
+    pbmih->biCompression = 0;
+    pDiData = pDib + sizeof(BITMAPINFOHEADER);
+    *ppDiData = pDiData;
+
+    /* first fill bitmap with gray and image border */
+
+    InitBitmap (pDiData, cxWinSize, cyWinSize);
+
+    /* then fill bitmap with image */
+
+    if (pbImage)
+    {
+        FillBitmap (
+            pDiData, cxWinSize, cyWinSize,
+            pbImage, cxImgSize, cyImgSize, cImgChannels,
+            bStretched);
+    }
+
+    return TRUE;
+}
+
+/*--------------
+ *  InitBitmap
+ *--------------
+ */
+BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
+{
+    BYTE *dst;
+    int x, y, col;
+
+    /* initialize the background with gray */
+
+    dst = pDiData;
+    for (y = 0; y < cyWinSize; y++)
+    {
+        col = 0;
+        for (x = 0; x < cxWinSize; x++)
+        {
+            /* fill with GRAY */
+            *dst++ = 127;
+            *dst++ = 127;
+            *dst++ = 127;
+            col += 3;
+        }
+        /* rows start on 4 byte boundaries */
+        while ((col % 4) != 0)
+        {
+            dst++;
+            col++;
+        }
+    }
+
+    return TRUE;
+}
+
+/*--------------
+ *  FillBitmap
+ *--------------
+ */
+BOOL FillBitmap (
+        BYTE *pDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched)
+{
+    BYTE *pStretchedImage;
+    BYTE *pImg;
+    BYTE *src, *dst;
+    BYTE r, g, b, a;
+    const int cDIChannels = 3;
+    WORD wImgRowBytes;
+    WORD wDIRowBytes;
+    int cxNewSize, cyNewSize;
+    int cxImgPos, cyImgPos;
+    int xImg, yImg;
+    int xWin, yWin;
+    int xOld, yOld;
+    int xNew, yNew;
+
+    if (bStretched)
+    {
+        cxNewSize = cxWinSize - 2 * MARGIN;
+        cyNewSize = cyWinSize - 2 * MARGIN;
+
+        /* stretch the image to it's window determined size */
+
+        /* 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;
+            cxImgPos = MARGIN;
+            cyImgPos = (cyWinSize - cyNewSize) / 2;
+        }
+        else
+        {
+            cxNewSize = cyNewSize * cxImgSize / cyImgSize;
+            cyImgPos = MARGIN;
+            cxImgPos = (cxWinSize - cxNewSize) / 2;
+        }
+
+        if (cyNewSize > ((size_t)(-1))/(cImgChannels * cxNewSize)) {
+        {
+            MessageBox (hwnd, TEXT ("Visual PNG: stretched image is too big");
+        }
+        pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize);
+        pImg = pStretchedImage;
+
+        for (yNew = 0; yNew < cyNewSize; yNew++)
+        {
+            yOld = yNew * cyImgSize / cyNewSize;
+            for (xNew = 0; xNew < cxNewSize; xNew++)
+            {
+                xOld = xNew * cxImgSize / cxNewSize;
+
+                r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0);
+                g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1);
+                b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2);
+                *pImg++ = r;
+                *pImg++ = g;
+                *pImg++ = b;
+                if (cImgChannels == 4)
+                {
+                    a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld)
+                        + 3);
+                    *pImg++ = a;
+                }
+            }
+        }
+
+        /* calculate row-bytes */
+
+        wImgRowBytes = cImgChannels * cxNewSize;
+        wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
+
+        /* copy image to screen */
+
+        for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
+        {
+            if (yWin >= cyWinSize - cyImgPos)
+                break;
+            src = pStretchedImage + yImg * wImgRowBytes;
+            dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
+
+            for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++)
+            {
+                if (xWin >= cxWinSize - cxImgPos)
+                    break;
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                *dst++ = b; /* note the reverse order  */
+                *dst++ = g;
+                *dst++ = r;
+                if (cImgChannels == 4)
+                {
+                    a = *src++;
+                }
+            }
+        }
+
+        /* free memory */
+
+        if (pStretchedImage != NULL)
+        {
+            free (pStretchedImage);
+            pStretchedImage = NULL;
+        }
+
+    }
+
+    /* process the image not-stretched */
+
+    else
+    {
+        /* calculate the central position */
+
+        cxImgPos = (cxWinSize - cxImgSize) / 2;
+        cyImgPos = (cyWinSize - cyImgSize) / 2;
+
+        /* check for image larger than window */
+
+        if (cxImgPos < MARGIN)
+            cxImgPos = MARGIN;
+        if (cyImgPos < MARGIN)
+            cyImgPos = MARGIN;
+
+        /* calculate both row-bytes */
+
+        wImgRowBytes = cImgChannels * cxImgSize;
+        wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
+
+        /* copy image to screen */
+
+        for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
+        {
+            if (yWin >= cyWinSize - MARGIN)
+                break;
+            src = pbImage + yImg * wImgRowBytes;
+            dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
+
+            for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++)
+            {
+                if (xWin >= cxWinSize - MARGIN)
+                    break;
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                *dst++ = b; /* note the reverse order  */
+                *dst++ = g;
+                *dst++ = r;
+                if (cImgChannels == 4)
+                {
+                    a = *src++;
+                }
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+/*-----------------
+ *  end of source
+ *-----------------
+ */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.dsp b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.dsp
new file mode 100755
index 0000000..741ef6c
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.dsp
@@ -0,0 +1,147 @@
+# Microsoft Developer Studio Project File - Name="VisualPng" - Package Owner=<4>

+# Microsoft Developer Studio Generated Build File, Format Version 6.00

+# ** DO NOT EDIT **

+

+# TARGTYPE "Win32 (x86) Application" 0x0101

+

+CFG=VisualPng - Win32 Debug

+!MESSAGE This is not a valid makefile. To build this project using NMAKE,

+!MESSAGE use the Export Makefile command and run

+!MESSAGE 

+!MESSAGE NMAKE /f "VisualPng.mak".

+!MESSAGE 

+!MESSAGE You can specify a configuration when running NMAKE

+!MESSAGE by defining the macro CFG on the command line. For example:

+!MESSAGE 

+!MESSAGE NMAKE /f "VisualPng.mak" CFG="VisualPng - Win32 Debug"

+!MESSAGE 

+!MESSAGE Possible choices for configuration are:

+!MESSAGE 

+!MESSAGE "VisualPng - Win32 Release" (based on "Win32 (x86) Application")

+!MESSAGE "VisualPng - Win32 Debug" (based on "Win32 (x86) Application")

+!MESSAGE 

+

+# Begin Project

+# PROP AllowPerConfigDependencies 0

+# PROP Scc_ProjName ""

+# PROP Scc_LocalPath ""

+CPP=cl.exe

+MTL=midl.exe

+RSC=rc.exe

+

+!IF  "$(CFG)" == "VisualPng - Win32 Release"

+

+# PROP BASE Use_MFC 0

+# PROP BASE Use_Debug_Libraries 0

+# PROP BASE Output_Dir "Release"

+# PROP BASE Intermediate_Dir "Release"

+# PROP BASE Ignore_Export_Lib 0

+# PROP BASE Target_Dir ""

+# PROP Use_MFC 0

+# PROP Use_Debug_Libraries 0

+# PROP Output_Dir "Release"

+# 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" /FD /c

+# SUBTRACT BASE CPP /YX

+# 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

+# ADD BASE RSC /l 0x409 /d "NDEBUG"

+# ADD RSC /l 0x409 /d "NDEBUG"

+BSC32=bscmake.exe

+# ADD BASE BSC32 /nologo

+# ADD BSC32 /nologo

+LINK32=link.exe

+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386

+# ADD LINK32 ..\..\projects\visualc6\Win32_LIB_Release\libpng.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_Release\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386

+# Begin Special Build Tool

+OutDir=.\Release

+SOURCE="$(InputPath)"

+PostBuild_Cmds=$(outdir)\VisualPng.exe ..\..\contrib\pngsuite\basn6a16.png

+# End Special Build Tool

+

+!ELSEIF  "$(CFG)" == "VisualPng - Win32 Debug"

+

+# PROP BASE Use_MFC 0

+# PROP BASE Use_Debug_Libraries 1

+# PROP BASE Output_Dir "Debug"

+# PROP BASE Intermediate_Dir "Debug"

+# PROP BASE Ignore_Export_Lib 0

+# PROP BASE Target_Dir ""

+# PROP Use_MFC 0

+# PROP Use_Debug_Libraries 1

+# PROP Output_Dir "Debug"

+# 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" /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" /FD /GZ /c

+# SUBTRACT CPP /YX

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

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

+# ADD BASE RSC /l 0x409 /d "_DEBUG"

+# ADD RSC /l 0x409 /d "_DEBUG"

+BSC32=bscmake.exe

+# ADD BASE BSC32 /nologo

+# ADD BSC32 /nologo

+LINK32=link.exe

+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept

+# ADD LINK32 ..\..\projects\visualc6\Win32_LIB_Release\libpng.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_Release\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /pdbtype:sept

+# Begin Special Build Tool

+OutDir=.\Debug

+SOURCE="$(InputPath)"

+PostBuild_Cmds=$(outdir)\VisualPng.exe ..\..\contrib\pngsuite\basn6a16.png

+# End Special Build Tool

+

+!ENDIF 

+

+# Begin Target

+

+# Name "VisualPng - Win32 Release"

+# Name "VisualPng - Win32 Debug"

+# Begin Group "Source Files"

+

+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"

+# Begin Source File

+

+SOURCE=.\PngFile.c

+# End Source File

+# Begin Source File

+

+SOURCE=.\VisualPng.c

+# End Source File

+# End Group

+# Begin Group "Header Files"

+

+# PROP Default_Filter "h;hpp;hxx;hm;inl"

+# Begin Source File

+

+SOURCE=.\cexcept.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\PngFile.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\resource.h

+# End Source File

+# End Group

+# Begin Group "Resource Files"

+

+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"

+# Begin Source File

+

+SOURCE=.\VisualPng.ico

+# End Source File

+# Begin Source File

+

+SOURCE=.\VisualPng.rc

+# End Source File

+# End Group

+# End Target

+# End Project

diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.dsw b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.dsw
new file mode 100755
index 0000000..a30e1cc
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00

+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!

+

+###############################################################################

+

+Project: "VisualPng"=.\VisualPng.dsp - Package Owner=<4>

+

+Package=<5>

+{{{

+}}}

+

+Package=<4>

+{{{

+}}}

+

+###############################################################################

+

+Global:

+

+Package=<5>

+{{{

+}}}

+

+Package=<3>

+{{{

+}}}

+

+###############################################################################

+

diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.ico b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.ico
new file mode 100755
index 0000000..68aa371
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.ico
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.png b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.png
new file mode 100755
index 0000000..c6aa80a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.png
Binary files differ
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.rc b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.rc
new file mode 100755
index 0000000..6e0623a
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/VisualPng.rc
@@ -0,0 +1,152 @@
+//Microsoft Developer Studio generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE DISCARDABLE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE DISCARDABLE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE DISCARDABLE 

+BEGIN

+    "\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Menu

+//

+

+VISUALPNG MENU DISCARDABLE 

+BEGIN

+    POPUP "&File"

+    BEGIN

+        MENUITEM "&Open Image...\tCtrl+O",      IDM_FILE_OPEN

+        MENUITEM "Save &As...",                 IDM_FILE_SAVE

+        MENUITEM SEPARATOR

+        MENUITEM "&Next Image\tCtrl+N",         IDM_FILE_NEXT

+        MENUITEM "Pre&vious Image\tCtrl+V",     IDM_FILE_PREVIOUS

+        MENUITEM SEPARATOR

+        MENUITEM "E&xit\tAlt+X",                IDM_FILE_EXIT

+    END

+    POPUP "&Options"

+    BEGIN

+        MENUITEM "&Stretch",                    IDM_OPTIONS_STRETCH, CHECKED

+    END

+    POPUP "&Help"

+    BEGIN

+        MENUITEM "&About",                      IDM_HELP_ABOUT

+    END

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Accelerator

+//

+

+VISUALPNG ACCELERATORS DISCARDABLE 

+BEGIN

+    "N",            IDM_FILE_NEXT,          VIRTKEY, CONTROL, NOINVERT

+    "O",            IDM_FILE_OPEN,          VIRTKEY, CONTROL, NOINVERT

+    "P",            IDM_FILE_PREVIOUS,      VIRTKEY, CONTROL, NOINVERT

+    "V",            IDM_FILE_PREVIOUS,      VIRTKEY, CONTROL, NOINVERT

+    "X",            IDM_FILE_EXIT,          VIRTKEY, ALT, NOINVERT

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Icon

+//

+

+// Icon with lowest ID value placed first to ensure application icon

+// remains consistent on all systems.

+VISUALPNG               ICON    DISCARDABLE     "VisualPng.ico"

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Dialog

+//

+

+ABOUTBOX DIALOG DISCARDABLE  0, 0, 186, 94

+STYLE DS_MODALFRAME | WS_POPUP

+FONT 8, "MS Sans Serif"

+BEGIN

+    DEFPUSHBUTTON   "OK",IDOK,68,67,50,14

+    CTEXT           "VisualPng 1.0  -  June 2000",IDC_STATIC,49,14,88,8

+    LTEXT           "a PNG image viewer",IDC_STATIC,60,30,66,8

+    LTEXT           "(c) Willem van Schaik, 2000",IDC_STATIC,48,52,90,8

+    LTEXT           "to demonstrate the use of libpng in Visual C",

+                    IDC_STATIC,25,38,136,8

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// DESIGNINFO

+//

+

+#ifdef APSTUDIO_INVOKED

+GUIDELINES DESIGNINFO DISCARDABLE 

+BEGIN

+    "ABOUTBOX", DIALOG

+    BEGIN

+        LEFTMARGIN, 7

+        RIGHTMARGIN, 179

+        TOPMARGIN, 7

+        BOTTOMMARGIN, 87

+    END

+END

+#endif    // APSTUDIO_INVOKED

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/cexcept.h b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/cexcept.h
new file mode 100755
index 0000000..5f45d76
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/cexcept.h
@@ -0,0 +1,248 @@
+/*===
+cexcept.h 2.0.1 (2008-Jul-19-Sat)
+http://www.nicemice.net/cexcept/
+Adam M. Costello
+http://www.nicemice.net/amc/
+
+An interface for exception-handling in ANSI C (C89 and subsequent ISO
+standards), developed jointly with Cosmin Truta.
+
+    Copyright (c) 2000-2008 Adam M. Costello and Cosmin Truta.
+    This software may be modified only if its author and version
+    information is updated accurately, and may be redistributed
+    only if accompanied by this unaltered notice.  Subject to those
+    restrictions, permission is granted to anyone to do anything
+    with this software.  The copyright holders make no guarantees
+    regarding this software, and are not responsible for any damage
+    resulting from its use.
+
+The cexcept interface is not compatible with and cannot interact
+with system exceptions (like division by zero or memory segmentation
+violation), compiler-generated exceptions (like C++ exceptions), or
+other exception-handling interfaces.
+
+When using this interface across multiple .c files, do not include
+this header file directly.  Instead, create a wrapper header file that
+includes this header file and then invokes the define_exception_type
+macro (see below).  The .c files should then include that header file.
+
+The interface consists of one type, one well-known name, and six macros.
+
+
+define_exception_type(type_name);
+
+    This macro is used like an external declaration.  It specifies
+    the type of object that gets copied from the exception thrower to
+    the exception catcher.  The type_name can be any type that can be
+    assigned to, that is, a non-constant arithmetic type, struct, union,
+    or pointer.  Examples:
+
+        define_exception_type(int);
+
+        enum exception { out_of_memory, bad_arguments, disk_full };
+        define_exception_type(enum exception);
+
+        struct exception { int code; const char *msg; };
+        define_exception_type(struct exception);
+
+    Because throwing an exception causes the object to be copied (not
+    just once, but twice), programmers may wish to consider size when
+    choosing the exception type.
+
+
+struct exception_context;
+
+    This type may be used after the define_exception_type() macro has
+    been invoked.  A struct exception_context must be known to both
+    the thrower and the catcher.  It is expected that there be one
+    context for each thread that uses exceptions.  It would certainly
+    be dangerous for multiple threads to access the same context.
+    One thread can use multiple contexts, but that is likely to be
+    confusing and not typically useful.  The application can allocate
+    this structure in any way it pleases--automatic, static, or dynamic.
+    The application programmer should pretend not to know the structure
+    members, which are subject to change.
+
+
+struct exception_context *the_exception_context;
+
+    The Try/Catch and Throw statements (described below) implicitly
+    refer to a context, using the name the_exception_context.  It is
+    the application's responsibility to make sure that this name yields
+    the address of a mutable (non-constant) struct exception_context
+    wherever those statements are used.  Subject to that constraint, the
+    application may declare a variable of this name anywhere it likes
+    (inside a function, in a parameter list, or externally), and may
+    use whatever storage class specifiers (static, extern, etc) or type
+    qualifiers (const, volatile, etc) it likes.  Examples:
+
+        static struct exception_context
+          * const the_exception_context = &foo;
+
+        { struct exception_context *the_exception_context = bar; ... }
+
+        int blah(struct exception_context *the_exception_context, ...);
+
+        extern struct exception_context the_exception_context[1];
+
+    The last example illustrates a trick that avoids creating a pointer
+    object separate from the structure object.
+
+    The name could even be a macro, for example:
+
+        struct exception_context ec_array[numthreads];
+        #define the_exception_context (ec_array + thread_id)
+
+    Be aware that the_exception_context is used several times by the
+    Try/Catch/Throw macros, so it shouldn't be expensive or have side
+    effects.  The expansion must be a drop-in replacement for an
+    identifier, so it's safest to put parentheses around it.
+
+
+void init_exception_context(struct exception_context *ec);
+
+    For context structures allocated statically (by an external
+    definition or using the "static" keyword), the implicit
+    initialization to all zeros is sufficient, but contexts allocated
+    by other means must be initialized using this macro before they
+    are used by a Try/Catch statement.  It does no harm to initialize
+    a context more than once (by using this macro on a statically
+    allocated context, or using this macro twice on the same context),
+    but a context must not be re-initialized after it has been used by a
+    Try/Catch statement.
+
+
+Try statement
+Catch (expression) statement
+
+    The Try/Catch/Throw macros are capitalized in order to avoid
+    confusion with the C++ keywords, which have subtly different
+    semantics.
+
+    A Try/Catch statement has a syntax similar to an if/else statement,
+    except that the parenthesized expression goes after the second
+    keyword rather than the first.  As with if/else, there are two
+    clauses, each of which may be a simple statement ending with a
+    semicolon or a brace-enclosed compound statement.  But whereas
+    the else clause is optional, the Catch clause is required.  The
+    expression must be a modifiable lvalue (something capable of being
+    assigned to) of the same type (disregarding type qualifiers) that
+    was passed to define_exception_type().
+
+    If a Throw that uses the same exception context as the Try/Catch is
+    executed within the Try clause (typically within a function called
+    by the Try clause), and the exception is not caught by a nested
+    Try/Catch statement, then a copy of the exception will be assigned
+    to the expression, and control will jump to the Catch clause.  If no
+    such Throw is executed, then the assignment is not performed, and
+    the Catch clause is not executed.
+
+    The expression is not evaluated unless and until the exception is
+    caught, which is significant if it has side effects, for example:
+
+        Try foo();
+        Catch (p[++i].e) { ... }
+
+    IMPORTANT: Jumping into or out of a Try clause (for example via
+    return, break, continue, goto, longjmp) is forbidden--the compiler
+    will not complain, but bad things will happen at run-time.  Jumping
+    into or out of a Catch clause is okay, and so is jumping around
+    inside a Try clause.  In many cases where one is tempted to return
+    from a Try clause, it will suffice to use Throw, and then return
+    from the Catch clause.  Another option is to set a flag variable and
+    use goto to jump to the end of the Try clause, then check the flag
+    after the Try/Catch statement.
+
+    IMPORTANT: The values of any non-volatile automatic variables
+    changed within the Try clause are undefined after an exception is
+    caught.  Therefore, variables modified inside the Try block whose
+    values are needed later outside the Try block must either use static
+    storage or be declared with the "volatile" type qualifier.
+
+
+Throw expression;
+
+    A Throw statement is very much like a return statement, except that
+    the expression is required.  Whereas return jumps back to the place
+    where the current function was called, Throw jumps back to the Catch
+    clause of the innermost enclosing Try clause.  The expression must
+    be compatible with the type passed to define_exception_type().  The
+    exception must be caught, otherwise the program may crash.
+
+    Slight limitation:  If the expression is a comma-expression, it must
+    be enclosed in parentheses.
+
+
+Try statement
+Catch_anonymous statement
+
+    When the value of the exception is not needed, a Try/Catch statement
+    can use Catch_anonymous instead of Catch (expression).
+
+
+Everything below this point is for the benefit of the compiler.  The
+application programmer should pretend not to know any of it, because it
+is subject to change.
+
+===*/
+
+
+#ifndef CEXCEPT_H
+#define CEXCEPT_H
+
+
+#include <setjmp.h>
+
+#define define_exception_type(etype) \
+struct exception_context { \
+  jmp_buf *penv; \
+  int caught; \
+  volatile struct { etype etmp; } v; \
+}
+
+/* etmp must be volatile because the application might use automatic */
+/* storage for the_exception_context, and etmp is modified between   */
+/* the calls to setjmp() and longjmp().  A wrapper struct is used to */
+/* avoid warnings about a duplicate volatile qualifier in case etype */
+/* already includes it.                                              */
+
+#define init_exception_context(ec) ((void)((ec)->penv = 0))
+
+#define Try \
+  { \
+    jmp_buf *exception__prev, exception__env; \
+    exception__prev = the_exception_context->penv; \
+    the_exception_context->penv = &exception__env; \
+    if (setjmp(exception__env) == 0) { \
+      do
+
+#define exception__catch(action) \
+      while (the_exception_context->caught = 0, \
+             the_exception_context->caught); \
+    } \
+    else { \
+      the_exception_context->caught = 1; \
+    } \
+    the_exception_context->penv = exception__prev; \
+  } \
+  if (!the_exception_context->caught || action) { } \
+  else
+
+#define Catch(e) exception__catch(((e) = the_exception_context->v.etmp, 0))
+#define Catch_anonymous exception__catch(0)
+
+/* Try ends with do, and Catch begins with while(0) and ends with     */
+/* else, to ensure that Try/Catch syntax is similar to if/else        */
+/* syntax.                                                            */
+/*                                                                    */
+/* The 0 in while(0) is expressed as x=0,x in order to appease        */
+/* compilers that warn about constant expressions inside while().     */
+/* Most compilers should still recognize that the condition is always */
+/* false and avoid generating code for it.                            */
+
+#define Throw \
+  for (;; longjmp(*the_exception_context->penv, 1)) \
+    the_exception_context->v.etmp =
+
+
+#endif /* CEXCEPT_H */
diff --git a/ap/lib/libpng/libpng-1.6.37/contrib/visupng/resource.h b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/resource.h
new file mode 100755
index 0000000..b62176d
--- /dev/null
+++ b/ap/lib/libpng/libpng-1.6.37/contrib/visupng/resource.h
@@ -0,0 +1,23 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by VisualPng.rc
+//
+#define IDM_FILE_OPEN                   40001
+#define IDM_FILE_SAVE                   40002
+#define IDM_FILE_NEXT                   40003
+#define IDM_FILE_PREVIOUS               40004
+#define IDM_FILE_EXIT                   40005
+#define IDM_OPTIONS_BACKGROUND          40006
+#define IDM_OPTIONS_STRETCH             40007
+#define IDM_HELP_ABOUT                  40008
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        113
+#define _APS_NEXT_COMMAND_VALUE         40009
+#define _APS_NEXT_CONTROL_VALUE         1001
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif