[devel] Implement expansion to 16 bits
diff --git a/png.h b/png.h
index 5d69f0f..1af4f12 100644
--- a/png.h
+++ b/png.h
@@ -1060,6 +1060,13 @@
 PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structp png_ptr));
 #endif
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16 bit channels, forces conversion of palette to RGB and expansion
+ * of a tRNS chunk if present.
+ */
+PNG_EXPORT(221, void, png_set_expand_16, (png_structp png_ptr));
+#endif
+
 #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
 /* Use blue, green, red order for pixels. */
 PNG_EXPORT(30, void, png_set_bgr, (png_structp png_ptr));
@@ -2271,7 +2278,7 @@
  * scripts/symbols.def as well.
  */
 #ifdef PNG_EXPORT_LAST_ORDINAL
-  PNG_EXPORT_LAST_ORDINAL(220);
+  PNG_EXPORT_LAST_ORDINAL(221);
 #endif
 
 #ifdef __cplusplus
diff --git a/pngpriv.h b/pngpriv.h
index b6f382e..57dff38 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -286,7 +286,7 @@
 #define PNG_QUANTIZE            0x0040
 #define PNG_BACKGROUND          0x0080
 #define PNG_BACKGROUND_EXPAND   0x0100
-                          /*    0x0200 unused */
+#define PNG_EXPAND_16           0x0200     /* Added to libpng 1.5.2 */
 #define PNG_16_TO_8             0x0400
 #define PNG_RGBA                0x0800
 #define PNG_EXPAND              0x1000
@@ -875,6 +875,11 @@
     png_bytep row, png_const_color_16p trans_color));
 #endif
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+PNG_EXTERN void png_do_expand_16 PNGARG((png_row_infop row_info,
+    png_bytep row));
+#endif
+
 /* The following decodes the appropriate chunks, and does error correction,
  * then calls the appropriate callback for the chunk if it is valid.
  */
diff --git a/pngrtran.c b/pngrtran.c
index 0ab3d8f..d3aa72f 100644
--- a/pngrtran.c
+++ b/pngrtran.c
@@ -137,6 +137,7 @@
       return;
 
    png_ptr->transformations |= PNG_16_TO_8;
+   png_ptr->transformations &= ~PNG_EXPAND_16;
 }
 #endif
 
@@ -686,6 +687,25 @@
 }
 #endif /* defined(PNG_READ_EXPAND_SUPPORTED) */
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16 bit channels, expand the tRNS chunk too (because otherwise
+ * it may not work correctly.)
+ */
+void PNGAPI
+png_set_expand_16(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand_16");
+
+   if (png_ptr == NULL)
+      return;
+
+   png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
+   png_ptr->transformations &= ~PNG_16_TO_8;
+
+   png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+}
+#endif
+
 #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
 void PNGAPI
 png_set_gray_to_rgb(png_structp png_ptr)
@@ -1285,6 +1305,14 @@
    }
 #endif
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 &&
+      info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      info_ptr->bit_depth = 16;
+   }
+#endif
+
 #ifdef PNG_READ_BACKGROUND_SUPPORTED
    if (png_ptr->transformations & PNG_BACKGROUND)
    {
@@ -1445,14 +1473,17 @@
              (png_ptr->transformations & PNG_EXPAND_tRNS))
             png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
                 &(png_ptr->trans_color));
-         else
 
+         else
             png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
                 NULL);
       }
    }
 #endif
 
+   /* Delay the 'expand 16' step until later for efficiency - so that the
+    * intermediate steps work with 8 bit data. */
+
 #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
    if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
       (png_ptr->row_info.color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
@@ -1567,6 +1598,16 @@
    }
 #endif /* PNG_READ_QUANTIZE_SUPPORTED */
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   /* Do the expansion now, after all the arithmetic has been done.  Notice
+    * that previous transformations can handle the PNG_EXPAND_16 flag if this
+    * is efficient (particularly true in the case of gamma correction, where
+    * better accuracy results faster!)
+    */
+   if (png_ptr->transformations & PNG_EXPAND_16)
+      png_do_expand_16(&png_ptr->row_info, png_ptr->row_buf + 1);
+#endif
+
 #ifdef PNG_READ_INVERT_SUPPORTED
    if (png_ptr->transformations & PNG_INVERT_MONO)
       png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
@@ -1594,6 +1635,9 @@
 #endif
 
 #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+   /*NOTE: this must be in the wrong place - what happens if BGR is set too?
+    * Need pngvalid to test this combo.
+    */
    /* If gray -> RGB, do so now only if we did not do so above */
    if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
        (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
@@ -4066,6 +4110,37 @@
 }
 #endif
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* If the bit depth is 8 and the colour type is not a palette type expand the
+ * whole row to 16 bits.  Has no effect otherwise.
+ */
+void /* PRIVATE */
+png_do_expand_16(png_row_infop row_info, png_bytep row)
+{
+   if (row_info->bit_depth == 8 &&
+      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      /* The row have a sequence of bytes containing [0..255] and we need
+       * to turn it into another row containing [0..65535], to do this we
+       * calculate:
+       *
+       *  (input / 255) * 65535
+       *
+       *  Which happens to be exactly input * 257 and this can be achieved
+       *  simply by byte replication in place (copying backwards).
+       */
+      png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */
+      png_byte *dp = sp + row_info->rowbytes;  /* destination, end + 1 */
+      while (dp > sp)
+         dp[-2] = dp[-1] = *--sp, dp -= 2;
+
+      row_info->rowbytes *= 2;
+      row_info->bit_depth = 16;
+      row_info->pixel_depth = (png_byte)(row_info->channels * 16);
+   }
+}
+#endif
+
 #ifdef PNG_READ_QUANTIZE_SUPPORTED
 void /* PRIVATE */
 png_do_quantize(png_row_infop row_info, png_bytep row,
diff --git a/pngrutil.c b/pngrutil.c
index 4c3cd53..09b821b 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -3463,6 +3463,24 @@
    }
 #endif
 
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+   if (png_ptr->transformations & PNG_EXPAND_16)
+   {
+#     ifdef PNG_READ_EXPAND_SUPPORTED
+         /* In fact it is an error if it isn't supported, but checking is
+          * the safe way.
+          */
+         if (png_ptr->transformations & PNG_EXPAND)
+         {
+            if (png_ptr->bit_depth < 16)
+               max_pixel_depth *= 2;
+         }
+         else
+#     endif
+         png_ptr->transformations &= ~PNG_EXPAND_16;
+   }
+#endif
+
 #ifdef PNG_READ_FILLER_SUPPORTED
    if (png_ptr->transformations & (PNG_FILLER))
    {
diff --git a/pngvalid.c b/pngvalid.c
index 417d71b..ff78b22 100644
--- a/pngvalid.c
+++ b/pngvalid.c
@@ -4465,6 +4465,48 @@
 }
 
 IT(expand_gray_1_2_4_to_8, expand);
+/* png_set_expand_16 */
+static void
+image_transform_png_set_expand_16_set(PNG_CONST image_transform *this,
+    transform_display *that, png_structp pp, png_infop pi)
+{
+   png_set_expand_16(pp);
+   this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
+    image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
+{
+   /* Expect expand_16 to expand everything to 16 bits as a result of also
+    * causing 'expand' to happen.
+    */
+   if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+      image_pixel_convert_PLTE(that, &display->this);
+
+   if (that->have_tRNS)
+      image_pixel_add_alpha(that, &display->this);
+
+   if (that->bit_depth < 16)
+      that->sample_depth = that->bit_depth = 16;
+
+   this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_16_add(image_transform *this,
+    PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+   UNUSED(colour_type)
+
+   this->next = *that;
+   *that = this;
+
+   /* expand_16 does something unless the bit depth is already 16. */
+   return bit_depth < 16;
+}
+
+IT(expand_16, expand_gray_1_2_4_to_8);
 
 /* png_set_strip_16 */
 static void
@@ -4516,7 +4558,7 @@
    return bit_depth > 8;
 }
 
-IT(strip_16, expand_gray_1_2_4_to_8);
+IT(strip_16, expand_16);
 
 /* png_set_strip_alpha */
 static void
diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa
index 9fe0d60..26afef8 100644
--- a/scripts/pnglibconf.dfa
+++ b/scripts/pnglibconf.dfa
@@ -304,6 +304,7 @@
 = NO_READ_TRANSFORMS READ_TRANSFORMS_NOT_SUPPORTED
 
 option READ_EXPAND requires READ_TRANSFORMS
+option READ_EXPAND_16 requires READ_TRANSFORMS READ_16BIT enables READ_EXPAND
 option READ_SHIFT requires READ_TRANSFORMS
 option READ_PACK requires READ_TRANSFORMS
 option READ_BGR requires READ_TRANSFORMS
diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt
index de1c4d3..142e90e 100644
--- a/scripts/pnglibconf.h.prebuilt
+++ b/scripts/pnglibconf.h.prebuilt
@@ -118,7 +118,6 @@
 #define PNG_WRITE_tEXt_SUPPORTED
 #define PNG_READ_gAMA_SUPPORTED
 #define PNG_READ_pCAL_SUPPORTED
-#define PNG_READ_EXPAND_SUPPORTED
 #define PNG_WRITE_sPLT_SUPPORTED
 #define PNG_READ_SWAP_SUPPORTED
 #define PNG_READ_tIME_SUPPORTED
@@ -155,6 +154,7 @@
 #define PNG_tRNS_SUPPORTED
 #define PNG_WRITE_iTXt_SUPPORTED
 #define PNG_oFFs_SUPPORTED
+#define PNG_READ_EXPAND_16_SUPPORTED
 #define PNG_USER_TRANSFORM_PTR_SUPPORTED
 #define PNG_hIST_SUPPORTED
 #define PNG_iCCP_SUPPORTED
@@ -164,6 +164,7 @@
 #define PNG_pCAL_SUPPORTED
 #define PNG_CHECK_cHRM_SUPPORTED
 #define PNG_tIME_SUPPORTED
+#define PNG_READ_EXPAND_SUPPORTED
 #define PNG_pHYs_SUPPORTED
 #define PNG_READ_iTXt_SUPPORTED
 #define PNG_TEXT_SUPPORTED
diff --git a/scripts/symbols.def b/scripts/symbols.def
index 493dc9a..900e480 100644
--- a/scripts/symbols.def
+++ b/scripts/symbols.def
@@ -226,3 +226,4 @@
  png_get_current_pass_number @218
  png_process_data_pause @219
  png_process_data_skip @220
+ png_set_expand_16 @221