Update libpng from 1.6.3 to 1.6.10

Change-Id: I76e81e7fd267d15991cd342c5caeb2fe77964ebf
diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c
index fbd6778..788b693 100644
--- a/contrib/libtests/pngvalid.c
+++ b/contrib/libtests/pngvalid.c
@@ -1,8 +1,8 @@
 
 /* pngvalid.c - validate libpng by constructing then reading png files.
  *
- * Last changed in libpng 1.6.1 [March 28, 2013]
- * Copyright (c) 2013 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ * Copyright (c) 2014 Glenn Randers-Pehrson
  * Written by John Cunningham Bowler
  *
  * This code is released under the libpng license.
@@ -43,7 +43,25 @@
 #  include "../../png.h"
 #endif
 
-#ifdef PNG_WRITE_SUPPORTED /* else pngvalid can do nothing */
+#ifdef PNG_ZLIB_HEADER
+#  include PNG_ZLIB_HEADER
+#else
+#  include <zlib.h>   /* For crc32 */
+#endif
+
+/* 1.6.1 added support for the configure test harness, which uses 77 to indicate
+ * a skipped test, in earlier versions we need to succeed on a skipped test, so:
+ */
+#if PNG_LIBPNG_VER < 10601
+#  define SKIP 0
+#else
+#  define SKIP 77
+#endif
+
+/* pngvalid requires write support and one of the fixed or floating point APIs.
+ */
+#if defined(PNG_WRITE_SUPPORTED) &&\
+   (defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED))
 
 #if PNG_LIBPNG_VER < 10500
 /* This deliberately lacks the PNG_CONST. */
@@ -88,8 +106,6 @@
 #  define png_const_structp png_structp
 #endif
 
-#include <zlib.h>   /* For crc32 */
-
 #include <float.h>  /* For floating point constants */
 #include <stdlib.h> /* For malloc */
 #include <string.h> /* For memcpy, memset */
@@ -344,11 +360,16 @@
 
 static int
 next_format(png_bytep colour_type, png_bytep bit_depth,
-   unsigned int* palette_number)
+   unsigned int* palette_number, int no_low_depth_gray)
 {
    if (*bit_depth == 0)
    {
-      *colour_type = 0, *bit_depth = 1, *palette_number = 0;
+      *colour_type = 0;
+      if (no_low_depth_gray)
+         *bit_depth = 8;
+      else
+         *bit_depth = 1;
+      *palette_number = 0;
       return 1;
    }
 
@@ -616,7 +637,12 @@
    unsigned int       validated :1;   /* used as a temporary flag */
    int                nerrors;
    int                nwarnings;
-   char               test[128]; /* Name of test */
+   int                noptions;       /* number of options below: */
+   struct {
+      unsigned char   option;         /* option number, 0..30 */
+      unsigned char   setting;        /* setting (unset,invalid,on,off) */
+   }                  options[16];
+   char               test[128];      /* Name of test */
    char               error[256];
 
    /* Read fields */
@@ -717,6 +743,7 @@
    ps->new.prev = NULL;
    ps->palette = NULL;
    ps->npalette = 0;
+   ps->noptions = 0;
 }
 
 static void
@@ -894,7 +921,7 @@
 #endif /* PNG_READ_SUPPORTED */
 
 /* Functions to use as PNG callbacks. */
-static void
+static void PNGCBAPI
 store_error(png_structp ppIn, png_const_charp message) /* PNG_NORETURN */
 {
    png_const_structp pp = ppIn;
@@ -910,7 +937,7 @@
    }
 }
 
-static void
+static void PNGCBAPI
 store_warning(png_structp ppIn, png_const_charp message)
 {
    png_const_structp pp = ppIn;
@@ -1061,7 +1088,7 @@
 }
 #endif /* PNG_READ_SUPPORTED */
 
-static void
+static void PNGCBAPI
 store_write(png_structp ppIn, png_bytep pb, png_size_t st)
 {
    png_const_structp pp = ppIn;
@@ -1089,7 +1116,7 @@
    }
 }
 
-static void
+static void PNGCBAPI
 store_flush(png_structp ppIn)
 {
    UNUSED(ppIn) /*DOES NOTHING*/
@@ -1183,7 +1210,7 @@
    }
 }
 
-static void
+static void PNGCBAPI
 store_read(png_structp ppIn, png_bytep pb, png_size_t st)
 {
    png_const_structp pp = ppIn;
@@ -1257,6 +1284,7 @@
 #endif /* PNG_READ_SUPPORTED */
 
 /***************************** MEMORY MANAGEMENT*** ***************************/
+#ifdef PNG_USER_MEM_SUPPORTED
 /* A store_memory is simply the header for an allocated block of memory.  The
  * pointer returned to libpng is just after the end of the header block, the
  * allocated memory is followed by a second copy of the 'mark'.
@@ -1375,7 +1403,7 @@
 }
 
 /* The memory callbacks: */
-static png_voidp
+static png_voidp PNGCBAPI
 store_malloc(png_structp ppIn, png_alloc_size_t cb)
 {
    png_const_structp pp = ppIn;
@@ -1424,7 +1452,7 @@
    return new;
 }
 
-static void
+static void PNGCBAPI
 store_free(png_structp ppIn, png_voidp memory)
 {
    png_const_structp pp = ppIn;
@@ -1457,6 +1485,7 @@
    this->next = NULL;
    store_memory_free(pp, pool, this);
 }
+#endif /* PNG_USER_MEM_SUPPORTED */
 
 /* Setup functions. */
 /* Cleanup when aborting a write or after storing the new file. */
@@ -1482,7 +1511,9 @@
    /* And make sure that all the memory has been freed - this will output
     * spurious errors in the case of memory corruption above, but this is safe.
     */
-   store_pool_delete(ps, &ps->write_memory_pool);
+#  ifdef PNG_USER_MEM_SUPPORTED
+      store_pool_delete(ps, &ps->write_memory_pool);
+#  endif
 
    store_freenew(ps);
 }
@@ -1506,18 +1537,32 @@
       store_write_reset(ps);
       safecat(ps->wname, sizeof ps->wname, 0, name);
 
-      /* Don't do the slow memory checks if doing a speed test. */
-      if (ps->speed)
+      /* Don't do the slow memory checks if doing a speed test, also if user
+       * memory is not supported we can't do it anyway.
+       */
+#     ifdef PNG_USER_MEM_SUPPORTED
+         if (!ps->speed)
+            ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
+               ps, store_error, store_warning, &ps->write_memory_pool,
+               store_malloc, store_free);
+
+         else
+#     endif
          ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
             ps, store_error, store_warning);
 
-      else
-         ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
-            ps, store_error, store_warning, &ps->write_memory_pool,
-            store_malloc, store_free);
-
       png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
 
+#     ifdef PNG_SET_OPTION_SUPPORTED
+         {
+            int opt;
+            for (opt=0; opt<ps->noptions; ++opt)
+               if (png_set_option(ps->pwrite, ps->options[opt].option,
+                  ps->options[opt].setting) == PNG_OPTION_INVALID)
+                  png_error(ps->pwrite, "png option invalid");
+         }
+#     endif
+
       if (ppi != NULL)
          *ppi = ps->piwrite = png_create_info_struct(ps->pwrite);
    }
@@ -1553,8 +1598,10 @@
       }
 #  endif
 
-   /* Always do this to be safe. */
-   store_pool_delete(ps, &ps->read_memory_pool);
+#  ifdef PNG_USER_MEM_SUPPORTED
+      /* Always do this to be safe. */
+      store_pool_delete(ps, &ps->read_memory_pool);
+#  endif
 
    ps->current = NULL;
    ps->next = NULL;
@@ -1581,14 +1628,14 @@
       pf = pf->next;
    }
 
-      {
+   {
       size_t pos;
       char msg[FILE_NAME_SIZE+64];
 
       pos = standard_name_from_id(msg, sizeof msg, 0, id);
       pos = safecat(msg, sizeof msg, pos, ": file not found");
       png_error(ps->pread, msg);
-      }
+   }
 }
 
 /* The main interface for reading a saved file - pass the id number of the file
@@ -1614,14 +1661,16 @@
     * However, given that store_error works correctly in these circumstances
     * we don't ever expect NULL in this program.
     */
-   if (ps->speed)
-      ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps,
-          store_error, store_warning);
+#  ifdef PNG_USER_MEM_SUPPORTED
+      if (!ps->speed)
+         ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
+             store_error, store_warning, &ps->read_memory_pool, store_malloc,
+             store_free);
 
-   else
-      ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
-          store_error, store_warning, &ps->read_memory_pool, store_malloc,
-          store_free);
+      else
+#  endif
+   ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps, store_error,
+      store_warning);
 
    if (ps->pread == NULL)
    {
@@ -1633,6 +1682,16 @@
       Throw ps;
    }
 
+#  ifdef PNG_SET_OPTION_SUPPORTED
+      {
+         int opt;
+         for (opt=0; opt<ps->noptions; ++opt)
+            if (png_set_option(ps->pread, ps->options[opt].option,
+               ps->options[opt].setting) == PNG_OPTION_INVALID)
+                  png_error(ps->pread, "png option invalid");
+      }
+#  endif
+
    store_read_set(ps, id);
 
    if (ppi != NULL)
@@ -1828,6 +1887,7 @@
    double                   maxout16; /* Maximum output value error */
    double                   maxabs16; /* Absolute sample error 0..1 */
    double                   maxcalc16;/* Absolute sample error 0..1 */
+   double                   maxcalcG; /* Absolute sample error 0..1 */
    double                   maxpc16;  /* Percentage sample error 0..100% */
 
    /* This is set by transforms that need to allow a higher limit, it is an
@@ -1867,10 +1927,15 @@
    /* Run the odd-sized image and interlace read/write tests? */
    unsigned int             test_size :1;
 
-   /* Run tests on reading with a combiniation of transforms, */
+   /* Run tests on reading with a combination of transforms, */
    unsigned int             test_transform :1;
 
-   /* When to use the use_input_precision option: */
+   /* When to use the use_input_precision option, this controls the gamma
+    * validation code checks.  If set any value that is within the transformed
+    * range input-.5 to input+.5 will be accepted, otherwise the value must be
+    * within the normal limits.  It should not be necessary to set this; the
+    * result should always be exact within the permitted error limits.
+    */
    unsigned int             use_input_precision :1;
    unsigned int             use_input_precision_sbit :1;
    unsigned int             use_input_precision_16to8 :1;
@@ -1880,8 +1945,8 @@
     */
    unsigned int             calculations_use_input_precision :1;
 
-   /* If set assume that the calculations are done in 16 bits even if both input
-    * and output are 8 bit or less.
+   /* If set assume that the calculations are done in 16 bits even if the sample
+    * depth is 8 bits.
     */
    unsigned int             assume_16_bit_calculations :1;
 
@@ -1908,7 +1973,7 @@
 
 /* This returns true if the test should be stopped now because it has already
  * failed and it is running silently.
- */
+  */
 static int fail(png_modifier *pm)
 {
    return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 ||
@@ -1936,6 +2001,7 @@
    pm->test_uses_encoding = 0;
    pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0;
    pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0;
+   pm->maxcalcG = 0;
    pm->limit = 4E-3;
    pm->log8 = pm->log16 = 0; /* Means 'off' */
    pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
@@ -1950,6 +2016,7 @@
    pm->use_input_precision_sbit = 0;
    pm->use_input_precision_16to8 = 0;
    pm->calculations_use_input_precision = 0;
+   pm->assume_16_bit_calculations = 0;
    pm->test_gamma_threshold = 0;
    pm->test_gamma_transform = 0;
    pm->test_gamma_sbit = 0;
@@ -1964,8 +2031,16 @@
 }
 
 #ifdef PNG_READ_TRANSFORMS_SUPPORTED
+
+/* This controls use of checks that explicitly know how libpng digitizes the
+ * samples in calculations; setting this circumvents simple error limit checking
+ * in the rgb_to_gray check, replacing it with an exact copy of the libpng 1.5
+ * algorithm.
+ */
+#define DIGITIZE PNG_LIBPNG_VER < 10700
+
 /* If pm->calculations_use_input_precision is set then operations will happen
- * with only 8 bit precision unless both the input and output bit depth are 16.
+ * with the precision of the input, not the precision of the output depth.
  *
  * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16
  * bit precision.  This only affects those of the following limits that pertain
@@ -1973,8 +2048,8 @@
  * called directly.
  */
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-static double digitize(PNG_CONST png_modifier *pm, double value,
-   int sample_depth, int do_round)
+#if DIGITIZE
+static double digitize(double value, int depth, int do_round)
 {
    /* 'value' is in the range 0 to 1, the result is the same value rounded to a
     * multiple of the digitization factor - 8 or 16 bits depending on both the
@@ -1982,14 +2057,14 @@
     * rounding and 'do_round' should be 1, if it is 0 the digitized value will
     * be truncated.
     */
-   PNG_CONST unsigned int digitization_factor =
-      (pm->assume_16_bit_calculations || sample_depth == 16) ? 65535 : 255;
+   PNG_CONST unsigned int digitization_factor = (1U << depth) -1;
 
    /* Limiting the range is done as a convenience to the caller - it's easier to
     * do it once here than every time at the call site.
     */
    if (value <= 0)
       value = 0;
+
    else if (value >= 1)
       value = 1;
 
@@ -1998,31 +2073,30 @@
    return floor(value)/digitization_factor;
 }
 #endif
+#endif /* RGB_TO_GRAY */
 
-#if defined(PNG_READ_GAMMA_SUPPORTED) ||\
-   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+#ifdef PNG_READ_GAMMA_SUPPORTED
 static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
 {
    /* Absolute error permitted in linear values - affected by the bit depth of
     * the calculations.
     */
-   if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
-      !pm->calculations_use_input_precision)))
+   if (pm->assume_16_bit_calculations ||
+      (pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
       return pm->maxabs16;
    else
       return pm->maxabs8;
 }
-#endif
 
-#ifdef PNG_READ_GAMMA_SUPPORTED
 static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth)
 {
    /* Error in the linear composition arithmetic - only relevant when
     * composition actually happens (0 < alpha < 1).
     */
-   if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
-      !pm->calculations_use_input_precision)))
+   if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
       return pm->maxcalc16;
+   else if (pm->assume_16_bit_calculations)
+      return pm->maxcalcG;
    else
       return pm->maxcalc8;
 }
@@ -2032,8 +2106,8 @@
    /* Percentage error permitted in the linear values.  Note that the specified
     * value is a percentage but this routine returns a simple number.
     */
-   if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
-      !pm->calculations_use_input_precision)))
+   if (pm->assume_16_bit_calculations ||
+      (pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
       return pm->maxpc16 * .01;
    else
       return pm->maxpc8 * .01;
@@ -2065,8 +2139,7 @@
    if (out_depth == 4)
       return .90644-.5;
 
-   if (out_depth == 16 && (in_depth == 16 ||
-      !pm->calculations_use_input_precision))
+   if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
       return pm->maxout16;
 
    /* This is the case where the value was calculated at 8-bit precision then
@@ -2099,8 +2172,7 @@
       return pm->log8;
    }
 
-   if (out_depth == 16 && (in_depth == 16 ||
-      !pm->calculations_use_input_precision))
+   if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
    {
       if (pm->log16 == 0)
          return 65536;
@@ -2125,8 +2197,8 @@
 static int output_quantization_factor(PNG_CONST png_modifier *pm, int in_depth,
    int out_depth)
 {
-   if (out_depth == 16 && in_depth != 16
-      && pm->calculations_use_input_precision)
+   if (out_depth == 16 && in_depth != 16 &&
+      pm->calculations_use_input_precision)
       return 257;
    else
       return 1;
@@ -2599,7 +2671,7 @@
 }
 
 /* The callback: */
-static void
+static void PNGCBAPI
 modifier_read(png_structp ppIn, png_bytep pb, png_size_t st)
 {
    png_const_structp pp = ppIn;
@@ -2919,6 +2991,12 @@
  * height of 16 rows.  The width and height are stored in the FILEID and, being
  * non-zero, indicate a size file.
  *
+ * Because the PNG filter code is typically the largest CPU consumer within
+ * libpng itself there is a tendency to attempt to optimize it.  This results in
+ * special case code which needs to be validated.  To cause this to happen the
+ * 'size' images are made to use each possible filter, in so far as this is
+ * possible for smaller images.
+ *
  * For palette image (colour type 3) multiple transform images are stored with
  * the same bit depth to allow testing of more colour combinations -
  * particularly important for testing the gamma code because libpng uses a
@@ -3077,8 +3155,10 @@
       for (; i<256; ++i)
          tRNS[i] = 24;
 
-      if (j > 0)
-         png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+#     ifdef PNG_WRITE_tRNS_SUPPORTED
+         if (j > 0)
+            png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+#     endif
    }
 }
 
@@ -3324,6 +3404,31 @@
  */
 #define DEPTH(bd) ((png_byte)(1U << (bd)))
 
+/* This is just a helper for compiling on minimal systems with no write
+ * interlacing support.  If there is no write interlacing we can't generate test
+ * cases with interlace:
+ */
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+#  define INTERLACE_LAST PNG_INTERLACE_LAST
+#  define check_interlace_type(type) ((void)(type))
+#else
+#  define INTERLACE_LAST (PNG_INTERLACE_NONE+1)
+#  define png_set_interlace_handling(a) (1)
+
+static void
+check_interlace_type(int PNG_CONST interlace_type)
+{
+   if (interlace_type != PNG_INTERLACE_NONE)
+   {
+      /* This is an internal error - --interlace tests should be skipped, not
+       * attempted.
+       */
+      fprintf(stderr, "pngvalid: no interlace support\n");
+      exit(99);
+   }
+}
+#endif
+
 /* Make a standardized image given a 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
@@ -3337,6 +3442,8 @@
 {
    context(ps, fault);
 
+   check_interlace_type(interlace_type);
+
    Try
    {
       png_infop pi;
@@ -3473,12 +3580,12 @@
    /* Use next_format to enumerate all the combinations we test, including
     * generating multiple low bit depth palette images.
     */
-   while (next_format(&colour_type, &bit_depth, &palette_number))
+   while (next_format(&colour_type, &bit_depth, &palette_number, 0))
    {
       int interlace_type;
 
       for (interlace_type = PNG_INTERLACE_NONE;
-           interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
+           interlace_type < INTERLACE_LAST; ++interlace_type)
       {
          char name[FILE_NAME_SIZE];
 
@@ -3568,6 +3675,12 @@
 {
    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
    {
       png_infop pi;
@@ -3634,6 +3747,9 @@
          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
@@ -3687,7 +3803,24 @@
                      continue;
                }
 
-               /* Only get to here if the row has some pixels in it. */
+#           ifdef PNG_WRITE_FILTER_SUPPORTED
+               /* Only get to here if the row has some pixels in it, set the
+                * filters to 'all' for the very first row and thereafter to a
+                * single filter.  It isn't well documented, but png_set_filter
+                * does accept a filter number (per the spec) as well as a bit
+                * mask.
+                *
+                * The apparent wackiness of decrementing nfilter rather than
+                * incrementing is so that Paeth gets used in all images bigger
+                * than 1 row - it's the tricky one.
+                */
+               png_set_filter(pp, 0/*method*/,
+                  nfilter >= PNG_FILTER_VALUE_LAST ? PNG_ALL_FILTERS : nfilter);
+
+               if (nfilter-- == 0)
+                  nfilter = PNG_FILTER_VALUE_LAST-1;
+#           endif
+
                png_write_row(pp, row);
             }
          }
@@ -3754,10 +3887,12 @@
                width, height, 0);
             make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
                width, height, 1);
+#        ifdef PNG_WRITE_INTERLACING_SUPPORTED
             make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
                width, height, 0);
             make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
                width, height, 1);
+#        endif
          }
       }
    }
@@ -3848,6 +3983,8 @@
 
    context(ps, fault);
 
+   check_interlace_type(interlace_type);
+
    Try
    {
       png_structp pp;
@@ -3951,7 +4088,7 @@
       int interlace_type;
 
       for (interlace_type = PNG_INTERLACE_NONE;
-           interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
+           interlace_type < INTERLACE_LAST; ++interlace_type)
       {
          unsigned int test;
          char name[FILE_NAME_SIZE];
@@ -3972,7 +4109,7 @@
 
    return 1; /* keep going */
 }
-#endif
+#endif /* PNG_WARNINGS_SUPPORTED */
 
 static void
 perform_error_test(png_modifier *pm)
@@ -4149,6 +4286,7 @@
       dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT =
          dp->bit_depth;
    dp->interlace_type = INTERLACE_FROM_ID(id);
+   check_interlace_type(dp->interlace_type);
    dp->id = id;
    /* All the rest are filled in after the read_info: */
    dp->w = 0;
@@ -4537,7 +4675,7 @@
    standard_info_part2(dp, pp, pi, nImages);
 }
 
-static void
+static void PNGCBAPI
 standard_info(png_structp pp, png_infop pi)
 {
    standard_display *dp = voidcast(standard_display*,
@@ -4549,7 +4687,7 @@
    standard_info_imp(dp, pp, pi, 1 /*only one image*/);
 }
 
-static void
+static void PNGCBAPI
 progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
 {
    png_const_structp pp = ppIn;
@@ -4758,7 +4896,7 @@
 
 static void
 standard_text_validate(standard_display *dp, png_const_structp pp,
-   png_infop pi)
+   png_infop pi, int check_end)
 {
    png_textp tp = NULL;
    png_uint_32 num_text = png_get_text(pp, pi, &tp, NULL);
@@ -4766,7 +4904,13 @@
    if (num_text == 2 && tp != NULL)
    {
       standard_check_text(pp, tp, "image name", dp->ps->current->name);
-      standard_check_text(pp, tp+1, "end marker", "end");
+
+      /* This exists because prior to 1.5.18 the progressive reader left the
+       * png_struct z_stream unreset at the end of the image, so subsequent
+       * attempts to use it simply returns Z_STREAM_END.
+       */
+      if (check_end)
+         standard_check_text(pp, tp+1, "end marker", "end");
    }
 
    else
@@ -4779,7 +4923,7 @@
    }
 }
 #else
-#  define standard_text_validate(dp,pp,pi) ((void)0)
+#  define standard_text_validate(dp,pp,pi,check_end) ((void)0)
 #endif
 
 static void
@@ -4857,7 +5001,7 @@
    dp->ps->validated = 1;
 }
 
-static void
+static void PNGCBAPI
 standard_end(png_structp ppIn, png_infop pi)
 {
    png_const_structp pp = ppIn;
@@ -4869,7 +5013,8 @@
    /* Validate the image - progressive reading only produces one variant for
     * interlaced images.
     */
-   standard_text_validate(dp, pp, pi);
+   standard_text_validate(dp, pp, pi,
+      PNG_LIBPNG_VER >= 10518/*check_end: see comments above*/);
    standard_image_validate(dp, pp, 0, -1);
 }
 
@@ -4940,7 +5085,7 @@
              */
             if (!d.speed)
             {
-               standard_text_validate(&d, pp, pi);
+               standard_text_validate(&d, pp, pi, 1/*check_end*/);
                standard_image_validate(&d, pp, 0, 1);
             }
             else
@@ -4971,7 +5116,7 @@
       int interlace_type;
 
       for (interlace_type = PNG_INTERLACE_NONE;
-           interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
+           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);
@@ -5046,6 +5191,7 @@
          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);
@@ -5059,6 +5205,7 @@
 
          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
@@ -5071,12 +5218,14 @@
          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), 1/*do_interlace*/,
             pm->use_update_info);
 
          if (fail(pm))
             return 0;
+#     endif
       }
    }
 
@@ -5687,7 +5836,7 @@
    }
 }
 
-static void
+static void PNGCBAPI
 transform_info(png_structp pp, png_infop pi)
 {
    transform_info_imp(voidcast(transform_display*, png_get_progressive_ptr(pp)),
@@ -5788,12 +5937,9 @@
 
       memset(out_palette, 0x5e, sizeof out_palette);
 
-      /* assume-8-bit-calculations means assume that if the input has 8 bit
-       * (or less) samples and the output has 16 bit samples the calculations
-       * will be done with 8 bit precision, not 16.
-       *
-       * TODO: fix this in libpng; png_set_expand_16 should cause 16 bit
-       * calculations to be used throughout.
+      /* use-input-precision means assume that if the input has 8 bit (or less)
+       * samples and the output has 16 bit samples the calculations will be done
+       * with 8 bit precision, not 16.
        */
       if (in_ct == PNG_COLOR_TYPE_PALETTE || in_bd < 16)
          in_sample_depth = 8;
@@ -5804,7 +5950,8 @@
          !dp->pm->calculations_use_input_precision)
          digitization_error = .5;
 
-      /* Else errors are at 8 bit precision, scale .5 in 8 bits to the 16 bits:
+      /* Else calculations are at 8 bit precision, and the output actually
+       * consists of scaled 8-bit values, so scale .5 in 8 bits to the 16 bits:
        */
       else
          digitization_error = .5 * 257;
@@ -5896,7 +6043,7 @@
    dp->this.ps->validated = 1;
 }
 
-static void
+static void PNGCBAPI
 transform_end(png_structp ppIn, png_infop pi)
 {
    png_const_structp pp = ppIn;
@@ -5988,7 +6135,7 @@
 
    Catch(fault)
    {
-      modifier_reset((png_modifier*)fault);
+      modifier_reset(voidcast(png_modifier*,(void*)fault));
    }
 }
 
@@ -6620,10 +6767,23 @@
    {
       if (that->this.bit_depth == 16 || pm->assume_16_bit_calculations)
       {
-         /* The 16 bit case ends up producing a maximum error of about
-          * +/-5 in 65535, allow for +/-8 with the given gamma.
+         /* The computations have the form:
+          *
+          *    r * rc + g * gc + b * bc
+          *
+          *  Each component of which is +/-1/65535 from the gamma_to_1 table
+          *  lookup, resulting in a base error of +/-6.  The gamma_from_1
+          *  conversion adds another +/-2 in the 16-bit case and
+          *  +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case.
           */
-         that->pm->limit += pow(8./65535, data.gamma);
+         that->pm->limit += pow(
+#           if PNG_MAX_GAMMA_8 < 14
+               (that->this.bit_depth == 16 ? 8. :
+                  6. + (1<<(15-PNG_MAX_GAMMA_8)))
+#           else
+               8.
+#           endif
+               /65535, data.gamma);
       }
 
       else
@@ -6631,8 +6791,23 @@
          /* Rounding to 8 bits in the linear space causes massive errors which
           * will trigger the error check in transform_range_check.  Fix that
           * here by taking the gamma encoding into account.
+          *
+          * When DIGITIZE is set because a pre-1.7 version of libpng is being
+          * tested allow a bigger slack.
+          *
+          * NOTE: this magic number was determined by experiment to be 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.
           */
-         that->pm->limit += pow(1./255, data.gamma);
+         that->pm->limit += pow(
+#           if DIGITIZE
+               1.1
+#           else
+               1.
+#           endif
+               /255, data.gamma);
       }
    }
 
@@ -6641,7 +6816,7 @@
       /* With no gamma correction a large error comes from the truncation of the
        * calculation in the 8 bit case, allow for that here.
        */
-      if (that->this.bit_depth != 16)
+      if (that->this.bit_depth != 16 && !pm->assume_16_bit_calculations)
          that->pm->limit += 4E-3;
    }
 }
@@ -6786,9 +6961,14 @@
          image_pixel_convert_PLTE(that);
 
       /* Image now has RGB channels... */
+#  if DIGITIZE
       {
          PNG_CONST png_modifier *pm = display->pm;
-         PNG_CONST unsigned int sample_depth = that->sample_depth;
+         const unsigned int sample_depth = that->sample_depth;
+         const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
+            sample_depth);
+         const unsigned int gamma_depth = (sample_depth == 16 ? 16 :
+            (pm->assume_16_bit_calculations ? PNG_MAX_GAMMA_8 : sample_depth));
          int isgray;
          double r, g, b;
          double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi;
@@ -6804,28 +6984,28 @@
           */
          r = rlo = rhi = that->redf;
          rlo -= that->rede;
-         rlo = digitize(pm, rlo, sample_depth, 1/*round*/);
+         rlo = digitize(rlo, calc_depth, 1/*round*/);
          rhi += that->rede;
-         rhi = digitize(pm, rhi, sample_depth, 1/*round*/);
+         rhi = digitize(rhi, calc_depth, 1/*round*/);
 
          g = glo = ghi = that->greenf;
          glo -= that->greene;
-         glo = digitize(pm, glo, sample_depth, 1/*round*/);
+         glo = digitize(glo, calc_depth, 1/*round*/);
          ghi += that->greene;
-         ghi = digitize(pm, ghi, sample_depth, 1/*round*/);
+         ghi = digitize(ghi, calc_depth, 1/*round*/);
 
          b = blo = bhi = that->bluef;
          blo -= that->bluee;
-         blo = digitize(pm, blo, sample_depth, 1/*round*/);
+         blo = digitize(blo, calc_depth, 1/*round*/);
          bhi += that->greene;
-         bhi = digitize(pm, bhi, sample_depth, 1/*round*/);
+         bhi = digitize(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 = abserr(pm, sample_depth, sample_depth);
+            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
@@ -6834,16 +7014,16 @@
              * where the real errors are introduced.
              */
             r = pow(r, power);
-            rlo = digitize(pm, pow(rlo, power)-abse, sample_depth, 1);
-            rhi = digitize(pm, pow(rhi, power)+abse, sample_depth, 1);
+            rlo = digitize(pow(rlo, power)-abse, calc_depth, 1);
+            rhi = digitize(pow(rhi, power)+abse, calc_depth, 1);
 
             g = pow(g, power);
-            glo = digitize(pm, pow(glo, power)-abse, sample_depth, 1);
-            ghi = digitize(pm, pow(ghi, power)+abse, sample_depth, 1);
+            glo = digitize(pow(glo, power)-abse, calc_depth, 1);
+            ghi = digitize(pow(ghi, power)+abse, calc_depth, 1);
 
             b = pow(b, power);
-            blo = digitize(pm, pow(blo, power)-abse, sample_depth, 1);
-            bhi = digitize(pm, pow(bhi, power)+abse, sample_depth, 1);
+            blo = digitize(pow(blo, power)-abse, calc_depth, 1);
+            bhi = digitize(pow(bhi, power)+abse, calc_depth, 1);
          }
 
          /* Now calculate the actual gray values.  Although the error in the
@@ -6860,18 +7040,18 @@
             b * data.blue_coefficient;
 
          {
-            PNG_CONST int do_round = data.gamma != 1 || sample_depth == 16;
+            PNG_CONST int do_round = data.gamma != 1 || calc_depth == 16;
             PNG_CONST double ce = 1. / 32768;
 
-            graylo = digitize(pm, rlo * (data.red_coefficient-ce) +
+            graylo = digitize(rlo * (data.red_coefficient-ce) +
                glo * (data.green_coefficient-ce) +
-               blo * (data.blue_coefficient-ce), sample_depth, do_round);
+               blo * (data.blue_coefficient-ce), gamma_depth, do_round);
             if (graylo <= 0)
                graylo = 0;
 
-            grayhi = digitize(pm, rhi * (data.red_coefficient+ce) +
+            grayhi = digitize(rhi * (data.red_coefficient+ce) +
                ghi * (data.green_coefficient+ce) +
-               bhi * (data.blue_coefficient+ce), sample_depth, do_round);
+               bhi * (data.blue_coefficient+ce), gamma_depth, do_round);
             if (grayhi >= 1)
                grayhi = 1;
          }
@@ -6882,8 +7062,8 @@
             PNG_CONST double power = data.gamma;
 
             gray = pow(gray, power);
-            graylo = digitize(pm, pow(graylo, power), sample_depth, 1);
-            grayhi = digitize(pm, pow(grayhi, power), sample_depth, 1);
+            graylo = digitize(pow(graylo, power), sample_depth, 1);
+            grayhi = digitize(pow(grayhi, power), sample_depth, 1);
          }
 
          /* Now the error can be calculated.
@@ -6901,7 +7081,7 @@
                err = fabs(graylo-gray);
 
             /* Check that this worked: */
-            if (err > display->pm->limit)
+            if (err > pm->limit)
             {
                size_t pos = 0;
                char buffer[128];
@@ -6909,12 +7089,120 @@
                pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error ");
                pos = safecatd(buffer, sizeof buffer, pos, err, 6);
                pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
-               pos = safecatd(buffer, sizeof buffer, pos,
-                  display->pm->limit, 6);
+               pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
                png_error(pp, buffer);
             }
          }
       }
+#  else  /* DIGITIZE */
+      {
+         double r = that->redf;
+         double re = that->rede;
+         double g = that->greenf;
+         double ge = that->greene;
+         double b = that->bluef;
+         double be = that->bluee;
+
+         /* 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;
+         }
+
+         else 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.)
+             */
+            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;
+         }
+
+         else
+         {
+            /* The calculation happens in linear space, and this produces much
+             * wider errors in the encoded space.  These are handled here by
+             * factoring the errors in to the calculation.  There are two table
+             * lookups in the calculation and each introduces a quantization
+             * error defined by the table size.
+             */
+            PNG_CONST png_modifier *pm = display->pm;
+            double in_qe = (that->sample_depth > 8 ? .5/65535 : .5/255);
+            double out_qe = (that->sample_depth > 8 ? .5/65535 :
+               (pm->assume_16_bit_calculations ? .5/(1<<PNG_MAX_GAMMA_8) :
+               .5/255));
+            double rhi, ghi, bhi, grayhi;
+            double g1 = 1/data.gamma;
+
+            rhi = r + re + in_qe; if (rhi > 1) rhi = 1;
+            r -= re + in_qe; if (r < 0) r = 0;
+            ghi = g + ge + in_qe; if (ghi > 1) ghi = 1;
+            g -= ge + in_qe; if (g < 0) g = 0;
+            bhi = b + be + in_qe; if (bhi > 1) bhi = 1;
+            b -= be + in_qe; if (b < 0) b = 0;
+
+            r = pow(r, g1)*(1-DBL_EPSILON); rhi = pow(rhi, g1)*(1+DBL_EPSILON);
+            g = pow(g, g1)*(1-DBL_EPSILON); ghi = pow(ghi, g1)*(1+DBL_EPSILON);
+            b = pow(b, g1)*(1-DBL_EPSILON); bhi = pow(bhi, g1)*(1+DBL_EPSILON);
+
+            /* Work out the lower and upper bounds for the gray value in the
+             * encoded space, then work out an average and error.  Remove the
+             * previously added input quantization error at this point.
+             */
+            gray = r * data.red_coefficient + g * data.green_coefficient +
+               b * data.blue_coefficient - 1./32768 - out_qe;
+            if (gray <= 0)
+               gray = 0;
+            else
+            {
+               gray *= (1 - 6 * DBL_EPSILON);
+               gray = pow(gray, data.gamma) * (1-DBL_EPSILON);
+            }
+
+            grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient +
+               bhi * data.blue_coefficient + 1./32768 + out_qe;
+            grayhi *= (1 + 6 * DBL_EPSILON);
+            if (grayhi >= 1)
+               grayhi = 1;
+            else
+               grayhi = pow(grayhi, data.gamma) * (1+DBL_EPSILON);
+
+            err = (grayhi - gray) / 2;
+            gray = (grayhi + gray) / 2;
+
+            if (err <= in_qe)
+               err = gray * DBL_EPSILON;
+
+            else
+               err -= in_qe;
+
+            /* Validate that the error is within limits (this has caused
+             * problems before, it's much easier to detect them here.)
+             */
+            if (err > pm->limit)
+            {
+               size_t pos = 0;
+               char buffer[128];
+
+               pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error ");
+               pos = safecatd(buffer, sizeof buffer, pos, err, 6);
+               pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
+               pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
+               png_error(pp, buffer);
+            }
+         }
+      }
+#  endif /* !DIGITIZE */
 
       that->bluef = that->greenf = that->redf = gray;
       that->bluee = that->greene = that->rede = err;
@@ -6963,7 +7251,7 @@
  *    int background_gamma_code, int need_expand,
  *    png_fixed_point background_gamma)
  *
- * As with rgb_to_gray this ignores the gamma (at present.)
+ * This ignores the gamma (at present.)
 */
 #define data ITDATA(background)
 static image_pixel data;
@@ -6974,6 +7262,7 @@
 {
    png_byte colour_type, bit_depth;
    png_byte random_bytes[8]; /* 8 bytes - 64 bits - the biggest pixel */
+   int expand;
    png_color_16 back;
 
    /* We need a background colour, because we don't know exactly what transforms
@@ -6991,10 +7280,14 @@
    {
       colour_type = PNG_COLOR_TYPE_RGB;
       bit_depth = 8;
+      expand = 0; /* passing in an RGB not a pixel index */
    }
 
    else
+   {
       bit_depth = that->this.bit_depth;
+      expand = 1;
+   }
 
    image_pixel_init(&data, random_bytes, colour_type,
       bit_depth, 0/*x*/, 0/*unused: palette*/);
@@ -7015,11 +7308,9 @@
       back.gray = (png_uint_16)data.red;
 
 #  ifdef PNG_FLOATING_POINT_SUPPORTED
-      png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, 1/*need expand*/,
-         0);
+      png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
 #  else
-      png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE,
-         1/*need expand*/, 0);
+      png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
 #  endif
 
    this->next->set(this->next, that, pp, pi);
@@ -7122,7 +7413,7 @@
    {
       fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n",
          name);
-      exit(1);
+      exit(99);
    }
 }
 
@@ -7144,7 +7435,7 @@
 
    fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n",
       name);
-   exit(1);
+   exit(99);
 }
 
 static void
@@ -7351,7 +7642,7 @@
    png_byte bit_depth = 0;
    unsigned int palette_number = 0;
 
-   while (next_format(&colour_type, &bit_depth, &palette_number))
+   while (next_format(&colour_type, &bit_depth, &palette_number, 0))
    {
       png_uint_32 counter = 0;
       size_t base_pos;
@@ -7569,7 +7860,7 @@
    standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
 }
 
-static void
+static void PNGCBAPI
 gamma_info(png_structp pp, png_infop pi)
 {
    gamma_info_imp(voidcast(gamma_display*, png_get_progressive_ptr(pp)), pp,
@@ -8036,14 +8327,25 @@
           * passed.  Don't do these additional tests here - just log the
           * original [es_lo..es_hi] values.
           */
-         if (pass == 0 && vi->use_input_precision)
+         if (pass == 0 && vi->use_input_precision && vi->dp->sbit)
          {
             /* Ok, something is wrong - this actually happens in current libpng
              * 16-to-8 processing.  Assume that the input value (id, adjusted
              * for sbit) can be anywhere between value-.5 and value+.5 - quite a
              * large range if sbit is low.
+             *
+             * NOTE: at present because the libpng gamma table stuff has been
+             * changed to use a rounding algorithm to correct errors in 8-bit
+             * calculations the precise sbit calculation (a shift) has been
+             * lost.  This can result in up to a +/-1 error in the presence of
+             * an sbit less than the bit depth.
              */
-            double tmp = (isbit - .5)/sbit_max;
+#           if PNG_LIBPNG_VER < 10700
+#              define SBIT_ERROR .5
+#           else
+#              define SBIT_ERROR 1.
+#           endif
+            double tmp = (isbit - SBIT_ERROR)/sbit_max;
 
             if (tmp <= 0)
                tmp = 0;
@@ -8062,10 +8364,10 @@
             if (is_lo < 0)
                is_lo = 0;
 
-            tmp = (isbit + .5)/sbit_max;
+            tmp = (isbit + SBIT_ERROR)/sbit_max;
 
-            if (tmp <= 0)
-               tmp = 0;
+            if (tmp >= 1)
+               tmp = 1;
 
             else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
                tmp = pow(tmp, vi->file_inverse);
@@ -8382,7 +8684,7 @@
     * Because there is limited precision in the input it is arguable that
     * an acceptable result is any valid result from input-.5 to input+.5.
     * The basic tests below do not do this, however if 'use_input_precision'
-    * is set a subsequent test is performed below.
+    * is set a subsequent test is performed above.
     */
    PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
    int processing;
@@ -8522,7 +8824,7 @@
    dp->this.ps->validated = 1;
 }
 
-static void
+static void PNGCBAPI
 gamma_end(png_structp ppIn, png_infop pi)
 {
    png_const_structp pp = ppIn;
@@ -8692,7 +8994,7 @@
    }
 
    Catch(fault)
-      modifier_reset((png_modifier*)fault);
+      modifier_reset(voidcast(png_modifier*,(void*)fault));
 }
 
 static void gamma_threshold_test(png_modifier *pm, png_byte colour_type,
@@ -8724,7 +9026,7 @@
     * fact this test is somewhat excessive since libpng doesn't make this
     * decision based on colour type or bit depth!
     */
-   while (next_format(&colour_type, &bit_depth, &palette_number))
+   while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
       if (palette_number == 0)
    {
       double test_gamma = 1.0;
@@ -8785,7 +9087,7 @@
    png_byte bit_depth = 0;
    unsigned int palette_number = 0;
 
-   while (next_format(&colour_type, &bit_depth, &palette_number))
+   while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
    {
       unsigned int i, j;
 
@@ -8815,7 +9117,7 @@
       png_byte colour_type = 0, bit_depth = 0;
       unsigned int npalette = 0;
 
-      while (next_format(&colour_type, &bit_depth, &npalette))
+      while (next_format(&colour_type, &bit_depth, &npalette, 1/*gamma*/))
          if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
             ((colour_type == 3 && sbit < 8) ||
             (colour_type != 3 && sbit < bit_depth)))
@@ -8850,6 +9152,7 @@
 #  ifndef PNG_MAX_GAMMA_8
 #     define PNG_MAX_GAMMA_8 11
 #  endif
+#  define SBIT_16_TO_8 PNG_MAX_GAMMA_8
    /* 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.
@@ -8867,28 +9170,28 @@
              fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD)
          {
             gamma_transform_test(pm, 0, 16, 0, pm->interlace_type,
-               1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
                pm->use_input_precision_16to8, 1 /*scale16*/);
 
             if (fail(pm))
                return;
 
             gamma_transform_test(pm, 2, 16, 0, pm->interlace_type,
-               1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
                pm->use_input_precision_16to8, 1 /*scale16*/);
 
             if (fail(pm))
                return;
 
             gamma_transform_test(pm, 4, 16, 0, pm->interlace_type,
-               1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
                pm->use_input_precision_16to8, 1 /*scale16*/);
 
             if (fail(pm))
                return;
 
             gamma_transform_test(pm, 6, 16, 0, pm->interlace_type,
-               1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+               1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
                pm->use_input_precision_16to8, 1 /*scale16*/);
 
             if (fail(pm))
@@ -8970,6 +9273,28 @@
       r = random_32();
       background.blue = (png_uint_16)r;
       background.gray = (png_uint_16)(r >> 16);
+
+      /* In earlier libpng versions, those where DIGITIZE is set, any background
+       * gamma correction in the expand16 case was done using 8-bit gamma
+       * correction tables, resulting in larger errors.  To cope with those
+       * cases use a 16-bit background value which will handle this gamma
+       * correction.
+       */
+#     if DIGITIZE
+         if (expand_16 && (do_background == PNG_BACKGROUND_GAMMA_UNIQUE ||
+                           do_background == PNG_BACKGROUND_GAMMA_FILE) &&
+            fabs(bg*screen_gamma-1) > PNG_GAMMA_THRESHOLD)
+         {
+            /* The background values will be looked up in an 8-bit table to do
+             * the gamma correction, so only select values which are an exact
+             * match for the 8-bit table entries:
+             */
+            background.red = (png_uint_16)((background.red >> 8) * 257);
+            background.green = (png_uint_16)((background.green >> 8) * 257);
+            background.blue = (png_uint_16)((background.blue >> 8) * 257);
+            background.gray = (png_uint_16)((background.gray >> 8) * 257);
+         }
+#     endif
    }
 
    else /* 8 bit colors */
@@ -9034,7 +9359,7 @@
    /* Skip the non-alpha cases - there is no setting of a transparency colour at
     * present.
     */
-   while (next_format(&colour_type, &bit_depth, &palette_number))
+   while (next_format(&colour_type, &bit_depth, &palette_number, 1/*gamma*/))
       if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
    {
       unsigned int i, j;
@@ -9056,31 +9381,46 @@
 static void
 init_gamma_errors(png_modifier *pm)
 {
-   pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
-   pm->error_color_8 = 0;
-   pm->error_indexed = 0;
-   pm->error_gray_16 = pm->error_color_16 = 0;
+   /* Use -1 to catch tests that were not actually run */
+   pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = -1.;
+   pm->error_color_8 = -1.;
+   pm->error_indexed = -1.;
+   pm->error_gray_16 = pm->error_color_16 = -1.;
 }
 
 static void
-summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth)
+print_one(const char *leader, double err)
 {
+   if (err != -1.)
+      printf(" %s %.5f\n", leader, err);
+}
+
+static void
+summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth,
+   int indexed)
+{
+   fflush(stderr);
+
    if (who)
-      printf("Gamma correction with %s:\n", who);
+      printf("\nGamma correction with %s:\n", who);
+
+   else
+      printf("\nBasic gamma correction:\n");
 
    if (low_bit_depth)
    {
-      printf("  2 bit gray:  %.5f\n", pm->error_gray_2);
-      printf("  4 bit gray:  %.5f\n", pm->error_gray_4);
-      printf("  8 bit gray:  %.5f\n", pm->error_gray_8);
-      printf("  8 bit color: %.5f\n", pm->error_color_8);
-      printf("  indexed:     %.5f\n", pm->error_indexed);
+      print_one(" 2 bit gray: ", pm->error_gray_2);
+      print_one(" 4 bit gray: ", pm->error_gray_4);
+      print_one(" 8 bit gray: ", pm->error_gray_8);
+      print_one(" 8 bit color:", pm->error_color_8);
+      if (indexed)
+         print_one(" indexed:    ", pm->error_indexed);
    }
 
-#ifdef DO_16BIT
-   printf(" 16 bit gray:  %.5f\n", pm->error_gray_16);
-   printf(" 16 bit color: %.5f\n", pm->error_color_16);
-#endif
+   print_one("16 bit gray: ", pm->error_gray_16);
+   print_one("16 bit color:", pm->error_color_16);
+
+   fflush(stdout);
 }
 
 static void
@@ -9106,18 +9446,9 @@
    /* Now some real transforms. */
    if (pm->test_gamma_transform)
    {
-      init_gamma_errors(pm);
-      /*TODO: remove this.  Necessary because the current libpng
-       * implementation works in 8 bits:
-       */
-      if (pm->test_gamma_expand16)
-         pm->calculations_use_input_precision = 1;
-      perform_gamma_transform_tests(pm);
-      if (!calculations_use_input_precision)
-         pm->calculations_use_input_precision = 0;
-
       if (summary)
       {
+         fflush(stderr);
          printf("Gamma correction error summary\n\n");
          printf("The printed value is the maximum error in the pixel values\n");
          printf("calculated by the libpng gamma correction code.  The error\n");
@@ -9129,10 +9460,25 @@
          printf("less than 1 for formats with fewer than 8 bits and a small\n");
          printf("number (typically less than 5) for the 16 bit formats.\n");
          printf("For performance reasons the value for 16 bit formats\n");
-         printf("increases when the image file includes an sBIT chunk.\n\n");
-
-         summarize_gamma_errors(pm, 0/*who*/, 1);
+         printf("increases when the image file includes an sBIT chunk.\n");
+         fflush(stdout);
       }
+
+      init_gamma_errors(pm);
+      /*TODO: remove this.  Necessary because the current libpng
+       * implementation works in 8 bits:
+       */
+      if (pm->test_gamma_expand16)
+         pm->calculations_use_input_precision = 1;
+      perform_gamma_transform_tests(pm);
+      if (!calculations_use_input_precision)
+         pm->calculations_use_input_precision = 0;
+
+      if (summary)
+         summarize_gamma_errors(pm, 0/*who*/, 1/*low bit depth*/, 1/*indexed*/);
+
+      if (fail(pm))
+         return;
    }
 
    /* The sbit tests produce much larger errors: */
@@ -9142,7 +9488,10 @@
       perform_gamma_sbit_tests(pm);
 
       if (summary)
-         summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U);
+         summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U, 1/*indexed*/);
+
+      if (fail(pm))
+         return;
    }
 
 #ifdef DO_16BIT /* Should be READ_16BIT_SUPPORTED */
@@ -9154,10 +9503,15 @@
 
       if (summary)
       {
-         printf("Gamma correction with 16 to 8 bit reduction:\n");
+         fflush(stderr);
+         printf("\nGamma correction with 16 to 8 bit reduction:\n");
          printf(" 16 bit gray:  %.5f\n", pm->error_gray_16);
          printf(" 16 bit color: %.5f\n", pm->error_color_16);
+         fflush(stdout);
       }
+
+      if (fail(pm))
+         return;
    }
 #endif
 
@@ -9181,7 +9535,10 @@
       pm->maxout8 = maxout8;
 
       if (summary)
-         summarize_gamma_errors(pm, "background", 1);
+         summarize_gamma_errors(pm, "background", 1, 0/*indexed*/);
+
+      if (fail(pm))
+         return;
    }
 #endif
 
@@ -9206,7 +9563,10 @@
          pm->calculations_use_input_precision = 0;
 
       if (summary)
-         summarize_gamma_errors(pm, "alpha mode", 1);
+         summarize_gamma_errors(pm, "alpha mode", 1, 0/*indexed*/);
+
+      if (fail(pm))
+         return;
    }
 #endif
 }
@@ -9448,7 +9808,7 @@
       if (m != f)
       {
          fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
-         exit(1);
+         exit(99);
       }
 
       m = PNG_PASS_START_COL(pass);
@@ -9456,7 +9816,7 @@
       if (m != f)
       {
          fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
-         exit(1);
+         exit(99);
       }
 
       m = PNG_PASS_ROW_SHIFT(pass);
@@ -9464,7 +9824,7 @@
       if (m != f)
       {
          fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
-         exit(1);
+         exit(99);
       }
 
       m = PNG_PASS_COL_SHIFT(pass);
@@ -9472,7 +9832,7 @@
       if (m != f)
       {
          fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
-         exit(1);
+         exit(99);
       }
 
       /* Macros that depend on the image or sub-image height too:
@@ -9493,7 +9853,7 @@
          {
             fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
                v, pass, m, f);
-            exit(1);
+            exit(99);
          }
 
          m = PNG_COL_FROM_PASS_COL(v, pass);
@@ -9502,7 +9862,7 @@
          {
             fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
                v, pass, m, f);
-            exit(1);
+            exit(99);
          }
 
          m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
@@ -9511,7 +9871,7 @@
          {
             fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
                v, pass, m, f);
-            exit(1);
+            exit(99);
          }
 
          m = PNG_COL_IN_INTERLACE_PASS(v, pass);
@@ -9520,7 +9880,7 @@
          {
             fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
                v, pass, m, f);
-            exit(1);
+            exit(99);
          }
 
          /* Then the base 1 stuff: */
@@ -9531,7 +9891,7 @@
          {
             fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
                v, pass, m, f);
-            exit(1);
+            exit(99);
          }
 
          m = PNG_PASS_COLS(v, pass);
@@ -9540,7 +9900,7 @@
          {
             fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
                v, pass, m, f);
-            exit(1);
+            exit(99);
          }
 
          /* Move to the next v - the stepping algorithm starts skipping
@@ -9714,6 +10074,19 @@
    /* Default to error on warning: */
    pm.this.treat_warnings_as_errors = 1;
 
+   /* Default assume_16_bit_calculations appropriately; this tells the checking
+    * code that 16-bit arithmetic is used for 8-bit samples when it would make a
+    * difference.
+    */
+   pm.assume_16_bit_calculations = PNG_LIBPNG_VER >= 10700;
+
+   /* Currently 16 bit expansion happens at the end of the pipeline, so the
+    * calculations are done in the input bit depth not the output.
+    *
+    * TODO: fix this
+    */
+   pm.calculations_use_input_precision = 1U;
+
    /* Store the test gammas */
    pm.gammas = gammas;
    pm.ngammas = (sizeof gammas) / (sizeof gammas[0]);
@@ -9724,13 +10097,16 @@
    pm.nencodings = (sizeof test_encodings) / (sizeof test_encodings[0]);
 
    pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+
    /* 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.
+    * way libpng treates the 16_TO_8 flag when building the gamma tables in
+    * releases up to 1.6.0.
     *
     * TODO: review this
     */
    pm.use_input_precision_16to8 = 1U;
+   pm.use_input_precision_sbit = 1U; /* because libpng now rounds sBIT */
 
    /* Some default values (set the behavior for 'make check' here).
     * These values simply control the maximum error permitted in the gamma
@@ -9741,11 +10117,12 @@
     */
    pm.maxout8 = .1;     /* Arithmetic error in *encoded* value */
    pm.maxabs8 = .00005; /* 1/20000 */
-   pm.maxcalc8 = .004;  /* +/-1 in 8 bits for compose errors */
+   pm.maxcalc8 = 1./255;  /* +/-1 in 8 bits for compose errors */
    pm.maxpc8 = .499;    /* I.e., .499% fractional error */
    pm.maxout16 = .499;  /* Error in *encoded* value */
    pm.maxabs16 = .00005;/* 1/20000 */
-   pm.maxcalc16 =.000015;/* +/-1 in 16 bits for compose errors */
+   pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */
+   pm.maxcalcG = 1./((1<<PNG_MAX_GAMMA_8)-1);
 
    /* 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
@@ -9889,16 +10266,26 @@
          ++pm.use_update_info; /* Can call multiple times */
 
       else if (strcmp(*argv, "--interlace") == 0)
-         pm.interlace_type = PNG_INTERLACE_ADAM7;
+      {
+#        ifdef PNG_WRITE_INTERLACING_SUPPORTED
+            pm.interlace_type = PNG_INTERLACE_ADAM7;
+#        else
+            fprintf(stderr, "pngvalid: no write interlace support\n");
+            return SKIP;
+#        endif
+      }
 
       else if (strcmp(*argv, "--use-input-precision") == 0)
-         pm.use_input_precision = 1;
+         pm.use_input_precision = 1U;
+
+      else if (strcmp(*argv, "--use-calculation-precision") == 0)
+         pm.use_input_precision = 0;
 
       else if (strcmp(*argv, "--calculations-use-input-precision") == 0)
-         pm.calculations_use_input_precision = 1;
+         pm.calculations_use_input_precision = 1U;
 
       else if (strcmp(*argv, "--assume-16-bit-calculations") == 0)
-         pm.assume_16_bit_calculations = 1;
+         pm.assume_16_bit_calculations = 1U;
 
       else if (strcmp(*argv, "--calculations-follow-bit-depth") == 0)
          pm.calculations_use_input_precision =
@@ -9944,7 +10331,7 @@
          else
          {
             fprintf(stderr, "pngvalid: %s: unknown 'max' option\n", *argv);
-            exit(1);
+            exit(99);
          }
 
          catmore = 1;
@@ -9956,10 +10343,53 @@
       else if (strcmp(*argv, "--log16") == 0)
          --argc, pm.log16 = atof(*++argv), catmore = 1;
 
+#ifdef PNG_SET_OPTION_SUPPORTED
+      else if (strncmp(*argv, "--option=", 9) == 0)
+      {
+         /* Syntax of the argument is <option>:{on|off} */
+         const char *arg = 9+*argv;
+         unsigned char option=0, setting=0;
+
+#ifdef PNG_ARM_NEON_API_SUPPORTED
+         if (strncmp(arg, "arm-neon:", 9) == 0)
+            option = PNG_ARM_NEON, arg += 9;
+
+         else
+#endif
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+         if (strncmp(arg, "max-inflate-window:", 19) == 0)
+            option = PNG_MAXIMUM_INFLATE_WINDOW, arg += 19;
+
+         else
+#endif
+         {
+            fprintf(stderr, "pngvalid: %s: %s: unknown option\n", *argv, arg);
+            exit(99);
+         }
+
+         if (strcmp(arg, "off") == 0)
+            setting = PNG_OPTION_OFF;
+
+         else if (strcmp(arg, "on") == 0)
+            setting = PNG_OPTION_ON;
+
+         else
+         {
+            fprintf(stderr,
+               "pngvalid: %s: %s: unknown setting (use 'on' or 'off')\n",
+               *argv, arg);
+            exit(99);
+         }
+
+         pm.this.options[pm.this.noptions].option = option;
+         pm.this.options[pm.this.noptions++].setting = setting;
+      }
+#endif /* PNG_SET_OPTION_SUPPORTED */
+
       else
       {
          fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
-         exit(1);
+         exit(99);
       }
 
       if (catmore) /* consumed an extra *argv */
@@ -10133,13 +10563,17 @@
       }
    }
 
+   /* This is required because some very minimal configurations do not use it:
+    */
+   UNUSED(fail)
    return 0;
 }
-#else /* write not supported */
+#else /* write or low level APIs not supported */
 int main(void)
 {
-   fprintf(stderr, "pngvalid: no write support in libpng, all tests skipped\n");
+   fprintf(stderr,
+      "pngvalid: no low level write support in libpng, all tests skipped\n");
    /* So the test is skipped: */
-   return 77;
+   return SKIP;
 }
 #endif