Imported Upstream version 1.6.40
[platform/upstream/libpng.git] / contrib / libtests / pngvalid.c
index e8401c9..6a7422e 100644 (file)
@@ -1,9 +1,8 @@
 
 /* pngvalid.c - validate libpng by constructing then reading png files.
  *
- * Last changed in libpng 1.6.10 [March 6, 2014]
- * Copyright (c) 2014 Glenn Randers-Pehrson
- * Written by John Cunningham Bowler
+ * Copyright (c) 2021 Cosmin Truta
+ * Copyright (c) 2014-2017 John Cunningham Bowler
  *
  * This code is released under the libpng license.
  * For conditions of distribution and use, see the disclaimer
@@ -22,6 +21,8 @@
 #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 */
+#define _DEFAULT_SOURCE 1 /* For the floating point exception extension */
 
 #include <signal.h>
 #include <stdio.h>
 #  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:
  */
 /* 1.6.1 added support for the configure test harness, which uses 77 to indicate
  * a skipped test, in earlier versions we need to succeed on a skipped test, so:
  */
-#if PNG_LIBPNG_VER < 10601
-#  define SKIP 0
-#else
+#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.
@@ -64,7 +75,7 @@
    (defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED))
 
 #if PNG_LIBPNG_VER < 10500
-/* This deliberately lacks the PNG_CONST. */
+/* This deliberately lacks the const. */
 typedef png_byte *png_const_bytep;
 
 /* This is copied from 1.5.1 png.h: */
@@ -93,7 +104,7 @@ typedef png_byte *png_const_bytep;
 #define PNG_WRITE_16BIT_SUPPORTED
 #define PNG_READ_16BIT_SUPPORTED
 
-/* This comes from pnglibconf.h afer 1.5: */
+/* 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))
@@ -106,11 +117,32 @@ typedef png_byte *png_const_bytep;
 #  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.
  */
@@ -147,6 +179,31 @@ define_exception_type(struct png_store*);
    &(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
@@ -155,7 +212,7 @@ define_exception_type(struct png_store*);
  * warning messages into buffers that are too small.
  */
 static size_t safecat(char *buffer, size_t bufsize, size_t pos,
-   PNG_CONST char *cat)
+   const char *cat)
 {
    while (pos < bufsize && cat != NULL && *cat != 0)
       buffer[pos++] = *cat++;
@@ -184,16 +241,16 @@ static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d,
 }
 #endif
 
-static PNG_CONST char invalid[] = "invalid";
-static PNG_CONST char sep[] = ": ";
+static const char invalid[] = "invalid";
+static const char sep[] = ": ";
 
-static PNG_CONST char *colour_types[8] =
+static const char *colour_types[8] =
 {
    "grayscale", invalid, "truecolour", "indexed-colour",
    "grayscale with alpha", invalid, "truecolour with alpha", invalid
 };
 
-#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
 /* Convert a double precision value to fixed point. */
 static png_fixed_point
 fix(double d)
@@ -241,7 +298,8 @@ make_four_random_bytes(png_uint_32* seed, png_bytep bytes)
    make_random_bytes(seed, bytes, 4);
 }
 
-#ifdef PNG_READ_SUPPORTED
+#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED ||\
+    defined PNG_WRITE_FILTER_SUPPORTED
 static void
 randomize(void *pv, size_t size)
 {
@@ -249,35 +307,62 @@ randomize(void *pv, size_t size)
    make_random_bytes(random_seed, pv, size);
 }
 
-#define RANDOMIZE(this) randomize(&(this), sizeof (this))
+#define R8(this) randomize(&(this), sizeof (this))
 
-static unsigned int
-random_mod(unsigned int max)
+#ifdef PNG_READ_SUPPORTED
+static png_byte
+random_byte(void)
 {
-   unsigned int x;
+   unsigned char b1[1];
+   randomize(b1, sizeof b1);
+   return b1[0];
+}
+#endif /* READ */
 
-   RANDOMIZE(x);
+static png_uint_16
+random_u16(void)
+{
+   unsigned char b2[2];
+   randomize(b2, sizeof b2);
+   return png_get_uint_16(b2);
+}
 
-   return x % max; /* 0 .. max-1 */
+#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 */
 
-#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+#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)
 {
-   unsigned char x;
-
-   RANDOMIZE(x);
-
-   return x & 1;
+   return random_byte() & 1;
 }
-#endif
-#endif /* PNG_READ_SUPPORTED */
+#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 (0 otherwise.)
+ * 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) + \
@@ -286,7 +371,7 @@ random_choice(void)
 #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) ((int)(((id) >> 13) & 0x3))
+#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)
@@ -298,12 +383,16 @@ standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
     png_uint_32 w, png_uint_32 h, int do_interlace)
 {
    pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
-   if (npalette > 0)
+   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");
@@ -360,25 +449,32 @@ standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
 
 static int
 next_format(png_bytep colour_type, png_bytep bit_depth,
-   unsigned int* palette_number, int no_low_depth_gray)
+   unsigned int* palette_number, int low_depth_gray, int tRNS)
 {
    if (*bit_depth == 0)
    {
       *colour_type = 0;
-      if (no_low_depth_gray)
-         *bit_depth = 8;
-      else
+      if (low_depth_gray)
          *bit_depth = 1;
+      else
+         *bit_depth = 8;
       *palette_number = 0;
       return 1;
    }
 
-   if (*colour_type == 3)
+   if  (*colour_type < 4/*no alpha channel*/)
    {
-      /* Add multiple palettes for colour type 3. */
-      if (++*palette_number < PALETTE_COUNT(*bit_depth))
+      /* 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;
    }
 
@@ -423,7 +519,7 @@ next_format(png_bytep colour_type, png_bytep bit_depth,
 #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)
+    png_uint_32 x, unsigned int sample_index, int swap16, int littleendian)
 {
    png_uint_32 bit_index, result;
 
@@ -452,11 +548,23 @@ sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
       return result;
 
    else if (bit_depth > 8)
-      return (result << 8) + *++row;
+   {
+      if (swap16)
+         return (*++row << 8) + result;
+      else
+         return (result << 8) + *++row;
+   }
 
-   /* Less than 8 bits per sample. */
+   /* 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;
-   return (result >> (8-bit_index-bit_depth)) & ((1U<<bit_depth)-1);
+
+   if (!littleendian)
+      bit_index = 8-bit_index-bit_depth;
+
+   return (result >> bit_index) & ((1U<<bit_depth)-1);
 }
 #endif /* PNG_READ_TRANSFORMS_SUPPORTED */
 
@@ -470,7 +578,8 @@ sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
  */
 static void
 pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
-   png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize)
+   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.
@@ -480,15 +589,25 @@ pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
    if (pixelSize < 8) /* Sub-byte */
    {
       /* Mask to select the location of the copied pixel: */
-      unsigned int destMask = ((1U<<pixelSize)-1) << (8-pixelSize-(toIndex&7));
+      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 (fromIndex > 0) sourceByte <<= fromIndex;
-      if ((toIndex & 7) > 0) sourceByte >>= toIndex & 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));
    }
@@ -501,7 +620,8 @@ pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
  * bytes at the end.
  */
 static void
-row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth)
+row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth,
+      int littleendian)
 {
    memcpy(toBuffer, fromBuffer, bitWidth >> 3);
 
@@ -511,10 +631,10 @@ row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth)
 
       toBuffer += bitWidth >> 3;
       fromBuffer += bitWidth >> 3;
-      /* The remaining bits are in the top of the byte, the mask is the bits to
-       * retain.
-       */
-      mask = 0xff >> (bitWidth & 7);
+      if (littleendian)
+         mask = 0xff << (bitWidth & 7);
+      else
+         mask = 0xff >> (bitWidth & 7);
       *toBuffer = (png_byte)((*toBuffer & mask) | (*fromBuffer & ~mask));
    }
 }
@@ -590,8 +710,10 @@ 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) */
-   png_size_t              datacount; /* In this (the last) buffer */
+   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 */
@@ -645,15 +767,25 @@ typedef struct png_store
    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 */
-   png_size_t         readpos;  /* Position in *next */
+   size_t             readpos;  /* Position in *next */
    png_byte*          image;    /* Buffer for reading interlaced images */
-   png_size_t         cb_image; /* Size of this buffer */
-   png_size_t         cb_row;   /* Row size of the image(s) */
+   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;
 
@@ -661,7 +793,7 @@ typedef struct png_store
    png_store_file*    saved;
    png_structp        pwrite;   /* Used when writing a new file */
    png_infop          piwrite;
-   png_size_t         writepos; /* Position in .new */
+   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;
@@ -678,7 +810,7 @@ store_pool_mark(png_bytep mark)
    make_four_random_bytes(store_seed, mark);
 }
 
-#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
 /* Use this for random 32 bit values; this function makes sure the result is
  * non-zero.
  */
@@ -686,7 +818,7 @@ static png_uint_32
 random_32(void)
 {
 
-   for(;;)
+   for (;;)
    {
       png_byte mark[4];
       png_uint_32 result;
@@ -740,6 +872,11 @@ store_init(png_store* ps)
    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;
@@ -762,6 +899,11 @@ 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);
@@ -775,9 +917,6 @@ store_storenew(png_store *ps)
 {
    png_store_buffer *pb;
 
-   if (ps->writepos != STORE_BUFFER_SIZE)
-      png_error(ps->pwrite, "invalid store call");
-
    pb = voidcast(png_store_buffer*, malloc(sizeof *pb));
 
    if (pb == NULL)
@@ -808,21 +947,52 @@ store_freefile(png_store_file **ppf)
    }
 }
 
-/* Main interface to file storeage, after writing a new PNG file (see the API
+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 = voidcast(png_store_file*, malloc(sizeof *pf));
+   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;
@@ -836,7 +1006,7 @@ store_storefile(png_store *ps, png_uint_32 id)
 /* 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, PNG_CONST char *msg)
+   size_t pos, const char *msg)
 {
    if (pp != NULL && pp == ps->pread)
    {
@@ -954,10 +1124,10 @@ store_warning(png_structp ppIn, png_const_charp message)
  */
 /* Return a single row from the correct image. */
 static png_bytep
-store_image_row(PNG_CONST png_store* ps, png_const_structp pp, int nImage,
+store_image_row(const png_store* ps, png_const_structp pp, int nImage,
    png_uint_32 y)
 {
-   png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
+   size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
 
    if (ps->image == NULL)
       png_error(pp, "no allocated image");
@@ -992,9 +1162,9 @@ store_image_free(png_store *ps, png_const_structp pp)
 
 static void
 store_ensure_image(png_store *ps, png_const_structp pp, int nImages,
-   png_size_t cbRow, png_uint_32 cRows)
+   size_t cbRow, png_uint_32 cRows)
 {
-   png_size_t cb = nImages * cRows * (cbRow + 5);
+   size_t cb = nImages * cRows * (cbRow + 5);
 
    if (ps->cb_image < cb)
    {
@@ -1058,7 +1228,7 @@ store_ensure_image(png_store *ps, png_const_structp pp, int nImages,
 
 #ifdef PNG_READ_SUPPORTED
 static void
-store_image_check(PNG_CONST png_store* ps, png_const_structp pp, int iImage)
+store_image_check(const png_store* ps, png_const_structp pp, int iImage)
 {
    png_const_bytep image = ps->image;
 
@@ -1066,14 +1236,14 @@ store_image_check(PNG_CONST png_store* ps, png_const_structp pp, int iImage)
       png_error(pp, "image overwrite");
    else
    {
-      png_size_t cbRow = ps->cb_row;
+      size_t cbRow = ps->cb_row;
       png_uint_32 rows = ps->image_h;
 
       image += iImage * (cbRow+5) * ps->image_h;
 
       image += 2; /* skip image first row markers */
 
-      while (rows-- > 0)
+      for (; rows > 0; --rows)
       {
          if (image[-2] != 190 || image[-1] != 239)
             png_error(pp, "row start overwritten");
@@ -1088,32 +1258,119 @@ store_image_check(PNG_CONST png_store* ps, png_const_structp pp, int iImage)
 }
 #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, png_size_t st)
+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)
    {
-      size_t cb;
+      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");
 
-      if (ps->writepos >= STORE_BUFFER_SIZE)
-         store_storenew(ps);
+            chunktype &= 0xffffffffU;
+            if (chunktype == CHUNK_IDAT)
+            {
+               if (chunklen > ~ps->IDAT_size)
+                  png_error(pp, "pngvalid internal image too large");
 
-      cb = st;
+               ps->IDAT_size += chunklen;
+            }
 
-      if (cb > STORE_BUFFER_SIZE - ps->writepos)
-         cb = STORE_BUFFER_SIZE - ps->writepos;
+            else if (!valid_chunktype(chunktype))
+               png_error(pp, "invalid chunk type");
 
-      memcpy(ps->new.buffer + ps->writepos, pb, cb);
-      pb += cb;
-      st -= cb;
-      ps->writepos += cb;
-   }
+            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
@@ -1133,7 +1390,6 @@ store_read_buffer_size(png_store *ps)
    return ps->current->datacount;
 }
 
-#ifdef PNG_READ_TRANSFORMS_SUPPORTED
 /* Return total bytes available for read. */
 static size_t
 store_read_buffer_avail(png_store *ps)
@@ -1158,7 +1414,6 @@ store_read_buffer_avail(png_store *ps)
 
    return 0;
 }
-#endif
 
 static int
 store_read_buffer_next(png_store *ps)
@@ -1187,7 +1442,7 @@ store_read_buffer_next(png_store *ps)
  * during progressive read, where the io_ptr is set internally by libpng.
  */
 static void
-store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
+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");
@@ -1210,8 +1465,241 @@ store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
    }
 }
 
+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, png_size_t st)
+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));
@@ -1219,26 +1707,33 @@ store_read(png_structp ppIn, png_bytep pb, png_size_t st)
    if (ps == NULL || ps->pread != pp)
       png_error(pp, "bad store read call");
 
-   store_read_imp(ps, pb, st);
+   store_read_chunk(ps, pb, st, st);
 }
 
 static void
 store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
 {
-   /* Notice that a call to store_read will cause this function to fail because
-    * readpos will be set.
-    */
    if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
       png_error(pp, "store state damaged (progressive)");
 
-   do
+   /* 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)
    {
-      if (ps->readpos != 0)
-         png_error(pp, "store_read called during progressive read");
+      static png_uint_32 noise = 2;
+      size_t cb;
+      png_byte buffer[512];
 
-      png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
+      /* 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);
    }
-   while (store_read_buffer_next(ps));
 }
 #endif /* PNG_READ_SUPPORTED */
 
@@ -1275,7 +1770,10 @@ store_current_palette(png_store *ps, int *npalette)
     * 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;
@@ -1305,7 +1803,7 @@ typedef struct store_memory
  * all the memory.
  */
 static void
-store_pool_error(png_store *ps, png_const_structp pp, PNG_CONST char *msg)
+store_pool_error(png_store *ps, png_const_structp pp, const char *msg)
 {
    if (pp != NULL)
       png_error(pp, msg);
@@ -1369,7 +1867,7 @@ store_pool_delete(png_store *ps, store_pool *pool)
          next->next = NULL;
 
          fprintf(stderr, "\t%lu bytes @ %p\n",
-             (unsigned long)next->size, (PNG_CONST void*)(next+1));
+             (unsigned long)next->size, (const void*)(next+1));
          /* The NULL means this will always return, even if the memory is
           * corrupted.
           */
@@ -1524,8 +2022,7 @@ store_write_reset(png_store *ps)
  * returned libpng structures as destroyed by store_write_reset above.
  */
 static png_structp
-set_store_for_write(png_store *ps, png_infopp ppi,
-   PNG_CONST char * volatile name)
+set_store_for_write(png_store *ps, png_infopp ppi, const char *name)
 {
    anon_context(ps);
 
@@ -1607,6 +2104,11 @@ store_read_reset(png_store *ps)
    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
@@ -1621,6 +2123,11 @@ store_read_set(png_store *ps, png_uint_32 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;
       }
@@ -1645,7 +2152,7 @@ store_read_set(png_store *ps, png_uint_32 id)
  */
 static png_structp
 set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
-   PNG_CONST char *name)
+   const char *name)
 {
    /* Set the name for png_error */
    safecat(ps->test, sizeof ps->test, 0, name);
@@ -1752,6 +2259,7 @@ typedef struct color_encoding
 } color_encoding;
 
 #ifdef PNG_READ_SUPPORTED
+#if defined PNG_READ_TRANSFORMS_SUPPORTED && defined PNG_READ_cHRM_SUPPORTED
 static double
 chromaticity_x(CIE_color c)
 {
@@ -1765,7 +2273,7 @@ chromaticity_y(CIE_color c)
 }
 
 static CIE_color
-white_point(PNG_CONST color_encoding *encoding)
+white_point(const color_encoding *encoding)
 {
    CIE_color white;
 
@@ -1775,12 +2283,13 @@ white_point(PNG_CONST color_encoding *encoding)
 
    return white;
 }
+#endif /* READ_TRANSFORMS && READ_cHRM */
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
 static void
 normalize_color_encoding(color_encoding *encoding)
 {
-   PNG_CONST double whiteY = encoding->red.Y + encoding->green.Y +
+   const double whiteY = encoding->red.Y + encoding->green.Y +
       encoding->blue.Y;
 
    if (whiteY != 1)
@@ -1798,9 +2307,10 @@ normalize_color_encoding(color_encoding *encoding)
 }
 #endif
 
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
 static size_t
 safecat_color_encoding(char *buffer, size_t bufsize, size_t pos,
-   PNG_CONST color_encoding *e, double encoding_gamma)
+   const color_encoding *e, double encoding_gamma)
 {
    if (e != 0)
    {
@@ -1837,6 +2347,7 @@ safecat_color_encoding(char *buffer, size_t bufsize, size_t pos,
 
    return pos;
 }
+#endif /* READ_TRANSFORMS */
 #endif /* PNG_READ_SUPPORTED */
 
 typedef struct png_modifier
@@ -1861,9 +2372,9 @@ typedef struct png_modifier
    unsigned int              ngammas;
    unsigned int              ngamma_tests;     /* Number of gamma tests to run*/
    double                    current_gamma;    /* 0 if not set */
-   PNG_CONST color_encoding *encodings;
+   const color_encoding *encodings;
    unsigned int              nencodings;
-   PNG_CONST color_encoding *current_encoding; /* If an encoding has been set */
+   const color_encoding *current_encoding; /* If an encoding has been set */
    unsigned int              encoding_counter; /* For iteration */
    int                       encoding_ignored; /* Something overwrote it */
 
@@ -1874,7 +2385,7 @@ typedef struct png_modifier
    unsigned int              repeat :1;   /* Repeat this transform test. */
    unsigned int              test_uses_encoding :1;
 
-   /* Lowest sbit to test (libpng fails for sbit < 8) */
+   /* 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
@@ -1894,6 +2405,8 @@ typedef struct png_modifier
     * 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 */
 
@@ -1929,6 +2442,7 @@ typedef struct png_modifier
 
    /* 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
@@ -1960,6 +2474,16 @@ typedef struct png_modifier
    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
@@ -2012,6 +2536,11 @@ modifier_init(png_modifier *pm)
    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;
@@ -2024,6 +2553,11 @@ modifier_init(png_modifier *pm)
    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;
 
@@ -2057,7 +2591,7 @@ static double digitize(double value, int depth, int do_round)
     * rounding and 'do_round' should be 1, if it is 0 the digitized value will
     * be truncated.
     */
-   PNG_CONST unsigned int digitization_factor = (1U << depth) -1;
+   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.
@@ -2076,7 +2610,7 @@ static double digitize(double value, int depth, int do_round)
 #endif /* RGB_TO_GRAY */
 
 #ifdef PNG_READ_GAMMA_SUPPORTED
-static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+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.
@@ -2088,7 +2622,7 @@ static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
       return pm->maxabs8;
 }
 
-static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+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).
@@ -2101,7 +2635,7 @@ static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
       return pm->maxcalc8;
 }
 
-static double pcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+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.
@@ -2124,7 +2658,7 @@ static double pcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
  * The specified parameter does *not* include the base .5 digitization error but
  * it is added here.
  */
-static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+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
@@ -2156,7 +2690,7 @@ static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
  * 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(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
+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:
@@ -2194,7 +2728,7 @@ static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
  * but in the 8 bit calculation case it's actually quantization to a multiple of
  * 257!
  */
-static int output_quantization_factor(PNG_CONST png_modifier *pm, int in_depth,
+static int output_quantization_factor(const png_modifier *pm, int in_depth,
    int out_depth)
 {
    if (out_depth == 16 && in_depth != 16 &&
@@ -2258,7 +2792,7 @@ modification_init(png_modification *pmm)
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
 static void
-modifier_current_encoding(PNG_CONST png_modifier *pm, color_encoding *ce)
+modifier_current_encoding(const png_modifier *pm, color_encoding *ce)
 {
    if (pm->current_encoding != 0)
       *ce = *pm->current_encoding;
@@ -2270,9 +2804,10 @@ modifier_current_encoding(PNG_CONST png_modifier *pm, color_encoding *ce)
 }
 #endif
 
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
 static size_t
 safecat_current_encoding(char *buffer, size_t bufsize, size_t pos,
-   PNG_CONST png_modifier *pm)
+   const png_modifier *pm)
 {
    pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding,
       pm->current_gamma);
@@ -2282,6 +2817,7 @@ safecat_current_encoding(char *buffer, size_t bufsize, size_t pos,
 
    return pos;
 }
+#endif
 
 /* Iterate through the usefully testable color encodings.  An encoding is one
  * of:
@@ -2301,7 +2837,7 @@ safecat_current_encoding(char *buffer, size_t bufsize, size_t pos,
  * caller of modifier_reset must reset it at the start of each run of the test!
  */
 static unsigned int
-modifier_total_encodings(PNG_CONST png_modifier *pm)
+modifier_total_encodings(const png_modifier *pm)
 {
    return 1 +                 /* (1) nothing */
       pm->ngammas +           /* (2) gamma values to test */
@@ -2417,29 +2953,18 @@ modifier_set_encoding(png_modifier *pm)
  * assumption below that the first encoding in the list is the one for sRGB.
  */
 static int
-modifier_color_encoding_is_sRGB(PNG_CONST png_modifier *pm)
+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(PNG_CONST png_modifier *pm)
+modifier_color_encoding_is_set(const png_modifier *pm)
 {
    return pm->current_gamma != 0;
 }
 
-/* Convenience macros. */
-#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
-#define CHUNK_IHDR CHUNK(73,72,68,82)
-#define CHUNK_PLTE CHUNK(80,76,84,69)
-#define CHUNK_IDAT CHUNK(73,68,65,84)
-#define CHUNK_IEND CHUNK(73,69,78,68)
-#define CHUNK_cHRM CHUNK(99,72,82,77)
-#define CHUNK_gAMA CHUNK(103,65,77,65)
-#define CHUNK_sBIT CHUNK(115,66,73,84)
-#define CHUNK_sRGB CHUNK(115,82,71,66)
-
 /* The guts of modification are performed during a read. */
 static void
 modifier_crc(png_bytep buffer)
@@ -2467,7 +2992,7 @@ modifier_setbuffer(png_modifier *pm)
  * png_struct.
  */
 static void
-modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
+modifier_read_imp(png_modifier *pm, png_bytep pb, size_t st)
 {
    while (st > 0)
    {
@@ -2479,7 +3004,7 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
       {
          static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
          case modifier_start:
-            store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
+            store_read_chunk(&pm->this, pm->buffer, 8, 8); /* signature. */
             pm->buffer_count = 8;
             pm->buffer_position = 0;
 
@@ -2489,7 +3014,7 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
             break;
 
          case modifier_signature:
-            store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
+            store_read_chunk(&pm->this, pm->buffer, 13+12, 13+12); /* IHDR */
             pm->buffer_count = 13+12;
             pm->buffer_position = 0;
 
@@ -2530,7 +3055,7 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
             {
                if (cb > st) cb = st;
                pm->flush -= cb;
-               store_read_imp(&pm->this, pb, cb);
+               store_read_chunk(&pm->this, pb, cb, cb);
                pb += cb;
                st -= cb;
                if (st == 0) return;
@@ -2547,7 +3072,7 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
                pm->pending_chunk = 0;
             }
             else
-               store_read_imp(&pm->this, pm->buffer, 8);
+               store_read_chunk(&pm->this, pm->buffer, 8, 8);
 
             pm->buffer_count = 8;
             pm->buffer_position = 0;
@@ -2613,8 +3138,8 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
              */
             if (len+12 <= sizeof pm->buffer)
             {
-               store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
-                   len+12-pm->buffer_count);
+               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. */
@@ -2672,7 +3197,7 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
 
 /* The callback: */
 static void PNGCBAPI
-modifier_read(png_structp ppIn, png_bytep pb, png_size_t st)
+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));
@@ -2702,7 +3227,7 @@ modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
    for (;;)
    {
       static png_uint_32 noise = 1;
-      png_size_t cb, cbAvail;
+      size_t cb, cbAvail;
       png_byte buffer[512];
 
       /* Generate 15 more bits of stuff: */
@@ -2743,7 +3268,7 @@ modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
 /* Set up a modifier. */
 static png_structp
 set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
-    PNG_CONST char *name)
+    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
@@ -2803,7 +3328,7 @@ gama_modification_init(gama_modification *me, png_modifier *pm, double gammad)
 typedef struct chrm_modification
 {
    png_modification          this;
-   PNG_CONST color_encoding *encoding;
+   const color_encoding *encoding;
    png_fixed_point           wx, wy, rx, ry, gx, gy, bx, by;
 } chrm_modification;
 
@@ -2827,7 +3352,7 @@ chrm_modify(png_modifier *pm, png_modification *me, int add)
 
 static void
 chrm_modification_init(chrm_modification *me, png_modifier *pm,
-   PNG_CONST color_encoding *encoding)
+   const color_encoding *encoding)
 {
    CIE_color white = white_point(encoding);
 
@@ -3155,16 +3680,61 @@ init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette,
       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
+      if (j > 0)
+         png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+#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:
- */
+#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)
 {
@@ -3411,13 +3981,17 @@ transform_row(png_const_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
 #  define INTERLACE_LAST PNG_INTERLACE_LAST
 #  define check_interlace_type(type) ((void)(type))
-#else
-#  define INTERLACE_LAST (PNG_INTERLACE_NONE+1)
-#  define png_set_interlace_handling(a) (1)
-
+#  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 PNG_CONST interlace_type)
+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
@@ -3427,17 +4001,110 @@ check_interlace_type(int PNG_CONST interlace_type)
       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
 
-/* Make a standardized image given a an image colour type, bit depth and
+/* 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* PNG_CONST ps, png_byte PNG_CONST colour_type,
-    png_byte PNG_CONST bit_depth, unsigned int palette_number,
+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);
@@ -3448,7 +4115,7 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
    {
       png_infop pi;
       png_structp pp = set_store_for_write(ps, &pi, name);
-      png_uint_32 h;
+      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
@@ -3457,10 +4124,10 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
       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, transform_width(pp, colour_type, bit_depth), h,
-         bit_depth, colour_type, interlace_type,
+      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
@@ -3495,11 +4162,16 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
       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, "row size incorrect");
+         png_error(pp, "transform row size incorrect");
 
       else
       {
@@ -3507,7 +4179,7 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
           * because if it is called before, the information in *pp has not been
           * updated to reflect the interlaced image.
           */
-         int npasses = png_set_interlace_handling(pp);
+         int npasses = set_write_interlace_handling(pp, interlace_type);
          int pass;
 
          if (npasses != npasses_from_interlace_type(pp, interlace_type))
@@ -3517,11 +4189,38 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
          {
             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);
             }
          }
@@ -3568,19 +4267,20 @@ make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
 }
 
 static void
-make_transform_images(png_store *ps)
+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(ps->test, sizeof ps->test, 0, "make standard images");
+   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.
+    * 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, 0))
+   while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1))
    {
       int interlace_type;
 
@@ -3590,59 +4290,13 @@ make_transform_images(png_store *ps)
          char name[FILE_NAME_SIZE];
 
          standard_name(name, sizeof name, 0, colour_type, bit_depth,
-            palette_number, interlace_type, 0, 0, 0);
-         make_transform_image(ps, colour_type, bit_depth, palette_number,
+            palette_number, interlace_type, 0, 0, do_own_interlace);
+         make_transform_image(&pm->this, colour_type, bit_depth, palette_number,
             interlace_type, name);
       }
    }
 }
 
-/* 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)
-{
-   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.
-    */
-   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);
-      ++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)
-{
-   /* 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);
-      ++xin;
-   }
-}
-#endif /* PNG_READ_SUPPORTED */
-
 /* Build a single row for the 'size' test images; this fills in only the
  * first bit_width bits of the sample row.
  */
@@ -3668,17 +4322,13 @@ size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
 }
 
 static void
-make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
-    png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
-    png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
-    int PNG_CONST do_interlace)
+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);
 
-   /* At present 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:
-    */
    check_interlace_type(interlace_type);
 
    Try
@@ -3689,7 +4339,7 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
 
       /* Make a name and get an appropriate id for the store: */
       char name[FILE_NAME_SIZE];
-      PNG_CONST png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
+      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);
@@ -3740,16 +4390,13 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
        */
       pixel_size = bit_size(pp, colour_type, bit_depth);
       if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
-         png_error(pp, "row size incorrect");
+         png_error(pp, "size row size incorrect");
 
       else
       {
          int npasses = npasses_from_interlace_type(pp, interlace_type);
          png_uint_32 y;
          int pass;
-#        ifdef PNG_WRITE_FILTER_SUPPORTED
-            int nfilter = PNG_FILTER_VALUE_LAST;
-#        endif
          png_byte image[16][SIZE_ROWMAX];
 
          /* To help consistent error detection make the parts of this buffer
@@ -3757,7 +4404,8 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
           */
          memset(image, 0xff, sizeof image);
 
-         if (!do_interlace && npasses != png_set_interlace_handling(pp))
+         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: */
@@ -3767,7 +4415,7 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
          for (pass=0; pass<npasses; ++pass)
          {
             /* The following two are for checking the macros: */
-            PNG_CONST png_uint_32 wPass = PNG_PASS_COLS(w, pass);
+            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,
@@ -3796,7 +4444,8 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
                       * set unset things to 0).
                       */
                      memset(tempRow, 0xff, sizeof tempRow);
-                     interlace_row(tempRow, row, pixel_size, w, pass);
+                     interlace_row(tempRow, row, pixel_size, w, pass,
+                           0/*data always bigendian*/);
                      row = tempRow;
                   }
                   else
@@ -3810,15 +4459,19 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
                 * does accept a filter number (per the spec) as well as a bit
                 * mask.
                 *
-                * The apparent wackiness of decrementing nfilter rather than
-                * incrementing is so that Paeth gets used in all images bigger
-                * than 1 row - it's the tricky one.
+                * 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.
                 */
-               png_set_filter(pp, 0/*method*/,
-                  nfilter >= PNG_FILTER_VALUE_LAST ? PNG_ALL_FILTERS : nfilter);
+               {
+                  int filters = 8 << random_mod(PNG_FILTER_VALUE_LAST);
 
-               if (nfilter-- == 0)
-                  nfilter = PNG_FILTER_VALUE_LAST-1;
+                  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);
@@ -3866,8 +4519,8 @@ make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
 }
 
 static void
-make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
-    int PNG_CONST bdhi)
+make_size(png_store* const ps, png_byte const colour_type, int bdlo,
+    int const bdhi)
 {
    for (; bdlo <= bdhi; ++bdlo)
    {
@@ -3890,6 +4543,11 @@ make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
 #        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
@@ -3963,41 +4621,45 @@ sBIT_error_fn(png_structp pp, png_infop pi)
    png_set_sBIT(pp, pi, &bad);
 }
 
-static PNG_CONST struct
+static const struct
 {
    void          (*fn)(png_structp, png_infop);
-   PNG_CONST char *msg;
+   const char *msg;
    unsigned int    warning :1; /* the error is a warning... */
 } error_test[] =
     {
-       /* no warnings makes these errors undetectable. */
-       { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
-       { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
+       /* 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* volatile psIn, png_byte PNG_CONST colour_type,
+make_error(png_store* const ps, png_byte const colour_type,
     png_byte bit_depth, int interlace_type, int test, png_const_charp name)
 {
-   png_store * volatile ps = psIn;
-
    context(ps, fault);
 
    check_interlace_type(interlace_type);
 
    Try
    {
-      png_structp pp;
       png_infop pi;
-
-      pp = set_store_for_write(ps, &pi, name);
+      png_structp pp = set_store_for_write(ps, &pi, name);
+      png_uint_32 w, h;
+      gnu_volatile(pp)
 
       if (pp == NULL)
          Throw ps;
 
-      png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
-         transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
-         interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+      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*/);
@@ -4009,6 +4671,8 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type,
 #     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;
@@ -4029,45 +4693,77 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type,
       }
 
       Catch (fault)
-         ps = fault; /* expected exit, make sure ps is not clobbered */
+      { /* expected exit */
+      }
 #undef exception__prev
 #undef exception__env
 
       /* And clear these flags */
-      ps->expect_error = 0;
       ps->expect_warning = 0;
 
-      /* Now write the whole image, just to make sure that the detected, or
-       * undetected, errro has not created problems inside libpng.
-       */
-      if (png_get_rowbytes(pp, pi) !=
-          transform_rowsize(pp, colour_type, bit_depth))
-         png_error(pp, "row size incorrect");
+      if (ps->expect_error)
+         ps->expect_error = 0;
 
       else
       {
-         png_uint_32 h = transform_height(pp, colour_type, bit_depth);
-         int npasses = png_set_interlace_handling(pp);
-         int pass;
-
-         if (npasses != npasses_from_interlace_type(pp, interlace_type))
-            png_error(pp, "write: png_set_interlace_handling failed");
+         /* 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");
 
-         for (pass=0; pass<npasses; ++pass)
+         else
          {
-            png_uint_32 y;
+            int npasses = set_write_interlace_handling(pp, interlace_type);
+            int pass;
 
-            for (y=0; y<h; ++y)
+            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_byte buffer[TRANSFORM_ROWMAX];
+               png_uint_32 y;
 
-               transform_row(pp, buffer, colour_type, bit_depth, y);
-               png_write_row(pp, buffer);
+               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);
+         png_write_end(pp, pi);
+      }
 
       /* The following deletes the file that was just written. */
       store_write_reset(ps);
@@ -4080,8 +4776,8 @@ make_error(png_store* volatile psIn, png_byte PNG_CONST colour_type,
 }
 
 static int
-make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
-    int bdlo, int PNG_CONST bdhi)
+make_errors(png_modifier* const pm, png_byte const colour_type,
+    int bdlo, int const bdhi)
 {
    for (; bdlo <= bdhi; ++bdlo)
    {
@@ -4094,9 +4790,9 @@ make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
          char name[FILE_NAME_SIZE];
 
          standard_name(name, sizeof name, 0, colour_type, 1<<bdlo, 0,
-            interlace_type, 0, 0, 0);
+            interlace_type, 0, 0, do_own_interlace);
 
-         for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
+         for (test=0; test<ARRAY_SIZE(error_test); ++test)
          {
             make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
                test, name);
@@ -4141,7 +4837,7 @@ perform_error_test(png_modifier *pm)
  * then the warning messages the library outputs will probably be garbage.
  */
 static void
-perform_formatting_test(png_store *volatile ps)
+perform_formatting_test(png_store *ps)
 {
 #ifdef PNG_TIME_RFC1123_SUPPORTED
    /* The handle into the formatting code is the RFC1123 support; this test does
@@ -4246,7 +4942,8 @@ typedef struct standard_display
    png_byte    green_sBIT;
    png_byte    blue_sBIT;
    png_byte    alpha_sBIT;
-   int         interlace_type;
+   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 */
@@ -4255,7 +4952,9 @@ typedef struct standard_display
    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
@@ -4296,13 +4995,14 @@ standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
    dp->bit_width = 0;
    dp->cbRow = 0;
    dp->do_interlace = do_interlace;
+   dp->littleendian = 0;
    dp->is_transparent = 0;
    dp->speed = ps->speed;
    dp->use_update_info = use_update_info;
    dp->npalette = 0;
    /* Preset the transparent color to black: */
    memset(&dp->transparent, 0, sizeof dp->transparent);
-   /* Preset the palette to full intensity/opaque througout: */
+   /* Preset the palette to full intensity/opaque throughout: */
    memset(dp->palette, 0xff, sizeof dp->palette);
 }
 
@@ -4571,7 +5271,7 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
     */
    standard_palette_validate(dp, pp, pi);
 
-   /* In any case always check for a tranparent color (notice that the
+   /* 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!)
     */
@@ -4588,14 +5288,14 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
          case 0:
             dp->transparent.red = dp->transparent.green = dp->transparent.blue =
                trans_color->gray;
-            dp->is_transparent = 1;
+            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->is_transparent = 1;
+            dp->has_tRNS = 1;
             break;
 
          case 3:
@@ -4616,8 +5316,19 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
     * turning on interlace handling (if do_interlace is not set.)
     */
    dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
-   if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp))
-      png_error(pp, "validate: file changed 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.
@@ -4633,8 +5344,16 @@ standard_info_part2(standard_display *dp, png_const_structp pp,
     png_const_infop pi, int nImages)
 {
    /* Record cbRow now that it can be found. */
-   dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
-      png_get_bit_depth(pp, pi));
+   {
+      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);
 
@@ -4691,7 +5410,7 @@ static void PNGCBAPI
 progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
 {
    png_const_structp pp = ppIn;
-   PNG_CONST standard_display *dp = voidcast(standard_display*,
+   const standard_display *dp = voidcast(standard_display*,
       png_get_progressive_ptr(pp));
 
    /* When handling interlacing some rows will be absent in each pass, the
@@ -4715,7 +5434,7 @@ progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
 
          if (pass != png_get_current_pass_number(pp))
             png_error(pp, "png_get_current_pass_number is broken");
-#endif
+#endif /* USER_TRANSFORM_INFO */
 
          y = PNG_ROW_FROM_PASS_ROW(y, pass);
       }
@@ -4726,38 +5445,39 @@ progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
 
       row = store_image_row(dp->ps, pp, 0, y);
 
-#ifdef PNG_READ_INTERLACING_SUPPORTED
       /* 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);
+            deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass,
+                  dp->littleendian);
          else
-            row_copy(row, new_row, dp->pixel_size * dp->w);
+            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 */
    }
 
-#ifdef 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");
-#endif /* PNG_READ_INTERLACING_SUPPORTED */
 }
 
 static void
 sequential_row(standard_display *dp, png_structp pp, png_infop pi,
-    PNG_CONST int iImage, PNG_CONST int iDisplay)
+    int iImage, int iDisplay)
 {
-   PNG_CONST int         npasses = dp->npasses;
-   PNG_CONST int         do_interlace = dp->do_interlace &&
+   int npasses = dp->npasses;
+   int do_interlace = dp->do_interlace &&
       dp->interlace_type == PNG_INTERLACE_ADAM7;
-   PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
-   PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
-   PNG_CONST png_store*  ps = dp->ps;
+   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)
@@ -4792,11 +5512,11 @@ sequential_row(standard_display *dp, png_structp pp, png_infop pi,
 
                if (iImage >= 0)
                   deinterlace_row(store_image_row(ps, pp, iImage, y), row,
-                     dp->pixel_size, dp->w, pass);
+                     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->pixel_size, dp->w, pass, dp->littleendian);
             }
          }
          else
@@ -4943,14 +5663,6 @@ standard_row_validate(standard_display *dp, png_const_structp pp,
     * 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 PNG_LIBPNG_VER < 10506
-    * The side effect inside the libpng sequential reader is that the 'row'
-    * array retains the correct values for unwritten pixels within the row
-    * bytes, while the 'display' array gets bits off the end of the image (in
-    * the last byte) trashed.  Unfortunately in the progressive reader the
-    * row bytes are always trashed, so we always do a pixel_cmp here even though
-    * a memcmp of all cbRow bytes will succeed for the sequential reader.
-#endif
     */
    if (iImage >= 0 &&
       (where = pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
@@ -4963,19 +5675,12 @@ standard_row_validate(standard_display *dp, png_const_structp pp,
       png_error(pp, msg);
    }
 
-#if PNG_LIBPNG_VER < 10506
-   /* In this case use pixel_cmp because we need to compare a partial
-    * byte at the end of the row if the row is not an exact multiple
-    * of 8 bits wide.  (This is fixed in libpng-1.5.6 and pixel_cmp is
-    * changed to match!)
-    */
-#endif
    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",
+      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);
@@ -5020,7 +5725,7 @@ standard_end(png_structp ppIn, png_infop pi)
 
 /* A single test run checking the standard image to ensure it is not damaged. */
 static void
-standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
+standard_test(png_store* const psIn, png_uint_32 const id,
    int do_interlace, int use_update_info)
 {
    standard_display d;
@@ -5108,8 +5813,8 @@ standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
 }
 
 static int
-test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
-    int bdlo, int PNG_CONST bdhi)
+test_standard(png_modifier* const pm, png_byte const colour_type,
+    int bdlo, int const bdhi)
 {
    for (; bdlo <= bdhi; ++bdlo)
    {
@@ -5118,8 +5823,9 @@ test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_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), 0/*do_interlace*/, pm->use_update_info);
+         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;
@@ -5154,8 +5860,8 @@ perform_standard_test(png_modifier *pm)
 
 /********************************** SIZE TESTS ********************************/
 static int
-test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
-    int bdlo, int PNG_CONST bdhi)
+test_size(png_modifier* const pm, png_byte const colour_type,
+    int bdlo, int const bdhi)
 {
    /* Run the tests on each combination.
     *
@@ -5164,72 +5870,114 @@ test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
     * width and height.  This is a waste of time in practice, hence the
     * hinc and winc stuff:
     */
-   static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
-   static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
+   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])
+      for (h=1; h<=16; h+=hinc[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);
+         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;
+            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);
+            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;
+            if (fail(pm))
+               return 0;
 
-#     ifdef PNG_WRITE_INTERLACING_SUPPORTED
-         standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
-            PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/,
-            pm->use_update_info);
+            /* 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 (fail(pm))
+               return 0;
 
-         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 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
+            if (fail(pm))
+               return 0;
+#        endif
+         }
+      }
+   }
 
-         /* 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);
+   /* 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;
 
-         if (fail(pm))
-            return 0;
+      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);
 
-#     ifdef PNG_WRITE_INTERLACING_SUPPORTED
-         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
+#        endif /* READ_INTERLACING */
 
-         if (fail(pm))
-            return 0;
-#     endif
-      }
-   }
+#        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);
 
-   return 1; /* keep going */
+            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
@@ -5274,10 +6022,17 @@ typedef struct image_pixel
     */
    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 */
-   int      have_tRNS;                   /* tRNS chunk may need processing */
+   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
@@ -5285,6 +6040,9 @@ typedef struct image_pixel
     * 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;
@@ -5293,26 +6051,27 @@ typedef struct image_pixel
 
 /* Shared utility function, see below. */
 static void
-image_pixel_setf(image_pixel *this, unsigned int max)
+image_pixel_setf(image_pixel *this, unsigned int rMax, unsigned int gMax,
+        unsigned int bMax, unsigned int aMax)
 {
-   this->redf = this->red / (double)max;
-   this->greenf = this->green / (double)max;
-   this->bluef = this->blue / (double)max;
-   this->alphaf = this->alpha / (double)max;
+   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 < max)
+   if (this->red < rMax)
       this->rede = this->redf * DBL_EPSILON;
    else
       this->rede = 0;
-   if (this->green < max)
+   if (this->green < gMax)
       this->greene = this->greenf * DBL_EPSILON;
    else
       this->greene = 0;
-   if (this->blue < max)
+   if (this->blue < bMax)
       this->bluee = this->bluef * DBL_EPSILON;
    else
       this->bluee = 0;
-   if (this->alpha < max)
+   if (this->alpha < aMax)
       this->alphae = this->alphaf * DBL_EPSILON;
    else
       this->alphae = 0;
@@ -5324,18 +6083,22 @@ image_pixel_setf(image_pixel *this, unsigned int max)
  */
 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)
+    png_byte bit_depth, png_uint_32 x, store_palette palette,
+    const image_pixel *format /*from pngvalid transform of input*/)
 {
-   PNG_CONST png_byte sample_depth = (png_byte)(colour_type ==
-      PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
-   PNG_CONST unsigned int max = (1U<<sample_depth)-1;
+   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);
+      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;
@@ -5346,7 +6109,7 @@ image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
       /* This permits the caller to default to the sample value. */
       if (palette != 0)
       {
-         PNG_CONST unsigned int i = this->palette_index;
+         unsigned int i = this->palette_index;
 
          this->red = palette[i].red;
          this->green = palette[i].green;
@@ -5359,21 +6122,52 @@ image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
    {
       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)
       {
-         this->green = sample(row, colour_type, bit_depth, x, 1);
-         this->blue = sample(row, colour_type, bit_depth, x, 2);
-         i = 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;
       }
-      if (colour_type & 4)
-         this->alpha = sample(row, colour_type, bit_depth, x, ++i);
    }
 
    /* 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, max);
+   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.
@@ -5382,8 +6176,18 @@ image_pixel_init(image_pixel *this, png_const_bytep row, png_byte 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,
@@ -5413,10 +6217,14 @@ image_pixel_convert_PLTE(image_pixel *this)
 
 /* 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.  Palette images will be converted to alpha (using the above API).
+ * 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, PNG_CONST standard_display *display)
+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);
@@ -5425,11 +6233,21 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display)
    {
       if (this->colour_type == PNG_COLOR_TYPE_GRAY)
       {
-         if (this->bit_depth < 8)
-            this->bit_depth = 8;
+#        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
@@ -5461,9 +6279,11 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display)
                this->alphaf = 0;
             else
                this->alphaf = 1;
-
-            this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
          }
+         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
@@ -5473,18 +6293,19 @@ image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display)
       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. */
-   PNG_CONST char *name;
+   const char *name;
 
    /* Each transform can be disabled from the command line: */
    int enable;
 
    /* The global list of transforms; read only. */
-   struct image_transform *PNG_CONST list;
+   struct image_transform *const list;
 
    /* The global count of the number of times this transform has been set on an
     * image.
@@ -5497,7 +6318,7 @@ typedef struct image_transform
    /* The next transform in the list, each transform must call its own next
     * transform after it has processed the pixel successfully.
     */
-   PNG_CONST struct image_transform *next;
+   const struct image_transform *next;
 
    /* A single transform for the image, expressed as a series of function
     * callbacks and some space for values.
@@ -5505,12 +6326,12 @@ typedef struct image_transform
     * 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)(PNG_CONST struct image_transform *this,
+   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)(PNG_CONST struct image_transform *this,
+   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
@@ -5519,8 +6340,8 @@ typedef struct image_transform
     * in the libpng implementation!)  The png_structp is solely to allow error
     * reporting via png_error and png_warning.
     */
-   void (*mod)(PNG_CONST struct image_transform *this, image_pixel *that,
-      png_const_structp pp, PNG_CONST struct transform_display *display);
+   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
@@ -5528,7 +6349,7 @@ typedef struct image_transform
     * point running it.
     */
    int (*add)(struct image_transform *this,
-      PNG_CONST struct image_transform **that, png_byte colour_type,
+      const struct image_transform **that, png_byte colour_type,
       png_byte bit_depth);
 } image_transform;
 
@@ -5538,11 +6359,13 @@ typedef struct transform_display
 
    /* Parameters */
    png_modifier*              pm;
-   PNG_CONST image_transform* transform_list;
+   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;
@@ -5579,7 +6402,7 @@ transform_set_encoding(transform_display *this)
 
 /* Three functions to end the list: */
 static void
-image_transform_ini_end(PNG_CONST image_transform *this,
+image_transform_ini_end(const image_transform *this,
    transform_display *that)
 {
    UNUSED(this)
@@ -5587,7 +6410,7 @@ image_transform_ini_end(PNG_CONST image_transform *this,
 }
 
 static void
-image_transform_set_end(PNG_CONST image_transform *this,
+image_transform_set_end(const image_transform *this,
    transform_display *that, png_structp pp, png_infop pi)
 {
    UNUSED(this)
@@ -5614,10 +6437,11 @@ sample_scale(double sample_value, unsigned int scale)
 }
 
 static void
-image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
-    png_const_structp pp, PNG_CONST transform_display *display)
+image_transform_mod_end(const image_transform *this, image_pixel *that,
+    png_const_structp pp, const transform_display *display)
 {
-   PNG_CONST unsigned int scale = (1U<<that->sample_depth)-1;
+   unsigned int scale = (1U<<that->sample_depth)-1;
+   int sig_bits = that->sig_bits;
 
    UNUSED(this)
    UNUSED(pp)
@@ -5632,6 +6456,13 @@ image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
     */
    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,
@@ -5647,7 +6478,13 @@ image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
    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));
    }
@@ -5667,9 +6504,12 @@ image_transform_mod_end(PNG_CONST image_transform *this, image_pixel *that,
    else
    {
       that->alpha = scale; /* opaque */
-      that->alpha = 1;     /* Override this. */
+      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: */
@@ -5692,21 +6532,23 @@ static image_transform image_transform_end =
  */
 static void
 transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id,
-    PNG_CONST image_transform *transform_list)
+    const image_transform *transform_list)
 {
    memset(dp, 0, sizeof *dp);
 
    /* Standard fields */
-   standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/,
+   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
@@ -5734,6 +6576,14 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
    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.
@@ -5742,16 +6592,16 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
    {
    case PNG_COLOR_TYPE_PALETTE:
       if (dp->output_bit_depth > 8) goto error;
-      /*FALL THROUGH*/
+      /* FALLTHROUGH */
    case PNG_COLOR_TYPE_GRAY:
       if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
          dp->output_bit_depth == 4)
          break;
-      /*FALL THROUGH*/
+      /* FALLTHROUGH */
    default:
       if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
          break;
-      /*FALL THROUGH*/
+      /* FALLTHROUGH */
    error:
       {
          char message[128];
@@ -5768,7 +6618,8 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
    }
 
    /* 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 avoids running the whole test if the output is unexpected.  This also
+    * checks for internal errors.
     */
    {
       image_pixel test_pixel;
@@ -5783,7 +6634,7 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
       /* 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;
+      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;
 
@@ -5814,22 +6665,41 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
       }
 
       /* If both bit depth and colour type are correct check the sample depth.
-       * I believe these are both internal errors.
        */
-      if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
+      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)
       {
-         if (test_pixel.sample_depth != 8) /* oops - internal error! */
-            png_error(pp, "pngvalid: internal: palette sample depth not 8");
+         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.sample_depth != dp->output_bit_depth)
+      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: sample depth ");
+            "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.sample_depth);
+         pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
 
          png_error(pp, message);
       }
@@ -5847,9 +6717,9 @@ 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,
-   PNG_CONST char *name, double digitization_error)
+   const char *name, double digitization_error)
 {
-   /* Compare the scaled, digitzed, values of our local calculation (in+-err)
+   /* Compare the scaled, digitized, 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
@@ -5858,7 +6728,7 @@ transform_range_check(png_const_structp pp, unsigned int r, unsigned int g,
    unsigned int max = (1U<<sample_depth)-1;
    double in_min = ceil((in-err)*max - digitization_error);
    double in_max = floor((in+err)*max + digitization_error);
-   if (err > limit || !(out >= in_min && out <= in_max))
+   if (debugonly(err > limit ||) !(out >= in_min && out <= in_max))
    {
       char message[256];
       size_t pos;
@@ -5884,6 +6754,8 @@ transform_range_check(png_const_structp pp, unsigned int r, unsigned int g,
 
       png_error(pp, message);
    }
+
+   UNUSED(limit)
 }
 
 static void
@@ -5891,20 +6763,20 @@ transform_image_validate(transform_display *dp, png_const_structp pp,
    png_infop pi)
 {
    /* Constants for the loop below: */
-   PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
-   PNG_CONST png_byte in_ct = dp->this.colour_type;
-   PNG_CONST png_byte in_bd = dp->this.bit_depth;
-   PNG_CONST png_uint_32 w = dp->this.w;
-   PNG_CONST png_uint_32 h = dp->this.h;
-   PNG_CONST png_byte out_ct = dp->output_colour_type;
-   PNG_CONST png_byte out_bd = dp->output_bit_depth;
-   PNG_CONST png_byte sample_depth = (png_byte)(out_ct ==
-      PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
-   PNG_CONST png_byte red_sBIT = dp->this.red_sBIT;
-   PNG_CONST png_byte green_sBIT = dp->this.green_sBIT;
-   PNG_CONST png_byte blue_sBIT = dp->this.blue_sBIT;
-   PNG_CONST png_byte alpha_sBIT = dp->this.alpha_sBIT;
-   PNG_CONST int have_tRNS = dp->this.is_transparent;
+   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;
@@ -5916,7 +6788,7 @@ transform_image_validate(transform_display *dp, png_const_structp pp,
    store_image_check(dp->this.ps, pp, 0);
 
    /* Read the palette corresponding to the output if the output colour type
-    * indicates a palette, othewise set out_palette to garbage.
+    * indicates a palette, otherwise set out_palette to garbage.
     */
    if (out_ct == PNG_COLOR_TYPE_PALETTE)
    {
@@ -5959,7 +6831,7 @@ transform_image_validate(transform_display *dp, png_const_structp pp,
 
    for (y=0; y<h; ++y)
    {
-      png_const_bytep PNG_CONST pRow = store_image_row(ps, pp, 0, y);
+      png_const_bytep const pRow = store_image_row(ps, pp, 0, y);
       png_uint_32 x;
 
       /* The original, standard, row pre-transforms. */
@@ -5976,13 +6848,14 @@ transform_image_validate(transform_display *dp, png_const_structp pp,
          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);
+         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;
+         in_pixel.have_tRNS = have_tRNS != 0;
 
          /* For error detection, below. */
          r = in_pixel.red;
@@ -5990,12 +6863,18 @@ transform_image_validate(transform_display *dp, png_const_structp pp,
          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.
+          * 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);
+         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.
@@ -6058,8 +6937,8 @@ transform_end(png_structp ppIn, png_infop pi)
 
 /* A single test run. */
 static void
-transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn,
-    PNG_CONST image_transform* transform_listIn, PNG_CONST char * volatile name)
+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);
@@ -6160,8 +7039,11 @@ static image_transform ITSTRUCT(name) =\
 #define PT ITSTRUCT(end) /* stores the previous transform */
 
 /* To save code: */
-static void
-image_transform_default_ini(PNG_CONST image_transform *this,
+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);
@@ -6170,7 +7052,7 @@ image_transform_default_ini(PNG_CONST image_transform *this,
 #ifdef PNG_READ_BACKGROUND_SUPPORTED
 static int
 image_transform_default_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(colour_type)
    UNUSED(bit_depth)
@@ -6185,7 +7067,7 @@ image_transform_default_add(image_transform *this,
 #ifdef PNG_READ_EXPAND_SUPPORTED
 /* png_set_palette_to_rgb */
 static void
-image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this,
+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);
@@ -6193,9 +7075,9 @@ image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this,
 }
 
 static void
-image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this,
+image_transform_png_set_palette_to_rgb_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
       image_pixel_convert_PLTE(that);
@@ -6205,7 +7087,7 @@ image_transform_png_set_palette_to_rgb_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_palette_to_rgb_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(bit_depth)
 
@@ -6223,28 +7105,42 @@ IT(palette_to_rgb);
 #ifdef PNG_READ_EXPAND_SUPPORTED
 /* png_set_tRNS_to_alpha */
 static void
-image_transform_png_set_tRNS_to_alpha_set(PNG_CONST image_transform *this,
+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(PNG_CONST image_transform *this,
+image_transform_png_set_tRNS_to_alpha_mod(const image_transform *this,
    image_pixel *that, png_const_structp pp,
-   PNG_CONST transform_display *display)
+   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)
-      image_pixel_add_alpha(that, &display->this);
+#     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
    {
@@ -6253,13 +7149,14 @@ image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this,
       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,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(bit_depth)
 
@@ -6268,9 +7165,14 @@ image_transform_png_set_tRNS_to_alpha_add(image_transform *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.
+    * channel.  In addition, after the bug fix in 1.7.0, there is no longer
+    * any action on a palette image.
     */
-   return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
+   return
+#  if PNG_LIBPNG_VER >= 10700
+      colour_type != PNG_COLOR_TYPE_PALETTE &&
+#  endif
+   (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
 }
 
 IT(tRNS_to_alpha);
@@ -6281,17 +7183,18 @@ IT(tRNS_to_alpha);
 #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
 /* png_set_gray_to_rgb */
 static void
-image_transform_png_set_gray_to_rgb_set(PNG_CONST image_transform *this,
+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(PNG_CONST image_transform *this,
+image_transform_png_set_gray_to_rgb_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    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
@@ -6299,7 +7202,7 @@ image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this,
     * 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);
+      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)
@@ -6322,7 +7225,7 @@ image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_gray_to_rgb_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(bit_depth)
 
@@ -6340,17 +7243,21 @@ IT(gray_to_rgb);
 #ifdef PNG_READ_EXPAND_SUPPORTED
 /* png_set_expand */
 static void
-image_transform_png_set_expand_set(PNG_CONST image_transform *this,
+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(PNG_CONST image_transform *this,
+image_transform_png_set_expand_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    /* The general expand case depends on what the colour type is: */
    if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
@@ -6359,14 +7266,14 @@ image_transform_png_set_expand_mod(PNG_CONST image_transform *this,
       that->sample_depth = that->bit_depth = 8;
 
    if (that->have_tRNS)
-      image_pixel_add_alpha(that, &display->this);
+      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,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(bit_depth)
 
@@ -6386,31 +7293,53 @@ IT(expand);
 
 #ifdef PNG_READ_EXPAND_SUPPORTED
 /* png_set_expand_gray_1_2_4_to_8
- * LIBPNG BUG: this just does an 'expand'
+ * 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(
-    PNG_CONST image_transform *this, transform_display *that, png_structp pp,
+    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(
-    PNG_CONST image_transform *this, image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    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,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    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);
@@ -6421,17 +7350,24 @@ IT(expand_gray_1_2_4_to_8);
 #ifdef PNG_READ_EXPAND_16_SUPPORTED
 /* png_set_expand_16 */
 static void
-image_transform_png_set_expand_16_set(PNG_CONST image_transform *this,
+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(PNG_CONST image_transform *this,
+image_transform_png_set_expand_16_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    /* Expect expand_16 to expand everything to 16 bits as a result of also
     * causing 'expand' to happen.
@@ -6440,7 +7376,7 @@ image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
       image_pixel_convert_PLTE(that);
 
    if (that->have_tRNS)
-      image_pixel_add_alpha(that, &display->this);
+      image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
 
    if (that->bit_depth < 16)
       that->sample_depth = that->bit_depth = 16;
@@ -6450,7 +7386,7 @@ image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_expand_16_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(colour_type)
 
@@ -6469,17 +7405,21 @@ IT(expand_16);
 #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(PNG_CONST image_transform *this,
+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(PNG_CONST image_transform *this,
+image_transform_png_set_scale_16_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    if (that->bit_depth == 16)
    {
@@ -6495,7 +7435,7 @@ image_transform_png_set_scale_16_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_scale_16_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(colour_type)
 
@@ -6513,17 +7453,21 @@ IT(scale_16);
 #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(PNG_CONST image_transform *this,
+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(PNG_CONST image_transform *this,
+image_transform_png_set_strip_16_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    if (that->bit_depth == 16)
    {
@@ -6548,7 +7492,7 @@ image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this,
           * png_set_scale_16 API in 1.5.4 (but 1.5.4+ always defines the above!)
           */
          {
-            PNG_CONST double d = (255-128.5)/65535;
+            const double d = (255-128.5)/65535;
             that->rede += d;
             that->greene += d;
             that->bluee += d;
@@ -6562,7 +7506,7 @@ image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_strip_16_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(colour_type)
 
@@ -6580,7 +7524,7 @@ IT(strip_16);
 #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
 /* png_set_strip_alpha */
 static void
-image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this,
+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);
@@ -6588,9 +7532,9 @@ image_transform_png_set_strip_alpha_set(PNG_CONST image_transform *this,
 }
 
 static void
-image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this,
+image_transform_png_set_strip_alpha_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
       that->colour_type = PNG_COLOR_TYPE_GRAY;
@@ -6605,7 +7549,7 @@ image_transform_png_set_strip_alpha_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_strip_alpha_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(bit_depth)
 
@@ -6626,7 +7570,8 @@ IT(strip_alpha);
  *    png_fixed_point green)
  * png_get_rgb_to_gray_status
  *
- * The 'default' test here uses values known to be used inside libpng:
+ * The 'default' test here uses values known to be used inside libpng prior to
+ * 1.7.0:
  *
  *   red:    6968
  *   green: 23434
@@ -6663,11 +7608,11 @@ static struct
 #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(PNG_CONST image_transform *this,
+image_transform_png_set_rgb_to_gray_ini(const image_transform *this,
     transform_display *that)
 {
    png_modifier *pm = that->pm;
-   PNG_CONST color_encoding *e = pm->current_encoding;
+   const color_encoding *e = pm->current_encoding;
 
    UNUSED(this)
 
@@ -6682,7 +7627,7 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
       /* Coefficients come from the encoding, but may need to be normalized to a
        * white point Y of 1.0
        */
-      PNG_CONST double whiteY = e->red.Y + e->green.Y + e->blue.Y;
+      const double whiteY = e->red.Y + e->green.Y + e->blue.Y;
 
       data.red_coefficient = e->red.Y;
       data.green_coefficient = e->green.Y;
@@ -6698,10 +7643,16 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
 
    else
    {
-      /* The default (built in) coeffcients, as above: */
-      data.red_coefficient = 6968 / 32768.;
-      data.green_coefficient = 23434 / 32768.;
-      data.blue_coefficient = 2366 / 32768.;
+      /* The default (built in) coefficients, 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;
@@ -6735,7 +7686,7 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
       png_uint_32 ru;
       double total;
 
-      RANDOMIZE(ru);
+      ru = random_u32();
       data.green_coefficient = total = (ru & 0xffff) / 65535.;
       ru >>= 16;
       data.red_coefficient = (1 - total) * (ru & 0xffff) / 65535.;
@@ -6776,14 +7727,15 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
           *  conversion adds another +/-2 in the 16-bit case and
           *  +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case.
           */
-         that->pm->limit +=
-#        if PNG_MAX_GAMMA_8 < 14
-               pow((that->this.bit_depth == 16 ?
-                  8. : 6. + (1<<(15-PNG_MAX_GAMMA_8)))/65535, data.gamma);
-#        else
-               pow((that->this.bit_depth == 16 ?
-                  8. : 8. + (1<<(15-PNG_MAX_GAMMA_8)))/65535, data.gamma);
+#        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
@@ -6795,18 +7747,14 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
           * When DIGITIZE is set because a pre-1.7 version of libpng is being
           * tested allow a bigger slack.
           *
-          * NOTE: this magic number was determined by experiment to be 1.1 (when
-          * using fixed point arithmetic).  There's no great merit to the value
-          * below, however it only affects the limit used for checking for
-          * internal calculation errors, not the actual limit imposed by
-          * pngvalid on the output errors.
+          * NOTE: this number only affects the internal limit check in pngvalid,
+          * it has no effect on the limits applied to the libpng values.
           */
-         that->pm->limit +=
-#        if DIGITIZE
-             pow(1.1 /255, data.gamma);
-#        else
-             pow(1.0 /255, data.gamma);
-#        endif
+#if DIGITIZE
+          that->pm->limit += pow( 2.0/255, data.gamma);
+#else
+          that->pm->limit += pow( 1.0/255, data.gamma);
+#endif
       }
    }
 
@@ -6821,10 +7769,10 @@ image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this,
 }
 
 static void
-image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
+image_transform_png_set_rgb_to_gray_set(const image_transform *this,
     transform_display *that, png_structp pp, png_infop pi)
 {
-   PNG_CONST int error_action = 1; /* no error, no defines in png.h */
+   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);
@@ -6861,7 +7809,7 @@ image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
                & PNG_INFO_cHRM) != 0)
          {
             double maxe;
-            PNG_CONST char *el;
+            const char *el;
             color_encoding e, o;
 
             /* Expect libpng to return a normalized result, but the original
@@ -6948,26 +7896,32 @@ image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
 }
 
 static void
-image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
+image_transform_png_set_rgb_to_gray_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    const transform_display *display)
 {
    if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
    {
       double gray, err;
 
-      if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
-         image_pixel_convert_PLTE(that);
+#     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_CONST png_modifier *pm = display->pm;
-         const unsigned int sample_depth = that->sample_depth;
-         const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
+         png_modifier *pm = display->pm;
+         unsigned int sample_depth = that->sample_depth;
+         unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
             sample_depth);
-         const unsigned int gamma_depth = (sample_depth == 16 ? 16 :
-            (pm->assume_16_bit_calculations ? PNG_MAX_GAMMA_8 : 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;
@@ -6980,56 +7934,73 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
           * 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 = digitize(rlo, calc_depth, 1/*round*/);
+         rlo = DD(rlo, calc_depth, 1/*round*/);
          rhi += that->rede;
-         rhi = digitize(rhi, calc_depth, 1/*round*/);
+         rhi = DU(rhi, calc_depth, 1/*round*/);
 
          g = glo = ghi = that->greenf;
          glo -= that->greene;
-         glo = digitize(glo, calc_depth, 1/*round*/);
+         glo = DD(glo, calc_depth, 1/*round*/);
          ghi += that->greene;
-         ghi = digitize(ghi, calc_depth, 1/*round*/);
+         ghi = DU(ghi, calc_depth, 1/*round*/);
 
          b = blo = bhi = that->bluef;
          blo -= that->bluee;
-         blo = digitize(blo, calc_depth, 1/*round*/);
-         bhi += that->greene;
-         bhi = digitize(bhi, calc_depth, 1/*round*/);
+         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)
          {
-            PNG_CONST double power = 1/data.gamma;
-            PNG_CONST double abse = calc_depth == 16 ? .5/65535 : .5/255;
-
-            /* 'abse' is the absolute error permitted in linear calculations. It
-             * is used here to capture the error permitted in the handling
-             * (undoing) of the gamma encoding.  Once again digitization occurs
-             * to handle the upper and lower bounds of the values.  This is
-             * where the real errors are introduced.
+            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 = digitize(pow(rlo, power)-abse, calc_depth, 1);
-            rhi = digitize(pow(rhi, power)+abse, calc_depth, 1);
+            rlo = DD(pow(rlo, power)-abse, calc_depth, 1);
+            rhi = DU(pow(rhi, power)+abse, calc_depth, 1);
 
             g = pow(g, power);
-            glo = digitize(pow(glo, power)-abse, calc_depth, 1);
-            ghi = digitize(pow(ghi, power)+abse, calc_depth, 1);
+            glo = DD(pow(glo, power)-abse, calc_depth, 1);
+            ghi = DU(pow(ghi, power)+abse, calc_depth, 1);
 
             b = pow(b, power);
-            blo = digitize(pow(blo, power)-abse, calc_depth, 1);
-            bhi = digitize(pow(bhi, power)+abse, calc_depth, 1);
+            blo = DD(pow(blo, power)-abse, calc_depth, 1);
+            bhi = DU(pow(bhi, power)+abse, calc_depth, 1);
          }
 
          /* Now calculate the actual gray values.  Although the error in the
           * coefficients depends on whether they were specified on the command
           * line (in which case truncation to 15 bits happened) or not (rounding
-          * was used) the maxium error in an individual coefficient is always
-          * 1/32768, because even in the rounding case the requirement that
+          * 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
@@ -7039,32 +8010,46 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
             b * data.blue_coefficient;
 
          {
-            PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16;
-            PNG_CONST double ce = 1. / 32768;
+            int do_round = data.gamma != 1 || calc_depth == 16;
+            const double ce = 2. / 32768;
 
-            graylo = digitize(rlo * (data.red_coefficient-ce) +
+            graylo = DD(rlo * (data.red_coefficient-ce) +
                glo * (data.green_coefficient-ce) +
-               blo * (data.blue_coefficient-ce), gamma_depth, do_round);
-            if (graylo <= 0)
-               graylo = 0;
+               blo * (data.blue_coefficient-ce), calc_depth, do_round);
+            if (graylo > gray) /* always accept the right answer */
+               graylo = gray;
 
-            grayhi = digitize(rhi * (data.red_coefficient+ce) +
+            grayhi = DU(rhi * (data.red_coefficient+ce) +
                ghi * (data.green_coefficient+ce) +
-               bhi * (data.blue_coefficient+ce), gamma_depth, do_round);
-            if (grayhi >= 1)
-               grayhi = 1;
+               bhi * (data.blue_coefficient+ce), calc_depth, do_round);
+            if (grayhi < gray)
+               grayhi = gray;
          }
 
          /* And invert the gamma. */
          if (data.gamma != 1)
          {
-            PNG_CONST double power = data.gamma;
+            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 = digitize(pow(graylo, power), sample_depth, 1);
-            grayhi = digitize(pow(grayhi, power), sample_depth, 1);
+            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
@@ -7076,9 +8061,11 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
          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)
             {
@@ -7089,11 +8076,13 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
                pos = safecatd(buffer, sizeof buffer, pos, err, 6);
                pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
                pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
-               png_error(pp, buffer);
+               png_warning(pp, buffer);
+               pm->limit = err;
             }
+#endif /* !RELEASE_BUILD */
          }
       }
-#  else  /* DIGITIZE */
+#  else  /* !DIGITIZE */
       {
          double r = that->redf;
          double re = that->rede;
@@ -7102,29 +8091,46 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
          double b = that->bluef;
          double be = that->bluee;
 
-         /* The true gray case involves no math. */
-         if (r == g && r == b)
-         {
-            gray = r;
-            err = re;
-            if (err < ge) err = ge;
-            if (err < be) err = be;
-         }
+#        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 if (data.gamma == 1)
+            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 1/32768 error in the coefficients because they only have
-             * 15 bits and are adjusted to make sure they add up to 32768, so
-             * the result may have an additional error up to 1/32768.  (Note
-             * that adding the 1/32768 here avoids needing to increase the
-             * global error limits to take this into account.)
+             * 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 + 1./32768 + gray * 5 * DBL_EPSILON;
+               be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON;
          }
 
          else
@@ -7135,10 +8141,10 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
              * lookups in the calculation and each introduces a quantization
              * error defined by the table size.
              */
-            PNG_CONST png_modifier *pm = display->pm;
+            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<<PNG_MAX_GAMMA_8) :
+               (pm->assume_16_bit_calculations ? .5/(1<<display->max_gamma_8) :
                .5/255));
             double rhi, ghi, bhi, grayhi;
             double g1 = 1/data.gamma;
@@ -7159,7 +8165,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
              * previously added input quantization error at this point.
              */
             gray = r * data.red_coefficient + g * data.green_coefficient +
-               b * data.blue_coefficient - 1./32768 - out_qe;
+               b * data.blue_coefficient - 2./32768 - out_qe;
             if (gray <= 0)
                gray = 0;
             else
@@ -7169,7 +8175,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
             }
 
             grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient +
-               bhi * data.blue_coefficient + 1./32768 + out_qe;
+               bhi * data.blue_coefficient + 2./32768 + out_qe;
             grayhi *= (1 + 6 * DBL_EPSILON);
             if (grayhi >= 1)
                grayhi = 1;
@@ -7185,6 +8191,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
             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.)
              */
@@ -7197,8 +8204,10 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
                pos = safecatd(buffer, sizeof buffer, pos, err, 6);
                pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
                pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
-               png_error(pp, buffer);
+               png_warning(pp, buffer);
+               pm->limit = err;
             }
+#endif /* !RELEASE_BUILD */
          }
       }
 #  endif /* !DIGITIZE */
@@ -7206,7 +8215,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
       that->bluef = that->greenf = that->redf = gray;
       that->bluee = that->greene = that->rede = err;
 
-      /* The sBIT is the minium of the three colour channel sBITs. */
+      /* 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)
@@ -7225,7 +8234,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
 
 static int
 image_transform_png_set_rgb_to_gray_add(image_transform *this,
-    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+    const image_transform **that, png_byte colour_type, png_byte bit_depth)
 {
    UNUSED(bit_depth)
 
@@ -7256,7 +8265,7 @@ IT(rgb_to_gray);
 static image_pixel data;
 
 static void
-image_transform_png_set_background_set(PNG_CONST image_transform *this,
+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;
@@ -7269,7 +8278,7 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this,
     * so we need to know what that is!  The background colour is stored in the
     * transform_display.
     */
-   RANDOMIZE(random_bytes);
+   R8(random_bytes);
 
    /* Read the random value, for colour type 3 the background colour is actually
     * expressed as a 24bit rgb, not an index.
@@ -7284,17 +8293,20 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this,
 
    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*/);
+      bit_depth, 0/*x*/, 0/*unused: palette*/, NULL/*format*/);
 
    /* Extract the background colour from this image_pixel, but make sure the
     * unused fields of 'back' are garbage.
     */
-   RANDOMIZE(back);
+   R8(back);
 
    if (colour_type & PNG_COLOR_MASK_COLOR)
    {
@@ -7306,85 +8318,678 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this,
    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
+#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_background_mod(PNG_CONST image_transform *this,
+image_transform_png_set_shift_mod(const image_transform *this,
     image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
+    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);
-
-   /* This is only necessary if the alpha value is less than 1. */
-   if (that->alphaf < 1)
+   /* Copy the correct values into the sBIT fields, libpng does not do
+    * anything to palette data:
+    */
+   if (that->colour_type != PNG_COLOR_TYPE_PALETTE)
    {
-      /* 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->sig_bits = 1;
 
-         that->rede = data.rede;
-         that->greene = data.greene;
-         that->bluee = data.bluee;
+       /* 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;
+       }
 
-         that->red_sBIT= data.red_sBIT;
-         that->green_sBIT= data.green_sBIT;
-         that->blue_sBIT= data.blue_sBIT;
-      }
+       else /* One grey channel */
+          that->red_sBIT = that->green_sBIT = that->blue_sBIT = data.gray;
 
-      else /* 0 < alpha < 1 */
-      {
-         double alf = 1 - that->alphaf;
+       that->alpha_sBIT = data.alpha;
+   }
 
-         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;
-      }
+   this->next->mod(this->next, that, pp, display);
+}
 
-      /* Remove the alpha type and set the alpha (not in that order.) */
-      that->alphaf = 1;
-      that->alphae = 0;
+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)
 
-      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 = *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);
 }
 
-#define image_transform_png_set_background_add image_transform_default_add
+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
 
-#undef data
-IT(background);
-#undef PT
-#define PT ITSTRUCT(background)
-#endif /* PNG_READ_BACKGROUND_SUPPORTED */
 
 /* This may just be 'end' if all the transforms are disabled! */
-static image_transform *PNG_CONST image_transform_first = &PT;
+static image_transform *const image_transform_first = &PT;
 
 static void
-transform_enable(PNG_CONST char *name)
+transform_enable(const char *name)
 {
    /* Everything starts out enabled, so if we see an 'enable' disabled
     * everything else the first time round.
@@ -7417,7 +9022,7 @@ transform_enable(PNG_CONST char *name)
 }
 
 static void
-transform_disable(PNG_CONST char *name)
+transform_disable(const char *name)
 {
    image_transform *list = image_transform_first;
 
@@ -7460,7 +9065,7 @@ image_transform_reset_count(void)
 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
+   /* Test the list to see if there is any point continuing, given a current
     * counter and a 'max' value.
     */
    image_transform *next = image_transform_first;
@@ -7480,7 +9085,7 @@ image_transform_test_counter(png_uint_32 counter, unsigned int max)
 }
 
 static png_uint_32
-image_transform_add(PNG_CONST image_transform **this, unsigned int max,
+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)
 {
@@ -7557,83 +9162,6 @@ image_transform_add(PNG_CONST image_transform **this, unsigned int max,
    }
 }
 
-#ifdef THIS_IS_THE_PROFORMA
-static void
-image_transform_png_set_@_set(PNG_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(PNG_CONST image_transform *this,
-    image_pixel *that, png_const_structp pp,
-    PNG_CONST transform_display *display)
-{
-   this->next->mod(this->next, that, pp, display);
-}
-
-static int
-image_transform_png_set_@_add(image_transform *this,
-    PNG_CONST image_transform **that, char *name, size_t sizeof_name,
-    size_t *pos, png_byte colour_type, png_byte bit_depth)
-{
-   this->next = *that;
-   *that = this;
-
-   *pos = safecat(name, sizeof_name, *pos, " +@");
-
-   return 1;
-}
-
-IT(@);
-#endif
-
-/* 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 */
-/*NOTE: TBD NYI */
-
-/* png_set_bgr */
-/*NOTE: TBD NYI */
-
-/* png_set_swap_alpha */
-/*NOTE: TBD NYI */
-
-/* png_set_swap */
-/*NOTE: TBD NYI */
-
-/* png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); */
-/*NOTE: TBD NYI */
-
-/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
-/*NOTE: TBD NYI */
-
-/* png_set_packing */
-/*NOTE: TBD NYI */
-
-/* png_set_packswap */
-/*NOTE: TBD NYI */
-
-/* png_set_invert_mono */
-/*NOTE: TBD NYI */
-
-/* png_set_shift(png_structp, png_const_color_8p true_bits) */
-/*NOTE: TBD NYI */
-
 static void
 perform_transform_test(png_modifier *pm)
 {
@@ -7641,7 +9169,8 @@ perform_transform_test(png_modifier *pm)
    png_byte bit_depth = 0;
    unsigned int palette_number = 0;
 
-   while (next_format(&colour_type, &bit_depth, &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;
@@ -7652,7 +9181,7 @@ perform_transform_test(png_modifier *pm)
       for (;;)
       {
          size_t pos = base_pos;
-         PNG_CONST image_transform *list = 0;
+         const image_transform *list = 0;
 
          /* 'max' is currently hardwired to '1'; this should be settable on the
           * command line.
@@ -7713,11 +9242,11 @@ 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, PNG_CONST png_color_16 *pointer_to_the_background_color,
+    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, 0/*do_interlace*/,
+   standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
       pm->use_update_info);
 
    /* Parameter fields */
@@ -7749,7 +9278,7 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop 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 when doing the following.
+    * PNG_MAX_GAMMA_8 prior to 1.7 when doing the following.
     */
    if (dp->scale16)
 #     ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
@@ -7778,12 +9307,12 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
          int mode = dp->do_background - ALPHA_MODE_OFFSET;
 
          /* The gamma value is the output gamma, and is in the standard,
-          * non-inverted, represenation.  It provides a default for the PNG file
+          * non-inverted, representation.  It provides a default for the PNG file
           * gamma, but since the file has a gAMA chunk this does not matter.
           */
-         PNG_CONST double sg = dp->screen_gamma;
+         const double sg = dp->screen_gamma;
 #        ifndef PNG_FLOATING_POINT_SUPPORTED
-            PNG_CONST png_fixed_point g = fix(sg);
+            png_fixed_point g = fix(sg);
 #        endif
 
 #        ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -7829,9 +9358,9 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
 #     ifdef PNG_READ_BACKGROUND_SUPPORTED
          /* NOTE: this assumes the caller provided the correct background gamma!
           */
-         PNG_CONST double bg = dp->background_gamma;
+         const double bg = dp->background_gamma;
 #        ifndef PNG_FLOATING_POINT_SUPPORTED
-            PNG_CONST png_fixed_point g = fix(bg);
+            png_fixed_point g = fix(bg);
 #        endif
 
 #        ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -7905,7 +9434,7 @@ static void
 init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
     int in_depth, int out_depth)
 {
-   PNG_CONST unsigned int outmax = (1U<<out_depth)-1;
+   unsigned int outmax = (1U<<out_depth)-1;
 
    vi->pp = pp;
    vi->dp = dp;
@@ -7944,13 +9473,15 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
    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 == 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)
       {
-         PNG_CONST double bg_inverse = 1/dp->background_gamma;
+         const double bg_inverse = 1/dp->background_gamma;
          double r, g, b;
 
          /* Caller must at least put the gray value into the red channel */
@@ -7974,7 +9505,7 @@ init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
          vi->background_blue = b;
       }
    }
-   else
+   else /* Do not expect any background processing */
       vi->do_background = 0;
 
    if (vi->do_background == 0)
@@ -8064,15 +9595,15 @@ gamma_component_compose(int do_background, double input_sample, double alpha,
 
 /* This API returns the encoded *input* component, in the range 0..1 */
 static double
-gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
-    PNG_CONST unsigned int id, PNG_CONST unsigned int od,
-    PNG_CONST double alpha /* <0 for the alpha channel itself */,
-    PNG_CONST double background /* component background value */)
+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 */)
 {
-   PNG_CONST unsigned int isbit = id >> vi->isbit_shift;
-   PNG_CONST unsigned int sbit_max = vi->sbit_max;
-   PNG_CONST unsigned int outmax = vi->outmax;
-   PNG_CONST int do_background = vi->do_background;
+   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;
 
@@ -8223,7 +9754,7 @@ gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
        *
        * pngvalid calculations:
        *  input_sample: linear result; i linearized and composed, range 0..1
-       *  encoded_sample: encoded result; input_sample scaled to ouput bit depth
+       *  encoded_sample: encoded result; input_sample scaled to output bit depth
        *
        * libpng calculations:
        *  output: linear result; od scaled to 0..1 and linearized
@@ -8471,9 +10002,9 @@ gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
                case PNG_BACKGROUND_GAMMA_FILE:
                case PNG_BACKGROUND_GAMMA_UNIQUE:
                   use_background = (alpha >= 0 && alpha < 1);
-                  /*FALL THROUGH*/
 #           endif
 #           ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+               /* FALLTHROUGH */
                case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
                case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
                case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
@@ -8637,14 +10168,14 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
    png_infop pi)
 {
    /* Get some constants derived from the input and output file formats: */
-   PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
-   PNG_CONST png_byte in_ct = dp->this.colour_type;
-   PNG_CONST png_byte in_bd = dp->this.bit_depth;
-   PNG_CONST png_uint_32 w = dp->this.w;
-   PNG_CONST png_uint_32 h = dp->this.h;
-   PNG_CONST size_t cbRow = dp->this.cbRow;
-   PNG_CONST png_byte out_ct = png_get_color_type(pp, pi);
-   PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi);
+   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
@@ -8660,10 +10191,10 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
     * Since the library must quantize the output to 8 or 16 bits there is
     * a fundamental limit on the accuracy of the output of +/-.5 - this
     * quantization limit is included in addition to the other limits
-    * specified by the paramaters to the API.  (Effectively, add .5
+    * specified by the parameters to the API.  (Effectively, add .5
     * everywhere.)
     *
-    * The behavior of the 'sbit' paramter is defined by section 12.5
+    * 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:
@@ -8685,11 +10216,12 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
     * The basic tests below do not do this, however if 'use_input_precision'
     * is set a subsequent test is performed above.
     */
-   PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
+   unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
    int processing;
    png_uint_32 y;
-   PNG_CONST store_palette_entry *in_palette = dp->this.palette;
-   PNG_CONST int in_is_transparent = dp->this.is_transparent;
+   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;
@@ -8705,6 +10237,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
 
    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
@@ -8735,20 +10268,20 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
             double alpha = 1; /* serves as a flag value */
 
             /* Record the palette index for index images. */
-            PNG_CONST unsigned int in_index =
-               in_ct == 3 ? sample(std, 3, in_bd, x, 0) : 256;
-            PNG_CONST unsigned int out_index =
-               out_ct == 3 ? sample(std, 3, out_bd, x, 0) : 256;
+            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))
+            if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 ||
+                (in_ct == 3 && in_is_transparent))
             {
-               PNG_CONST unsigned int input_alpha = in_ct == 3 ?
+               unsigned int input_alpha = in_ct == 3 ?
                   dp->this.palette[in_index].alpha :
-                  sample(std, in_ct, in_bd, x, samples_per_pixel);
+                  sample(std, in_ct, in_bd, x, samples_per_pixel, 0, 0);
 
                unsigned int output_alpha = 65536 /* as a flag value */;
 
@@ -8760,7 +10293,7 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
 
                else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0)
                   output_alpha = sample(pRow, out_ct, out_bd, x,
-                     samples_per_pixel);
+                     samples_per_pixel, 0, 0);
 
                if (output_alpha != 65536)
                   alpha = gamma_component_validate("alpha", &vi, input_alpha,
@@ -8776,33 +10309,62 @@ gamma_image_validate(gamma_display *dp, png_const_structp pp,
                }
             }
 
+            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),
-                  sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/,
-                  vi.background_red);
+                  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),
+                     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),
+                     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),
+                     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),
+                     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),
+                     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),
+                     sample(pRow, out_ct, out_bd, x, 2, 0, 0),
                   alpha/*component*/, vi.background_blue);
             }
          }
@@ -8842,15 +10404,15 @@ gamma_end(png_structp ppIn, png_infop pi)
  * maxpc:  maximum percentage error (as a percentage)
  */
 static void
-gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
-    PNG_CONST png_byte bit_depthIn, PNG_CONST int palette_numberIn,
-    PNG_CONST int interlace_typeIn,
-    PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn,
-    PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn,
-    PNG_CONST char *name,
-    PNG_CONST int use_input_precisionIn, PNG_CONST int scale16In,
-    PNG_CONST int expand16In, PNG_CONST int do_backgroundIn,
-    PNG_CONST png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
+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);
@@ -8885,7 +10447,7 @@ gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
 
       modification_reset(d.pm->modifications);
 
-      /* Get a png_struct for writing the image. */
+      /* 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);
 
@@ -9024,9 +10586,13 @@ perform_gamma_threshold_tests(png_modifier *pm)
    /* 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, 1/*gamma*/))
-      if (palette_number == 0)
+   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)
@@ -9049,11 +10615,11 @@ perform_gamma_threshold_tests(png_modifier *pm)
 }
 
 static void gamma_transform_test(png_modifier *pm,
-   PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
-   PNG_CONST int palette_number,
-   PNG_CONST int interlace_type, PNG_CONST double file_gamma,
-   PNG_CONST double screen_gamma, PNG_CONST png_byte sbit,
-   PNG_CONST int use_input_precision, PNG_CONST int scale16)
+   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];
@@ -9086,20 +10652,26 @@ static void perform_gamma_transform_tests(png_modifier *pm)
    png_byte bit_depth = 0;
    unsigned int palette_number = 0;
 
-   while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
+   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)
+      for (i=0; i<pm->ngamma_tests; ++i)
+      {
+         for (j=0; j<pm->ngamma_tests; ++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 (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;
+               if (fail(pm))
+                  return;
+            }
          }
+      }
    }
 }
 
@@ -9116,7 +10688,8 @@ static void perform_gamma_sbit_tests(png_modifier *pm)
       png_byte colour_type = 0, bit_depth = 0;
       unsigned int npalette = 0;
 
-      while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/))
+      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)))
@@ -9127,14 +10700,17 @@ static void perform_gamma_sbit_tests(png_modifier *pm)
          {
             unsigned int j;
 
-            for (j=0; j<pm->ngamma_tests; ++j) if (i != j)
+            for (j=0; j<pm->ngamma_tests; ++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 (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;
+                  if (fail(pm))
+                     return;
+               }
             }
          }
       }
@@ -9151,7 +10727,11 @@ static void perform_gamma_scale16_tests(png_modifier *pm)
 #  ifndef PNG_MAX_GAMMA_8
 #     define PNG_MAX_GAMMA_8 11
 #  endif
-#  define SBIT_16_TO_8 PNG_MAX_GAMMA_8
+#  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.
@@ -9204,12 +10784,12 @@ static void perform_gamma_scale16_tests(png_modifier *pm)
 #if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
    defined(PNG_READ_ALPHA_MODE_SUPPORTED)
 static void gamma_composition_test(png_modifier *pm,
-   PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
-   PNG_CONST int palette_number,
-   PNG_CONST int interlace_type, PNG_CONST double file_gamma,
-   PNG_CONST double screen_gamma,
-   PNG_CONST int use_input_precision, PNG_CONST int do_background,
-   PNG_CONST int expand_16)
+   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;
@@ -9307,8 +10887,17 @@ static void gamma_composition_test(png_modifier *pm,
    }
 
    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.
        */
@@ -9357,21 +10946,33 @@ perform_gamma_composition_tests(png_modifier *pm, int do_background,
 
    /* Skip the non-alpha cases - there is no setting of a transparency colour at
     * present.
-    */
-   while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
-      if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
+    *
+    * 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)
+      for (i=0; i<pm->ngamma_tests; ++i)
       {
-         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);
+         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;
+            if (fail(pm))
+               return;
+         }
       }
    }
 }
@@ -9474,7 +11075,7 @@ perform_gamma_test(png_modifier *pm, int summary)
          pm->calculations_use_input_precision = 0;
 
       if (summary)
-         summarize_gamma_errors(pm, 0/*who*/, 1/*low bit depth*/, 1/*indexed*/);
+         summarize_gamma_errors(pm, NULL/*who*/, 1/*low bit depth*/, 1/*indexed*/);
 
       if (fail(pm))
          return;
@@ -9578,7 +11179,7 @@ perform_gamma_test(png_modifier *pm, int summary)
  * be indexed adam7[y][x] and notice that the pass numbers are based at
  * 1, not 0 - the base libpng uses.
  */
-static PNG_CONST
+static const
 png_byte adam7[8][8] =
 {
    { 1,6,4,6,2,6,4,6 },
@@ -9600,8 +11201,10 @@ 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;
+   for (y=0; y<8; ++y)
+      for (x=0; x<8; ++x)
+         if (adam7[y][x] == pass)
+            return y;
    return 0xf;
 }
 
@@ -9610,8 +11213,10 @@ 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;
+   for (x=0; x<8; ++x)
+      for (y=0; y<8; ++y)
+         if (adam7[y][x] == pass)
+            return x;
    return 0xf;
 }
 
@@ -9620,18 +11225,24 @@ 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)
+   for (y=0; y<8; ++y)
    {
-      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! */
+      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! */
@@ -9654,18 +11265,24 @@ 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)
+   for (x=0; x<8; ++x)
    {
-      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! */
+      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! */
@@ -9729,8 +11346,9 @@ png_row_in_interlace_pass(png_uint_32 y, int pass)
    int x;
    y &= 7;
    ++pass;
-   for (x=0; x<8; ++x) if (adam7[y][x] == pass)
-      return 1;
+   for (x=0; x<8; ++x)
+      if (adam7[y][x] == pass)
+         return 1;
 
    return 0;
 }
@@ -9742,8 +11360,9 @@ png_col_in_interlace_pass(png_uint_32 x, int pass)
    int y;
    x &= 7;
    ++pass;
-   for (y=0; y<8; ++y) if (adam7[y][x] == pass)
-      return 1;
+   for (y=0; y<8; ++y)
+      if (adam7[y][x] == pass)
+         return 1;
 
    return 0;
 }
@@ -9757,11 +11376,17 @@ png_pass_rows(png_uint_32 height, int pass)
 
    height &= 7;
    ++pass;
-   for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+   for (y=0; y<8; ++y)
    {
-      rows += tiles;
-      if (y < height) ++rows;
-      break; /* i.e. break the 'x', column, loop. */
+      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;
@@ -9776,11 +11401,17 @@ png_pass_cols(png_uint_32 width, int pass)
 
    width &= 7;
    ++pass;
-   for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+   for (x=0; x<8; ++x)
    {
-      cols += tiles;
-      if (x < width) ++cols;
-      break; /* i.e. break the 'y', row, loop. */
+      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;
@@ -9845,23 +11476,36 @@ perform_interlace_macro_validation(void)
        */
       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: */
-         m = PNG_ROW_FROM_PASS_ROW(v, pass);
-         f = png_row_from_pass_row(v, pass);
-         if (m != f)
+         if (v < png_pass_rows(0xFFFFFFFFU, pass))
          {
-            fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
-               v, pass, m, f);
-            exit(99);
+            m = PNG_ROW_FROM_PASS_ROW(v, pass);
+            f = png_row_from_pass_row(v, pass);
+            if (m != f)
+            {
+               fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
+                  v, pass, m, f);
+               exit(99);
+            }
          }
 
-         m = PNG_COL_FROM_PASS_COL(v, pass);
-         f = png_col_from_pass_col(v, pass);
-         if (m != f)
+         if (v < png_pass_cols(0xFFFFFFFFU, pass))
          {
-            fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
-               v, pass, m, f);
-            exit(99);
+            m = PNG_COL_FROM_PASS_COL(v, pass);
+            f = png_col_from_pass_col(v, pass);
+            if (m != f)
+            {
+               fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
+                  v, pass, m, f);
+               exit(99);
+            }
          }
 
          m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
@@ -9929,7 +11573,7 @@ perform_interlace_macro_validation(void)
  * The png_modifier code assumes that encodings[0] is sRGB and treats it
  * specially: do not change the first entry in this list!
  */
-static PNG_CONST color_encoding test_encodings[] =
+static const color_encoding test_encodings[] =
 {
 /* sRGB: must be first in this list! */
 /*gamma:*/ { 1/2.2,
@@ -9951,6 +11595,11 @@ static PNG_CONST color_encoding test_encodings[] =
 /*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
@@ -10022,11 +11671,11 @@ static void signal_handler(int signum)
 /* main program */
 int main(int argc, char **argv)
 {
-   volatile int summary = 1;  /* Print the error summary at the end */
-   volatile int memstats = 0; /* Print memory statistics at the end */
+   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: */
-   PNG_CONST char *volatile touch = NULL;
+   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.)
@@ -10042,6 +11691,10 @@ int main(int argc, char **argv)
 
    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);
@@ -10088,18 +11741,34 @@ int main(int argc, char **argv)
 
    /* Store the test gammas */
    pm.gammas = gammas;
-   pm.ngammas = (sizeof gammas) / (sizeof gammas[0]);
+   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 = (sizeof test_encodings) / (sizeof test_encodings[0]);
+   pm.nencodings = ARRAY_SIZE(test_encodings);
 
-   pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+#  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
+    * way libpng treats the 16_TO_8 flag when building the gamma tables in
     * releases up to 1.6.0.
     *
     * TODO: review this
@@ -10109,7 +11778,7 @@ int main(int argc, char **argv)
 
    /* Some default values (set the behavior for 'make check' here).
     * These values simply control the maximum error permitted in the gamma
-    * transformations.  The practial limits for human perception are described
+    * 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.
@@ -10121,7 +11790,11 @@ int main(int argc, char **argv)
    pm.maxout16 = .499;  /* Error in *encoded* value */
    pm.maxabs16 = .00005;/* 1/20000 */
    pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */
-   pm.maxcalcG = 1./((1<<PNG_MAX_GAMMA_8)-1);
+#  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
@@ -10151,7 +11824,11 @@ int main(int argc, char **argv)
       else if (strcmp(*argv, "-q") == 0)
          summary = pm.this.verbose = pm.log = 0;
 
-      else if (strcmp(*argv, "-w") == 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)
@@ -10182,30 +11859,30 @@ int main(int argc, char **argv)
 #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;
+         pm.test_gamma_background = 1; /* composition */
          pm.test_gamma_alpha_mode = 1;
-         }
+      }
 
       else if (strcmp(*argv, "--nogamma") == 0)
          pm.ngamma_tests = 0;
@@ -10252,6 +11929,24 @@ int main(int argc, char **argv)
       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;
 
@@ -10266,12 +11961,12 @@ int main(int argc, char **argv)
 
       else if (strcmp(*argv, "--interlace") == 0)
       {
-#        ifdef PNG_WRITE_INTERLACING_SUPPORTED
+#        if CAN_WRITE_INTERLACE
             pm.interlace_type = PNG_INTERLACE_ADAM7;
-#        else
+#        else /* !CAN_WRITE_INTERLACE */
             fprintf(stderr, "pngvalid: no write interlace support\n");
             return SKIP;
-#        endif
+#        endif /* !CAN_WRITE_INTERLACE */
       }
 
       else if (strcmp(*argv, "--use-input-precision") == 0)
@@ -10349,12 +12044,18 @@ int main(int argc, char **argv)
          const char *arg = 9+*argv;
          unsigned char option=0, setting=0;
 
-#ifdef PNG_ARM_NEON_API_SUPPORTED
+#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;
@@ -10442,7 +12143,7 @@ int main(int argc, char **argv)
    Try
    {
       /* Make useful base images */
-      make_transform_images(&pm.this);
+      make_transform_images(&pm);
 
       /* Perform the standard and gamma tests. */
       if (pm.test_standard)