DO NOT MERGE Update libpng to 1.6.20
BUG:23265085
Change-Id: I28912577d10ea5bafd139c4ca1baf3b543bf36a9
diff --git a/contrib/README.txt b/contrib/README.txt
new file mode 100644
index 0000000..97963c6
--- /dev/null
+++ b/contrib/README.txt
@@ -0,0 +1,5 @@
+
+This "contrib" directory contains contributions which are not necessarily under
+the libpng license, although all are open source. They are not part of
+libpng proper and are not used for building the library, although some are used
+for testing the library via "make check".
diff --git a/contrib/arm-neon/README b/contrib/arm-neon/README
new file mode 100644
index 0000000..b4248cf
--- /dev/null
+++ b/contrib/arm-neon/README
@@ -0,0 +1,83 @@
+OPERATING SYSTEM SPECIFIC ARM NEON DETECTION
+--------------------------------------------
+
+Detection of the ability to execute ARM NEON on an ARM processor requires
+operating system support. (The information is not available in user mode.)
+
+HOW TO USE THIS
+---------------
+
+This directory contains C code fragments that can be included in arm/arm_init.c
+by setting the macro PNG_ARM_NEON_FILE to the file name in "" or <> at build
+time. This setting is not recorded in pnglibconf.h and can be changed simply by
+rebuilding arm/arm_init.o with the required macro definition.
+
+For any of this code to be used the ARM NEON code must be enabled and run time
+checks must be supported. I.e.:
+
+#if PNG_ARM_NEON_OPT > 0
+#ifdef PNG_ARM_NEON_CHECK_SUPPORTED
+
+This is done in a 'configure' build by passing configure the argument:
+
+ --enable-arm-neon=check
+
+Apart from the basic Linux implementation in contrib/arm-neon/linux.c this code
+is unsupported. That means that it is not even compiled on a regular basis and
+may be broken in any given minor release.
+
+FILE FORMAT
+-----------
+
+Each file documents its testing status as of the last time it was tested (which
+may have been a long time ago):
+
+STATUS: one of:
+ SUPPORTED: This indicates that the file is included in the regularly
+ performed test builds and bugs are fixed when discovered.
+ COMPILED: This indicates that the code did compile at least once. See the
+ more detailed description for the extent to which the result was
+ successful.
+ TESTED: This means the code was fully compiled into the libpng test programs
+ and these were run at least once.
+
+BUG REPORTS: an email address to which to send reports of problems
+
+The file is a fragment of C code. It should not define any 'extern' symbols;
+everything should be static. It must define the function:
+
+static int png_have_neon(png_structp png_ptr);
+
+That function must return 1 if ARM NEON instructions are supported, 0 if not.
+It must not execute png_error unless it detects a bug. A png_error will prevent
+the reading of the PNG and in the future, writing too.
+
+BUG REPORTS
+-----------
+
+If you mail a bug report for any file that is not SUPPORTED there may only be
+limited response. Consider fixing it and sending a patch to fix the problem -
+this is more likely to result in action.
+
+CONTRIBUTIONS
+-------------
+
+You may send contributions of new implementations to
+png-mng-implement@sourceforge.net. Please write code in strict C90 C where
+possible. Obviously OS dependencies are to be expected. If you submit code you
+must have the authors permission and it must have a license that is acceptable
+to the current maintainer; in particular that license must permit modification
+and redistribution.
+
+Please try to make the contribution a single file and give the file a clear and
+unambiguous name that identifies the target OS. If multiple files really are
+required put them all in a sub-directory.
+
+You must also be prepared to handle bug reports from users of the code, either
+by joining the png-mng-implement mailing list or by providing an email for the
+"BUG REPORTS" entry or both. Please make sure that the header of the file
+contains the STATUS and BUG REPORTS fields as above.
+
+Please list the OS requirements as precisely as possible. Ideally you should
+also list the environment in which the code has been tested and certainly list
+any environments where you suspect it might not work.
diff --git a/contrib/arm-neon/android-ndk.c b/contrib/arm-neon/android-ndk.c
new file mode 100644
index 0000000..7240123
--- /dev/null
+++ b/contrib/arm-neon/android-ndk.c
@@ -0,0 +1,39 @@
+/* contrib/arm-neon/android-ndk.c
+ *
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by John Bowler, 2014.
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/arm-neon/README before reporting bugs
+ *
+ * STATUS: COMPILED, UNTESTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_neon implemented for the Android NDK, see:
+ *
+ * Documentation:
+ * http://www.kandroid.org/ndk/docs/CPU-ARM-NEON.html
+ * http://code.google.com/p/android/issues/detail?id=49065
+ *
+ * NOTE: this requires that libpng is built against the Android NDK and linked
+ * with an implementation of the Android ARM 'cpu-features' library. The code
+ * has been compiled only, not linked: no version of the library has been found,
+ * only the header files exist in the NDK.
+ */
+#include <cpu-features.h>
+
+static int
+png_have_neon(png_structp png_ptr)
+{
+ /* This is a whole lot easier than the linux code, however it is probably
+ * implemented as below, therefore it is better to cache the result (these
+ * function calls may be slow!)
+ */
+ PNG_UNUSED(png_ptr)
+ return android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
+ (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+}
diff --git a/contrib/arm-neon/linux-auxv.c b/contrib/arm-neon/linux-auxv.c
new file mode 100644
index 0000000..4d26bd3
--- /dev/null
+++ b/contrib/arm-neon/linux-auxv.c
@@ -0,0 +1,120 @@
+/* contrib/arm-neon/linux-auxv.c
+ *
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by Mans Rullgard, 2011.
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/arm-neon/README before reporting bugs
+ *
+ * STATUS: COMPILED, TESTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_neon implemented for Linux versions which allow access to
+ * /proc/self/auxv. This is probably faster, cleaner and safer than the code to
+ * read /proc/cpuinfo in contrib/arm-neon/linux, however it is yet another piece
+ * of potentially untested code and has more complex dependencies than the code
+ * to read cpuinfo.
+ *
+ * This generic __linux__ implementation requires reading /proc/self/auxv and
+ * looking at each element for one that records NEON capabilities.
+ */
+#include <unistd.h> /* for POSIX 1003.1 */
+#include <errno.h> /* for EINTR */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <asm/hwcap.h>
+
+/* A read call may be interrupted, in which case it returns -1 and sets errno to
+ * EINTR if nothing was done, otherwise (if something was done) a partial read
+ * may result.
+ */
+static size_t
+safe_read(png_structp png_ptr, int fd, void *buffer_in, size_t nbytes)
+{
+ size_t ntotal = 0;
+ char *buffer = png_voidcast(char*, buffer_in);
+
+ while (nbytes > 0)
+ {
+ unsigned int nread;
+ int iread;
+
+ /* Passing nread > INT_MAX to read is implementation defined in POSIX
+ * 1003.1, therefore despite the unsigned argument portable code must
+ * limit the value to INT_MAX!
+ */
+ if (nbytes > INT_MAX)
+ nread = INT_MAX;
+
+ else
+ nread = (unsigned int)/*SAFE*/nbytes;
+
+ iread = read(fd, buffer, nread);
+
+ if (iread == -1)
+ {
+ /* This is the devil in the details, a read can terminate early with 0
+ * bytes read because of EINTR, yet it still returns -1 otherwise end
+ * of file cannot be distinguished.
+ */
+ if (errno != EINTR)
+ {
+ png_warning(png_ptr, "/proc read failed");
+ return 0; /* I.e., a permanent failure */
+ }
+ }
+
+ else if (iread < 0)
+ {
+ /* Not a valid 'read' result: */
+ png_warning(png_ptr, "OS /proc read bug");
+ return 0;
+ }
+
+ else if (iread > 0)
+ {
+ /* Continue reading until a permanent failure, or EOF */
+ buffer += iread;
+ nbytes -= (unsigned int)/*SAFE*/iread;
+ ntotal += (unsigned int)/*SAFE*/iread;
+ }
+
+ else
+ return ntotal;
+ }
+
+ return ntotal; /* nbytes == 0 */
+}
+
+static int
+png_have_neon(png_structp png_ptr)
+{
+ int fd = open("/proc/self/auxv", O_RDONLY);
+ Elf32_auxv_t aux;
+
+ /* Failsafe: failure to open means no NEON */
+ if (fd == -1)
+ {
+ png_warning(png_ptr, "/proc/self/auxv open failed");
+ return 0;
+ }
+
+ while (safe_read(png_ptr, fd, &aux, sizeof aux) == sizeof aux)
+ {
+ if (aux.a_type == AT_HWCAP && (aux.a_un.a_val & HWCAP_NEON) != 0)
+ {
+ close(fd);
+ return 1;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/contrib/arm-neon/linux.c b/contrib/arm-neon/linux.c
new file mode 100644
index 0000000..94f9bb1
--- /dev/null
+++ b/contrib/arm-neon/linux.c
@@ -0,0 +1,161 @@
+/* contrib/arm-neon/linux.c
+ *
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by John Bowler, 2014.
+ * Last changed in libpng 1.6.16 [December 22, 2014]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * SEE contrib/arm-neon/README before reporting bugs
+ *
+ * STATUS: SUPPORTED
+ * BUG REPORTS: png-mng-implement@sourceforge.net
+ *
+ * png_have_neon implemented for Linux by reading the widely available
+ * pseudo-file /proc/cpuinfo.
+ *
+ * This code is strict ANSI-C and is probably moderately portable; it does
+ * however use <stdio.h> and it assumes that /proc/cpuinfo is never localized.
+ */
+#include <stdio.h>
+
+static int
+png_have_neon(png_structp png_ptr)
+{
+ FILE *f = fopen("/proc/cpuinfo", "rb");
+
+ if (f != NULL)
+ {
+ /* This is a simple state machine which reads the input byte-by-byte until
+ * it gets a match on the 'neon' feature or reaches the end of the stream.
+ */
+ static const char ch_feature[] = { 70, 69, 65, 84, 85, 82, 69, 83 };
+ static const char ch_neon[] = { 78, 69, 79, 78 };
+
+ enum
+ {
+ StartLine, Feature, Colon, StartTag, Neon, HaveNeon, SkipTag, SkipLine
+ } state;
+ int counter;
+
+ for (state=StartLine, counter=0;;)
+ {
+ int ch = fgetc(f);
+
+ if (ch == EOF)
+ {
+ /* EOF means error or end-of-file, return false; neon at EOF is
+ * assumed to be a mistake.
+ */
+ fclose(f);
+ return 0;
+ }
+
+ switch (state)
+ {
+ case StartLine:
+ /* Match spaces at the start of line */
+ if (ch <= 32) /* skip control characters and space */
+ break;
+
+ counter=0;
+ state = Feature;
+ /* FALL THROUGH */
+
+ case Feature:
+ /* Match 'FEATURE', ASCII case insensitive. */
+ if ((ch & ~0x20) == ch_feature[counter])
+ {
+ if (++counter == (sizeof ch_feature))
+ state = Colon;
+ break;
+ }
+
+ /* did not match 'feature' */
+ state = SkipLine;
+ /* FALL THROUGH */
+
+ case SkipLine:
+ skipLine:
+ /* Skip everything until we see linefeed or carriage return */
+ if (ch != 10 && ch != 13)
+ break;
+
+ state = StartLine;
+ break;
+
+ case Colon:
+ /* Match any number of space or tab followed by ':' */
+ if (ch == 32 || ch == 9)
+ break;
+
+ if (ch == 58) /* i.e. ':' */
+ {
+ state = StartTag;
+ break;
+ }
+
+ /* Either a bad line format or a 'feature' prefix followed by
+ * other characters.
+ */
+ state = SkipLine;
+ goto skipLine;
+
+ case StartTag:
+ /* Skip space characters before a tag */
+ if (ch == 32 || ch == 9)
+ break;
+
+ state = Neon;
+ counter = 0;
+ /* FALL THROUGH */
+
+ case Neon:
+ /* Look for 'neon' tag */
+ if ((ch & ~0x20) == ch_neon[counter])
+ {
+ if (++counter == (sizeof ch_neon))
+ state = HaveNeon;
+ break;
+ }
+
+ state = SkipTag;
+ /* FALL THROUGH */
+
+ case SkipTag:
+ /* Skip non-space characters */
+ if (ch == 10 || ch == 13)
+ state = StartLine;
+
+ else if (ch == 32 || ch == 9)
+ state = StartTag;
+ break;
+
+ case HaveNeon:
+ /* Have seen a 'neon' prefix, but there must be a space or new
+ * line character to terminate it.
+ */
+ if (ch == 10 || ch == 13 || ch == 32 || ch == 9)
+ {
+ fclose(f);
+ return 1;
+ }
+
+ state = SkipTag;
+ break;
+
+ default:
+ png_error(png_ptr, "png_have_neon: internal error (bug)");
+ }
+ }
+ }
+
+#ifdef PNG_WARNINGS_SUPPORTED
+ else
+ png_warning(png_ptr, "/proc/cpuinfo open failed");
+#endif
+
+ return 0;
+}
diff --git a/contrib/conftest/README b/contrib/conftest/README
new file mode 100644
index 0000000..0f47279
--- /dev/null
+++ b/contrib/conftest/README
@@ -0,0 +1,49 @@
+This directory contains test configuration files, currently always '.dfa' files
+intended to be used in the build by setting the make macro DFA_XTRA to the name
+of the file.
+
+These files are used in release validation of the 'configure' builds of libpng
+by building 'make check', or 'make all-am' for cross-builds, with each .dfa
+file.
+
+The files in this directory may change between minor releases, however
+contributions describing specific builds of libpng are welcomed. There is no
+guarantee that libpng will continue to build with such configurations; support
+for given configurations can be, and has been, dropped between successive minor
+releases. However if a .dfa file describing a configuration is not in this
+directory it is very unlikely that it will be tested before a minor release!
+
+You can use these .dfa files as the basis of new configurations. Files in this
+directory should not have any use restrictions or restrictive licenses.
+
+This directory is not included in the .zip and .7z distributions, which do
+not contain 'configure' scripts.
+
+DOCUMENTATION
+=============
+
+Examples:
+ ${srcdir}/pngusr.dfa
+ ${srcdir}/contrib/pngminim/*/pngusr.dfa
+
+Documentation of the options:
+ ${srcdir}/scripts/pnglibconf.dfa
+
+Documentation of the file format:
+ ${srcdir}/scripts/options.awk
+
+FILE NAMING
+===========
+
+File names in this directory may NOT contain any of the five characters:
+
+ - , + * ?
+
+Neither may they contain any space character.
+
+While other characters may be used it is strongly suggested that file names be
+limited to lower case Latiin alphabetic characters (a-z), digits (0-9) and, if
+necessary the underscore (_) character. File names should be about 8 characters
+long (excluding the .dfa extension). Submitted .dfa files should have names
+between 7 and 16 characters long, shorter names (6 characters or less) are
+reserved for standard tests.
diff --git a/contrib/conftest/read.dfa b/contrib/conftest/read.dfa
new file mode 100644
index 0000000..21e88d0
--- /dev/null
+++ b/contrib/conftest/read.dfa
@@ -0,0 +1,58 @@
+# read.dfa
+# Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work. This work is published from:
+# United States.
+#
+# Build libpng with basic read support. This enables the lowest level libpng
+# read API - the one where the calling code has to use a loop to read each row.
+# At present this is the API used by most programs.
+#
+# Support is enabled only for those chunks and transformations that are
+# typically required - others can be added easily.
+#
+
+everything = off
+
+# The sequential read code is enabled here; the progressive code can be used
+# instead but there is no point enabling both.
+
+option SEQUENTIAL_READ on
+
+# Likewise it is pointless enabling both fixed and floating point APIs. Choose
+# one or the other for both the API and the internal math.
+
+#Fixed point:
+#option FIXED_POINT on
+#option FLOATING_ARITHMETIC off
+
+#Floating point:
+option FLOATING_POINT on
+option FLOATING_ARITHMETIC on
+
+# Basic error handling, IO and user memory support. The latter allows the
+# application program to provide its own implementations of 'malloc' and 'free'.
+option SETJMP on
+option STDIO on
+option USER_MEM on
+
+# To read the full set of PNG images correctly interlace, transparency and
+# 16-bit support is required. The application can implement interlace itself,
+# but very few do and it's no longer possible to disable it when READ is
+# enabled.
+option READ_tRNS on
+option READ_16BIT on
+
+# Everything else is application dependent. This file assumes the app handles
+# all the native PNG bit layouts, so it doesn't need any of layout change
+# transforms, but needs libpng to perform gamma correction. It doesn't do any
+# colorspace stuff and ignores the 'significant bit' information.
+#
+# If your app always expands the image to a limited set of bit layouts you
+# probably want to consider using the simplified API instead of the low level
+# one - see png.h and s_read.dfa.
+option READ_GAMMA on
diff --git a/contrib/conftest/s_read.dfa b/contrib/conftest/s_read.dfa
new file mode 100644
index 0000000..cb1ce0b
--- /dev/null
+++ b/contrib/conftest/s_read.dfa
@@ -0,0 +1,35 @@
+# s_read.dfa
+# Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work. This work is published from:
+# United States.
+#
+# Build libpng with simplified read support (only). This builds a minimal
+# libpng able to read all PNG formats and convert them into a small number of
+# well understood memory formats.
+#
+
+everything = off
+
+option SIMPLIFIED_READ on
+
+# It isn't necessary to chose fixed or floating point for the APIs because the
+# simplified API doesn't need fixed or floating point numbers. It is necessary
+# to chose an internal math implementation. The default (because of 'everything
+# = off') is fixed point - turn the floating point implementation on if you have
+# hardware floating point or prefer your software floating point implementation.
+option FLOATING_ARITHMETIC on
+
+# This is not strictly necessary, but without it the message strings in the API
+# will not be filled in
+option ERROR_TEXT on
+
+# Switching these options on enables the 'AFIRST' and 'BGR' formats - you don't
+# need this if you don't use them, they just allow the in-memory layout to be
+# changed to match common hardware formats.
+option SIMPLIFIED_READ_AFIRST on
+option SIMPLIFIED_READ_BGR on
diff --git a/contrib/conftest/s_write.dfa b/contrib/conftest/s_write.dfa
new file mode 100644
index 0000000..e540a46
--- /dev/null
+++ b/contrib/conftest/s_write.dfa
@@ -0,0 +1,33 @@
+# s_write.dfa
+# Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work. This work is published from:
+# United States.
+#
+# Build libpng with (just) simplified write support
+#
+
+everything = off
+
+option SIMPLIFIED_WRITE on
+
+# It isn't necessary to chose fixed or floating point for the APIs because the
+# simplified API doesn't need fixed or floating point numbers. It is necessary
+# to chose an internal math implementation. The default (because of 'everything
+# = off') is fixed point - turn the floating point implementation on if you have
+# hardware floating point or prefer your software floating point implementation.
+option FLOATING_ARITHMETIC on
+
+# This is not strictly necessary, but without it the message strings in the API
+# will not be filled in
+option ERROR_TEXT on
+
+# Switching these options on enables the 'AFIRST' and 'BGR' formats - you don't
+# need this if you don't use them, they just allow the in-memory layout to be
+# changed to match common hardware formats.
+option SIMPLIFIED_WRITE_AFIRST on
+option SIMPLIFIED_WRITE_BGR on
diff --git a/contrib/conftest/simple.dfa b/contrib/conftest/simple.dfa
new file mode 100644
index 0000000..0419333
--- /dev/null
+++ b/contrib/conftest/simple.dfa
@@ -0,0 +1,36 @@
+# simple.dfa
+# Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work. This work is published from:
+# United States.
+#
+# Build libpng with just the simplified APIs (read and write).
+#
+
+everything = off
+
+option SIMPLIFIED_WRITE on
+option SIMPLIFIED_READ on
+
+# It isn't necessary to chose fixed or floating point for the APIs because the
+# simplified API doesn't need fixed or floating point numbers. It is necessary
+# to chose an internal math implementation. The default (because of 'everything
+# = off') is fixed point - turn the floating point implementation on if you have
+# hardware floating point or prefer your software floating point implementation.
+option FLOATING_ARITHMETIC on
+
+# This is not strictly necessary, but without it the message strings in the API
+# will not be filled in
+option ERROR_TEXT on
+
+# Switching these options on enables the 'AFIRST' and 'BGR' formats - you don't
+# need this if you don't use them, they just allow the in-memory layout to be
+# changed to match common hardware formats.
+option SIMPLIFIED_READ_AFIRST on
+option SIMPLIFIED_READ_BGR on
+option SIMPLIFIED_WRITE_AFIRST on
+option SIMPLIFIED_WRITE_BGR on
diff --git a/contrib/conftest/write.dfa b/contrib/conftest/write.dfa
new file mode 100644
index 0000000..3319aab
--- /dev/null
+++ b/contrib/conftest/write.dfa
@@ -0,0 +1,45 @@
+# write.dfa
+# Build time configuration of libpng
+#
+# Author: John Bowler
+# Copyright: (c) John Bowler, 2013
+# Usage rights:
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work. This work is published from:
+# United States.
+#
+# Build libpng with no read support and minimal write support.
+#
+
+everything = off
+
+# Switch on the write code - this makes a minimalist encoder
+
+option WRITE on
+
+# Choose fixed or floating point APIs and arithmetic. The choices are
+# independent but normally they will match. It is typically better to use the
+# floating point if you have floating point hardware. If you don't know, or
+# (perhaps) to make libpng smaller used fixed point throughout.
+
+#Fixed point:
+#option FIXED_POINT on
+#option FLOATING_ARITHMETIC off
+
+#Floating point:
+option FLOATING_POINT on
+option FLOATING_ARITHMETIC on
+
+# Basic error handling, IO and user memory support. The latter allows the
+# application program to provide its own implementations of 'malloc' and 'free'.
+option SETJMP on
+option STDIO on
+option USER_MEM on
+
+# Everything else is optional. Unlike the read code in libpng the write code
+# does not need to deal with arbitrary formats, so only add support for things
+# you really do write! For example you might only write sRGB images, sometimes
+# with transparency and never write 16 bit images, so:
+option WRITE_sRGB on
+option WRITE_tRNS on
+#option WRITE_16BIT off (this is the default with 'everything = off')
diff --git a/contrib/examples/README.txt b/contrib/examples/README.txt
new file mode 100644
index 0000000..0525c9d
--- /dev/null
+++ b/contrib/examples/README.txt
@@ -0,0 +1,24 @@
+
+This directory (contrib/examples) contains examples of libpng usage.
+
+NO COPYRIGHT RIGHTS ARE CLAIMED TO ANY OF THE FILES IN THIS DIRECTORY.
+
+To the extent possible under law, the authors have waived all copyright and
+related or neighboring rights to this work. This work is published from:
+United States.
+
+The files may be used freely in any way. The intention is that appropriate
+parts of the files be used in other libpng-using programs without any need for
+the authors of the using code to seek copyright or license from the original
+authors.
+
+The source code and comments in this directory are the original work of the
+people named below. No other person or organization has made contributions to
+the work in this directory.
+
+ORIGINAL AUTHORS
+ The following people have contributed to the code in this directory. None
+ of the people below claim any rights with regard to the contents of this
+ directory.
+
+ John Bowler <jbowler@acm.org>
diff --git a/contrib/examples/iccfrompng.c b/contrib/examples/iccfrompng.c
new file mode 100644
index 0000000..603037e
--- /dev/null
+++ b/contrib/examples/iccfrompng.c
@@ -0,0 +1,185 @@
+/*- iccfrompng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Extract any icc profiles found in the given PNG files. This is a simple
+ * example of a program that extracts information from the header of a PNG file
+ * without processing the image. Notice that some header information may occur
+ * after the image data. Textual data and comments are an example; the approach
+ * in this file won't work reliably for such data because it only looks for the
+ * information in the section of the file that preceeds the image data.
+ *
+ * Compile and link against libpng and zlib, plus anything else required on the
+ * system you use.
+ *
+ * To use supply a list of PNG files containing iCCP chunks, the chunks will be
+ * extracted to a similarly named file with the extension replaced by 'icc',
+ * which will be overwritten without warning.
+ */
+#include <stdlib.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <png.h>
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) && \
+ defined (PNG_iCCP_SUPPORTED)
+
+
+static int verbose = 1;
+static png_byte no_profile[] = "no profile";
+
+static png_bytep
+extract(FILE *fp, png_uint_32 *proflen)
+{
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ png_infop info_ptr = NULL;
+ png_bytep result = NULL;
+
+ /* Initialize for error or no profile: */
+ *proflen = 0;
+
+ if (png_ptr == NULL)
+ {
+ fprintf(stderr, "iccfrompng: version library mismatch?\n");
+ return 0;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 0;
+ }
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ png_read_info(png_ptr, info_ptr);
+
+ {
+ png_charp name;
+ int compression_type;
+ png_bytep profile;
+
+ if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile,
+ proflen) & PNG_INFO_iCCP)
+ {
+ result = malloc(*proflen);
+ if (result != NULL)
+ memcpy(result, profile, *proflen);
+
+ else
+ png_error(png_ptr, "OOM allocating profile buffer");
+ }
+
+ else
+ result = no_profile;
+ }
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return result;
+}
+
+static int
+extract_one_file(const char *filename)
+{
+ int result = 0;
+ FILE *fp = fopen(filename, "rb");
+
+ if (fp != NULL)
+ {
+ png_uint_32 proflen = 0;
+ png_bytep profile = extract(fp, &proflen);
+
+ if (profile != NULL && profile != no_profile)
+ {
+ size_t len;
+ char *output;
+
+ {
+ const char *ep = strrchr(filename, '.');
+
+ if (ep != NULL)
+ len = ep-filename;
+
+ else
+ len = strlen(filename);
+ }
+
+ output = malloc(len + 5);
+ if (output != NULL)
+ {
+ FILE *of;
+
+ memcpy(output, filename, len);
+ strcpy(output+len, ".icc");
+
+ of = fopen(output, "wb");
+ if (of != NULL)
+ {
+ if (fwrite(profile, proflen, 1, of) == 1 &&
+ fflush(of) == 0 &&
+ fclose(of) == 0)
+ {
+ if (verbose)
+ printf("%s -> %s\n", filename, output);
+ /* Success return */
+ result = 1;
+ }
+
+ else
+ {
+ fprintf(stderr, "%s: error writing profile\n", output);
+ if (remove(output))
+ fprintf(stderr, "%s: could not remove file\n", output);
+ }
+ }
+
+ else
+ fprintf(stderr, "%s: failed to open output file\n", output);
+
+ free(output);
+ }
+
+ else
+ fprintf(stderr, "%s: OOM allocating string!\n", filename);
+
+ free(profile);
+ }
+
+ else if (verbose && profile == no_profile)
+ printf("%s has no profile\n", filename);
+ }
+
+ else
+ fprintf(stderr, "%s: could not open file\n", filename);
+
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ int extracted = 0;
+
+ for (i=1; i<argc; ++i)
+ {
+ if (strcmp(argv[i], "-q") == 0)
+ verbose = 0;
+
+ else if (extract_one_file(argv[i]))
+ extracted = 1;
+ }
+
+ /* Exit code is true if any extract succeeds */
+ return extracted == 0;
+}
+#endif /* READ && STDIO && iCCP */
diff --git a/contrib/examples/pngpixel.c b/contrib/examples/pngpixel.c
new file mode 100644
index 0000000..f762379
--- /dev/null
+++ b/contrib/examples/pngpixel.c
@@ -0,0 +1,371 @@
+/*- pngpixel
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Read a single pixel value from a PNG file.
+ *
+ * This code illustrates basic 'by-row' reading of a PNG file using libpng.
+ * Rows are read until a particular pixel is found; the value of this pixel is
+ * then printed on stdout.
+ *
+ * The code illustrates how to do this on interlaced as well as non-interlaced
+ * images. Normally you would call png_set_interlace_handling() to have libpng
+ * deal with the interlace for you, but that obliges you to buffer half of the
+ * image to assemble the interlaced rows. In this code
+ * png_set_interlace_handling() is not called and, instead, the code handles the
+ * interlace passes directly looking for the required pixel.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h> /* required for error handling */
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)
+
+/* Return component 'c' of pixel 'x' from the given row. */
+static unsigned int
+component(png_const_bytep row, png_uint_32 x, unsigned int c,
+ unsigned int bit_depth, unsigned int channels)
+{
+ /* PNG images can be up to 2^31 pixels wide, but this means they can be up to
+ * 2^37 bits wide (for a 64-bit pixel - the largest possible) and hence 2^34
+ * bytes wide. Since the row fitted into memory, however, the following must
+ * work:
+ */
+ png_uint_32 bit_offset_hi = bit_depth * ((x >> 6) * channels);
+ png_uint_32 bit_offset_lo = bit_depth * ((x & 0x3f) * channels + c);
+
+ row = (png_const_bytep)(((PNG_CONST png_byte (*)[8])row) + bit_offset_hi);
+ row += bit_offset_lo >> 3;
+ bit_offset_lo &= 0x07;
+
+ /* PNG pixels are packed into bytes to put the first pixel in the highest
+ * bits of the byte and into two bytes for 16-bit values with the high 8 bits
+ * first, so:
+ */
+ switch (bit_depth)
+ {
+ case 1: return (row[0] >> (7-bit_offset_lo)) & 0x01;
+ case 2: return (row[0] >> (6-bit_offset_lo)) & 0x03;
+ case 4: return (row[0] >> (4-bit_offset_lo)) & 0x0f;
+ case 8: return row[0];
+ case 16: return (row[0] << 8) + row[1];
+ default:
+ /* This should never happen; it indicates a bug in this program or in
+ * libpng itself:
+ */
+ fprintf(stderr, "pngpixel: invalid bit depth %u\n", bit_depth);
+ exit(1);
+ }
+}
+
+/* Print a pixel from a row returned by libpng; determine the row format, find
+ * the pixel, and print the relevant information to stdout.
+ */
+static void
+print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row,
+ png_uint_32 x)
+{
+ PNG_CONST unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+
+ switch (png_get_color_type(png_ptr, info_ptr))
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ printf("GRAY %u\n", component(row, x, 0, bit_depth, 1));
+ return;
+
+ /* The palette case is slightly more difficult - the palette and, if
+ * present, the tRNS ('transparency', though the values are really
+ * opacity) data must be read to give the full picture:
+ */
+ case PNG_COLOR_TYPE_PALETTE:
+ {
+ PNG_CONST int index = component(row, x, 0, bit_depth, 1);
+ png_colorp palette = NULL;
+ int num_palette = 0;
+
+ if ((png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) &
+ PNG_INFO_PLTE) && num_palette > 0 && palette != NULL)
+ {
+ png_bytep trans_alpha = NULL;
+ int num_trans = 0;
+ if ((png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans,
+ NULL) & PNG_INFO_tRNS) && num_trans > 0 &&
+ trans_alpha != NULL)
+ printf("INDEXED %u = %d %d %d %d\n", index,
+ palette[index].red, palette[index].green,
+ palette[index].blue,
+ index < num_trans ? trans_alpha[index] : 255);
+
+ else /* no transparency */
+ printf("INDEXED %u = %d %d %d\n", index,
+ palette[index].red, palette[index].green,
+ palette[index].blue);
+ }
+
+ else
+ printf("INDEXED %u = invalid index\n", index);
+ }
+ return;
+
+ case PNG_COLOR_TYPE_RGB:
+ printf("RGB %u %u %u\n", component(row, x, 0, bit_depth, 3),
+ component(row, x, 1, bit_depth, 3),
+ component(row, x, 2, bit_depth, 3));
+ return;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ printf("GRAY+ALPHA %u %u\n", component(row, x, 0, bit_depth, 2),
+ component(row, x, 1, bit_depth, 2));
+ return;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ printf("RGBA %u %u %u %u\n", component(row, x, 0, bit_depth, 4),
+ component(row, x, 1, bit_depth, 4),
+ component(row, x, 2, bit_depth, 4),
+ component(row, x, 3, bit_depth, 4));
+ return;
+
+ default:
+ png_error(png_ptr, "pngpixel: invalid color type");
+ }
+}
+
+int main(int argc, const char **argv)
+{
+ /* This program uses the default, <setjmp.h> based, libpng error handling
+ * mechanism, therefore any local variable that exists before the call to
+ * setjmp and is changed after the call to setjmp returns successfully must
+ * be declared with 'volatile' to ensure that their values don't get
+ * destroyed by longjmp:
+ */
+ volatile int result = 1/*fail*/;
+
+ if (argc == 4)
+ {
+ long x = atol(argv[1]);
+ long y = atol(argv[2]);
+ FILE *f = fopen(argv[3], "rb");
+ volatile png_bytep row = NULL;
+
+ if (f != NULL)
+ {
+ /* libpng requires a callback function for handling errors; this
+ * callback must not return. The default callback function uses a
+ * stored <setjmp.h> style jmp_buf which is held in a png_struct and
+ * writes error messages to stderr. Creating the png_struct is a
+ * little tricky; just copy the following code.
+ */
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+
+ if (png_ptr != NULL)
+ {
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+
+ if (info_ptr != NULL)
+ {
+ /* Declare stack variables to hold pointers to locally allocated
+ * data.
+ */
+
+ /* Initialize the error control buffer: */
+ if (setjmp(png_jmpbuf(png_ptr)) == 0)
+ {
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_method,
+ compression_method, filter_method;
+ png_bytep row_tmp;
+
+ /* Now associate the recently opened (FILE*) with the default
+ * libpng initialization functions. Sometimes libpng is
+ * compiled without stdio support (it can be difficult to do
+ * in some environments); in that case you will have to write
+ * your own read callback to read data from the (FILE*).
+ */
+ png_init_io(png_ptr, f);
+
+ /* And read the first part of the PNG file - the header and
+ * all the information up to the first pixel.
+ */
+ png_read_info(png_ptr, info_ptr);
+
+ /* This fills in enough information to tell us the width of
+ * each row in bytes, allocate the appropriate amount of
+ * space. In this case png_malloc is used - it will not
+ * return if memory isn't available.
+ */
+ row = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
+ info_ptr));
+
+ /* To avoid the overhead of using a volatile auto copy row_tmp
+ * to a local here - just use row for the png_free below.
+ */
+ row_tmp = row;
+
+ /* All the information we need is in the header is returned by
+ * png_get_IHDR, if this fails we can now use 'png_error' to
+ * signal the error and return control to the setjmp above.
+ */
+ if (png_get_IHDR(png_ptr, info_ptr, &width, &height,
+ &bit_depth, &color_type, &interlace_method,
+ &compression_method, &filter_method))
+ {
+ int passes, pass;
+
+ /* png_set_interlace_handling returns the number of
+ * passes required as well as turning on libpng's
+ * handling, but since we do it ourselves this is
+ * necessary:
+ */
+ switch (interlace_method)
+ {
+ case PNG_INTERLACE_NONE:
+ passes = 1;
+ break;
+
+ case PNG_INTERLACE_ADAM7:
+ passes = PNG_INTERLACE_ADAM7_PASSES;
+ break;
+
+ default:
+ png_error(png_ptr, "pngpixel: unknown interlace");
+ }
+
+ /* Now read the pixels, pass-by-pass, row-by-row: */
+ png_start_read_image(png_ptr);
+
+ for (pass=0; pass<passes; ++pass)
+ {
+ png_uint_32 ystart, xstart, ystep, xstep;
+ png_uint_32 py;
+
+ if (interlace_method == PNG_INTERLACE_ADAM7)
+ {
+ /* Sometimes the whole pass is empty because the
+ * image is too narrow or too short. libpng
+ * expects to be called for each row that is
+ * present in the pass, so it may be necessary to
+ * skip the loop below (over py) if the image is
+ * too narrow.
+ */
+ if (PNG_PASS_COLS(width, pass) == 0)
+ continue;
+
+ /* We need the starting pixel and the offset
+ * between each pixel in this pass; use the macros
+ * in png.h:
+ */
+ xstart = PNG_PASS_START_COL(pass);
+ ystart = PNG_PASS_START_ROW(pass);
+ xstep = PNG_PASS_COL_OFFSET(pass);
+ ystep = PNG_PASS_ROW_OFFSET(pass);
+ }
+
+ else
+ {
+ ystart = xstart = 0;
+ ystep = xstep = 1;
+ }
+
+ /* To find the pixel, loop over 'py' for each pass
+ * reading a row and then checking to see if it
+ * contains the pixel.
+ */
+ for (py = ystart; py < height; py += ystep)
+ {
+ png_uint_32 px, ppx;
+
+ /* png_read_row takes two pointers. When libpng
+ * handles the interlace the first is filled in
+ * pixel-by-pixel, and the second receives the same
+ * pixels but they are replicated across the
+ * unwritten pixels so far for each pass. When we
+ * do the interlace, however, they just contain
+ * the pixels from the interlace pass - giving
+ * both is wasteful and pointless, so we pass a
+ * NULL pointer.
+ */
+ png_read_row(png_ptr, row_tmp, NULL);
+
+ /* Now find the pixel if it is in this row; there
+ * are, of course, much better ways of doing this
+ * than using a for loop:
+ */
+ if (y == py) for (px = xstart, ppx = 0;
+ px < width; px += xstep, ++ppx) if (x == px)
+ {
+ /* 'ppx' is the index of the pixel in the row
+ * buffer.
+ */
+ print_pixel(png_ptr, info_ptr, row_tmp, ppx);
+
+ /* Now terminate the loops early - we have
+ * found and handled the required data.
+ */
+ goto pass_loop_end;
+ } /* x loop */
+ } /* y loop */
+ } /* pass loop */
+
+ /* Finally free the temporary buffer: */
+ pass_loop_end:
+ row = NULL;
+ png_free(png_ptr, row_tmp);
+ }
+
+ else
+ png_error(png_ptr, "pngpixel: png_get_IHDR failed");
+
+ }
+
+ else
+ {
+ /* Else libpng has raised an error. An error message has
+ * already been output, so it is only necessary to clean up
+ * locally allocated data:
+ */
+ if (row != NULL)
+ {
+ /* The default implementation of png_free never errors out
+ * (it just crashes if something goes wrong), but the safe
+ * way of using it is still to clear 'row' before calling
+ * png_free:
+ */
+ png_bytep row_tmp = row;
+ row = NULL;
+ png_free(png_ptr, row_tmp);
+ }
+ }
+
+ png_destroy_info_struct(png_ptr, &info_ptr);
+ }
+
+ else
+ fprintf(stderr, "pngpixel: out of memory allocating png_info\n");
+
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ }
+
+ else
+ fprintf(stderr, "pngpixel: out of memory allocating png_struct\n");
+ }
+
+ else
+ fprintf(stderr, "pngpixel: %s: could not open file\n", argv[3]);
+ }
+
+ else
+ /* Wrong number of arguments */
+ fprintf(stderr, "pngpixel: usage: pngpixel x y png-file\n");
+
+ return result;
+}
+#endif /* READ && SEQUENTIAL_READ */
diff --git a/contrib/examples/pngtopng.c b/contrib/examples/pngtopng.c
new file mode 100644
index 0000000..4acf6b3
--- /dev/null
+++ b/contrib/examples/pngtopng.c
@@ -0,0 +1,95 @@
+/*- pngtopng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2011.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Read a PNG and write it out in a fixed format, using the 'simplified API'
+ * that was introduced in libpng-1.6.0.
+ *
+ * This sample code is just the code from the top of 'example.c' with some error
+ * handling added. See example.c for more comments.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && \
+ defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+
+int main(int argc, const char **argv)
+{
+ int result = 1;
+
+ if (argc == 3)
+ {
+ png_image image;
+
+ /* Only the image structure version number needs to be set. */
+ memset(&image, 0, sizeof image);
+ image.version = PNG_IMAGE_VERSION;
+
+ if (png_image_begin_read_from_file(&image, argv[1]))
+ {
+ png_bytep buffer;
+
+ /* Change this to try different formats! If you set a colormap format
+ * then you must also supply a colormap below.
+ */
+ image.format = PNG_FORMAT_RGBA;
+
+ buffer = malloc(PNG_IMAGE_SIZE(image));
+
+ if (buffer != NULL)
+ {
+ if (png_image_finish_read(&image, NULL/*background*/, buffer,
+ 0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
+ {
+ if (png_image_write_to_file(&image, argv[2],
+ 0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+ NULL/*colormap*/))
+ result = 0;
+
+ else
+ fprintf(stderr, "pngtopng: write %s: %s\n", argv[2],
+ image.message);
+
+ free(buffer);
+ }
+
+ else
+ {
+ fprintf(stderr, "pngtopng: read %s: %s\n", argv[1],
+ image.message);
+
+ /* This is the only place where a 'free' is required; libpng does
+ * the cleanup on error and success, but in this case we couldn't
+ * complete the read because of running out of memory.
+ */
+ png_image_free(&image);
+ }
+ }
+
+ else
+ fprintf(stderr, "pngtopng: out of memory: %lu bytes\n",
+ (unsigned long)PNG_IMAGE_SIZE(image));
+ }
+
+ else
+ /* Failed to read the first argument: */
+ fprintf(stderr, "pngtopng: %s: %s\n", argv[1], image.message);
+ }
+
+ else
+ /* Wrong number of arguments */
+ fprintf(stderr, "pngtopng: usage: pngtopng input-file output-file\n");
+
+ return result;
+}
+#endif /* READ && WRITE */
diff --git a/contrib/examples/simpleover.c b/contrib/examples/simpleover.c
new file mode 100644
index 0000000..59dd313
--- /dev/null
+++ b/contrib/examples/simpleover.c
@@ -0,0 +1,648 @@
+/*- simpleover
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2015.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Read several PNG files, which should have an alpha channel or transparency
+ * information, and composite them together to produce one or more 16-bit linear
+ * RGBA intermediates. This involves doing the correct 'over' composition to
+ * combine the alpha channels and corresponding data.
+ *
+ * Finally read an output (background) PNG using the 24-bit RGB format (the
+ * PNG will be composited on green (#00ff00) by default if it has an alpha
+ * channel), and apply the intermediate image generated above to specified
+ * locations in the image.
+ *
+ * The command line has the general format:
+ *
+ * simpleover <background.png> [output.png]
+ * {--sprite=width,height,name {[--at=x,y] {sprite.png}}}
+ * {--add=name {x,y}}
+ *
+ * The --sprite and --add options may occur multiple times. They are executed
+ * in order. --add may refer to any sprite already read.
+ *
+ * This code is intended to show how to composite multiple images together
+ * correctly. Apart from the libpng Simplified API the only work done in here
+ * is to combine multiple input PNG images into a single sprite; this involves
+ * a Porter-Duff 'over' operation and the input PNG images may, as a result,
+ * be regarded as being layered one on top of the other with the first (leftmost
+ * on the command line) being at the bottom and the last on the top.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation, so long as this
+ * file is linked against a sufficiently recent libpng (1.6+) it is ok to
+ * change this to <png.h>:
+ */
+#include "../../png.h"
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+
+#define sprite_name_chars 15
+struct sprite {
+ FILE *file;
+ png_uint_16p buffer;
+ unsigned int width;
+ unsigned int height;
+ char name[sprite_name_chars+1];
+};
+
+#if 0 /* div by 65535 test program */
+#include <math.h>
+#include <stdio.h>
+
+int main(void) {
+ double err = 0;
+ unsigned int xerr = 0;
+ unsigned int r = 32769;
+ {
+ unsigned int x = 0;
+
+ do {
+ unsigned int t = x + (x >> 16) /*+ (x >> 31)*/ + r;
+ double v = x, errtest;
+
+ if (t < x) {
+ fprintf(stderr, "overflow: %u+%u -> %u\n", x, r, t);
+ return 1;
+ }
+
+ v /= 65535;
+ errtest = v;
+ t >>= 16;
+ errtest -= t;
+
+ if (errtest > err) {
+ err = errtest;
+ xerr = x;
+
+ if (errtest >= .5) {
+ fprintf(stderr, "error: %u/65535 = %f, not %u, error %f\n",
+ x, v, t, errtest);
+ return 0;
+ }
+ }
+ } while (++x <= 65535U*65535U);
+ }
+
+ printf("error %f @ %u\n", err, xerr);
+
+ return 0;
+}
+#endif /* div by 65535 test program */
+
+static void
+sprite_op(const struct sprite *sprite, int x_offset, int y_offset,
+ png_imagep image, const png_uint_16 *buffer)
+{
+ /* This is where the Porter-Duff 'Over' operator is evaluated; change this
+ * code to change the operator (this could be parameterized). Any other
+ * image processing operation could be used here.
+ */
+
+
+ /* Check for an x or y offset that pushes any part of the image beyond the
+ * right or bottom of the sprite:
+ */
+ if ((y_offset < 0 || (unsigned)/*SAFE*/y_offset < sprite->height) &&
+ (x_offset < 0 || (unsigned)/*SAFE*/x_offset < sprite->width))
+ {
+ unsigned int y = 0;
+
+ if (y_offset < 0)
+ y = -y_offset; /* Skip to first visible row */
+
+ do
+ {
+ unsigned int x = 0;
+
+ if (x_offset < 0)
+ x = -x_offset;
+
+ do
+ {
+ /* In and out are RGBA values, so: */
+ const png_uint_16 *in_pixel = buffer + (y * image->width + x)*4;
+ png_uint_32 in_alpha = in_pixel[3];
+
+ /* This is the optimized Porter-Duff 'Over' operation, when the
+ * input alpha is 0 the output is not changed.
+ */
+ if (in_alpha > 0)
+ {
+ png_uint_16 *out_pixel = sprite->buffer +
+ ((y+y_offset) * sprite->width + (x+x_offset))*4;
+
+ /* This is the weight to apply to the output: */
+ in_alpha = 65535-in_alpha;
+
+ if (in_alpha > 0)
+ {
+ /* The input must be composed onto the output. This means
+ * multiplying the current output pixel value by the inverse
+ * of the input alpha (1-alpha). A division is required but
+ * it is by the constant 65535. Approximate this as:
+ *
+ * (x + (x >> 16) + 32769) >> 16;
+ *
+ * This is exact (and does not overflow) for all values of
+ * x in the range 0..65535*65535. (Note that the calculation
+ * produces the closest integer; the maximum error is <0.5).
+ */
+ png_uint_32 tmp;
+
+# define compose(c)\
+ tmp = out_pixel[c] * in_alpha;\
+ tmp = (tmp + (tmp >> 16) + 32769) >> 16;\
+ out_pixel[c] = tmp + in_pixel[c]
+
+ /* The following is very vectorizable... */
+ compose(0);
+ compose(1);
+ compose(2);
+ compose(3);
+ }
+
+ else
+ out_pixel[0] = in_pixel[0],
+ out_pixel[1] = in_pixel[1],
+ out_pixel[2] = in_pixel[2],
+ out_pixel[3] = in_pixel[3];
+ }
+ }
+ while (++x < image->width);
+ }
+ while (++y < image->height);
+ }
+}
+
+static int
+create_sprite(struct sprite *sprite, int *argc, const char ***argv)
+{
+ /* Read the arguments and create this sprite. The sprite buffer has already
+ * been allocated. This reads the input PNGs one by one in linear format,
+ * composes them onto the sprite buffer (the code in the function above)
+ * then saves the result, converting it on the fly to PNG RGBA 8-bit format.
+ */
+ while (*argc > 0)
+ {
+ char tombstone;
+ int x = 0, y = 0;
+
+ if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
+ {
+ /* The only supported option is --at. */
+ if (sscanf((*argv)[0], "--at=%d,%d%c", &x, &y, &tombstone) != 2)
+ break; /* success; caller will parse this option */
+
+ ++*argv, --*argc;
+ }
+
+ else
+ {
+ /* The argument has to be a file name */
+ png_image image;
+
+ image.version = PNG_IMAGE_VERSION;
+ image.opaque = NULL;
+
+ if (png_image_begin_read_from_file(&image, (*argv)[0]))
+ {
+ png_uint_16p buffer;
+
+ image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+
+ buffer = malloc(PNG_IMAGE_SIZE(image));
+
+ if (buffer != NULL)
+ {
+ if (png_image_finish_read(&image, NULL/*background*/, buffer,
+ 0/*row_stride*/,
+ NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
+ {
+ /* This is the place where the Porter-Duff 'Over' operator
+ * needs to be done by this code. In fact, any image
+ * processing required can be done here; the data is in
+ * the correct format (linear, 16-bit) and source and
+ * destination are in memory.
+ */
+ sprite_op(sprite, x, y, &image, buffer);
+ free(buffer);
+ ++*argv, --*argc;
+ /* And continue to the next argument */
+ continue;
+ }
+
+ else
+ {
+ free(buffer);
+ fprintf(stderr, "simpleover: read %s: %s\n", (*argv)[0],
+ image.message);
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
+ (unsigned long)PNG_IMAGE_SIZE(image));
+
+ /* png_image_free must be called if we abort the Simplified API
+ * read because of a problem detected in this code. If problems
+ * are detected in the Simplified API it cleans up itself.
+ */
+ png_image_free(&image);
+ }
+ }
+
+ else
+ {
+ /* Failed to read the first argument: */
+ fprintf(stderr, "simpleover: %s: %s\n", (*argv)[0], image.message);
+ }
+
+ return 0; /* failure */
+ }
+ }
+
+ /* All the sprite operations have completed successfully. Save the RGBA
+ * buffer as a PNG using the simplified write API.
+ */
+ sprite->file = tmpfile();
+
+ if (sprite->file != NULL)
+ {
+ png_image save;
+
+ memset(&save, 0, sizeof save);
+ save.version = PNG_IMAGE_VERSION;
+ save.opaque = NULL;
+ save.width = sprite->width;
+ save.height = sprite->height;
+ save.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+ save.flags = PNG_IMAGE_FLAG_FAST;
+ save.colormap_entries = 0;
+
+ if (png_image_write_to_stdio(&save, sprite->file, 1/*convert_to_8_bit*/,
+ sprite->buffer, 0/*row_stride*/, NULL/*colormap*/))
+ {
+ /* Success; the buffer is no longer needed: */
+ free(sprite->buffer);
+ sprite->buffer = NULL;
+ return 1; /* ok */
+ }
+
+ else
+ fprintf(stderr, "simpleover: write sprite %s: %s\n", sprite->name,
+ save.message);
+ }
+
+ else
+ fprintf(stderr, "simpleover: sprite %s: could not allocate tmpfile: %s\n",
+ sprite->name, strerror(errno));
+
+ return 0; /* fail */
+}
+
+static int
+add_sprite(png_imagep output, png_bytep out_buf, struct sprite *sprite,
+ int *argc, const char ***argv)
+{
+ /* Given a --add argument naming this sprite, perform the operations listed
+ * in the following arguments. The arguments are expected to have the form
+ * (x,y), which is just an offset at which to add the sprite to the
+ * output.
+ */
+ while (*argc > 0)
+ {
+ char tombstone;
+ int x, y;
+
+ if ((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
+ return 1; /* success */
+
+ if (sscanf((*argv)[0], "%d,%d%c", &x, &y, &tombstone) == 2)
+ {
+ /* Now add the new image into the sprite data, but only if it
+ * will fit.
+ */
+ if (x < 0 || y < 0 ||
+ (unsigned)/*SAFE*/x >= output->width ||
+ (unsigned)/*SAFE*/y >= output->height ||
+ sprite->width > output->width-x ||
+ sprite->height > output->height-y)
+ {
+ fprintf(stderr, "simpleover: sprite %s @ (%d,%d) outside image\n",
+ sprite->name, x, y);
+ /* Could just skip this, but for the moment it is an error */
+ return 0; /* error */
+ }
+
+ else
+ {
+ /* Since we know the sprite fits we can just read it into the
+ * output using the simplified API.
+ */
+ png_image in;
+
+ in.version = PNG_IMAGE_VERSION;
+ rewind(sprite->file);
+
+ if (png_image_begin_read_from_stdio(&in, sprite->file))
+ {
+ in.format = PNG_FORMAT_RGB; /* force compose */
+
+ if (png_image_finish_read(&in, NULL/*background*/,
+ out_buf + (y*output->width + x)*3/*RGB*/,
+ output->width*3/*row_stride*/,
+ NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
+ {
+ ++*argv, --*argc;
+ continue;
+ }
+ }
+
+ /* The read failed: */
+ fprintf(stderr, "simpleover: add sprite %s: %s\n", sprite->name,
+ in.message);
+ return 0; /* error */
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "simpleover: --add='%s': invalid position %s\n",
+ sprite->name, (*argv)[0]);
+ return 0; /* error */
+ }
+ }
+
+ return 1; /* ok */
+}
+
+static int
+simpleover_process(png_imagep output, png_bytep out_buf, int argc,
+ const char **argv)
+{
+ int result = 1; /* success */
+# define csprites 10/*limit*/
+# define str(a) #a
+ int nsprites = 0;
+ struct sprite sprites[csprites];
+
+ while (argc > 0)
+ {
+ result = 0; /* fail */
+
+ if (strncmp(argv[0], "--sprite=", 9) == 0)
+ {
+ char tombstone;
+
+ if (nsprites < csprites)
+ {
+ int n;
+
+ sprites[nsprites].width = sprites[nsprites].height = 0;
+ sprites[nsprites].name[0] = 0;
+
+ n = sscanf(argv[0], "--sprite=%u,%u,%" str(sprite_name_chars) "s%c",
+ &sprites[nsprites].width, &sprites[nsprites].height,
+ sprites[nsprites].name, &tombstone);
+
+ if ((n == 2 || n == 3) &&
+ sprites[nsprites].width > 0 && sprites[nsprites].height > 0)
+ {
+ size_t buf_size, tmp;
+
+ /* Default a name if not given. */
+ if (sprites[nsprites].name[0] == 0)
+ sprintf(sprites[nsprites].name, "sprite-%d", nsprites+1);
+
+ /* Allocate a buffer for the sprite and calculate the buffer
+ * size:
+ */
+ buf_size = sizeof (png_uint_16 [4]);
+ buf_size *= sprites[nsprites].width;
+ buf_size *= sprites[nsprites].height;
+
+ /* This can overflow a (size_t); check for this: */
+ tmp = buf_size;
+ tmp /= sprites[nsprites].width;
+ tmp /= sprites[nsprites].height;
+
+ if (tmp == sizeof (png_uint_16 [4]))
+ {
+ sprites[nsprites].buffer = malloc(buf_size);
+ /* This buffer must be initialized to transparent: */
+ memset(sprites[nsprites].buffer, 0, buf_size);
+
+ if (sprites[nsprites].buffer != NULL)
+ {
+ sprites[nsprites].file = NULL;
+ ++argv, --argc;
+
+ if (create_sprite(sprites+nsprites++, &argc, &argv))
+ {
+ result = 1; /* still ok */
+ continue;
+ }
+
+ break; /* error */
+ }
+ }
+
+ /* Overflow, or OOM */
+ fprintf(stderr, "simpleover: %s: sprite too large\n", argv[0]);
+ break;
+ }
+
+ else
+ {
+ fprintf(stderr, "simpleover: %s: invalid sprite (%u,%u)\n",
+ argv[0], sprites[nsprites].width, sprites[nsprites].height);
+ break;
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "simpleover: %s: too many sprites\n", argv[0]);
+ break;
+ }
+ }
+
+ else if (strncmp(argv[0], "--add=", 6) == 0)
+ {
+ const char *name = argv[0]+6;
+ int isprite = nsprites;
+
+ ++argv, --argc;
+
+ while (--isprite >= 0)
+ {
+ if (strcmp(sprites[isprite].name, name) == 0)
+ {
+ if (!add_sprite(output, out_buf, sprites+isprite, &argc, &argv))
+ goto out; /* error in add_sprite */
+
+ break;
+ }
+ }
+
+ if (isprite < 0) /* sprite not found */
+ {
+ fprintf(stderr, "simpleover: --add='%s': sprite not found\n", name);
+ break;
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "simpleover: %s: unrecognized operation\n", argv[0]);
+ break;
+ }
+
+ result = 1; /* ok */
+ }
+
+ /* Clean up the cache of sprites: */
+out:
+ while (--nsprites >= 0)
+ {
+ if (sprites[nsprites].buffer != NULL)
+ free(sprites[nsprites].buffer);
+
+ if (sprites[nsprites].file != NULL)
+ (void)fclose(sprites[nsprites].file);
+ }
+
+ return result;
+}
+
+int main(int argc, const char **argv)
+{
+ int result = 1; /* default to fail */
+
+ if (argc >= 2)
+ {
+ int argi = 2;
+ const char *output = NULL;
+ png_image image;
+
+ if (argc > 2 && argv[2][0] != '-'/*an operation*/)
+ {
+ output = argv[2];
+ argi = 3;
+ }
+
+ image.version = PNG_IMAGE_VERSION;
+ image.opaque = NULL;
+
+ if (png_image_begin_read_from_file(&image, argv[1]))
+ {
+ png_bytep buffer;
+
+ image.format = PNG_FORMAT_RGB; /* 24-bit RGB */
+
+ buffer = malloc(PNG_IMAGE_SIZE(image));
+
+ if (buffer != NULL)
+ {
+ png_color background = {0, 0xff, 0}; /* fully saturated green */
+
+ if (png_image_finish_read(&image, &background, buffer,
+ 0/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP */))
+ {
+ /* At this point png_image_finish_read has cleaned up the
+ * allocated data in png_image, and only the buffer needs to be
+ * freed.
+ *
+ * Perform the remaining operations:
+ */
+ if (simpleover_process(&image, buffer, argc-argi, argv+argi))
+ {
+ /* Write the output: */
+ if ((output != NULL &&
+ png_image_write_to_file(&image, output,
+ 0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+ NULL/*colormap*/)) ||
+ (output == NULL &&
+ png_image_write_to_stdio(&image, stdout,
+ 0/*convert_to_8bit*/, buffer, 0/*row_stride*/,
+ NULL/*colormap*/)))
+ result = 0;
+
+ else
+ fprintf(stderr, "simpleover: write %s: %s\n",
+ output == NULL ? "stdout" : output, image.message);
+ }
+
+ /* else simpleover_process writes an error message */
+ }
+
+ else
+ fprintf(stderr, "simpleover: read %s: %s\n", argv[1],
+ image.message);
+
+ free(buffer);
+ }
+
+ else
+ {
+ fprintf(stderr, "simpleover: out of memory: %lu bytes\n",
+ (unsigned long)PNG_IMAGE_SIZE(image));
+
+ /* This is the only place where a 'free' is required; libpng does
+ * the cleanup on error and success, but in this case we couldn't
+ * complete the read because of running out of memory.
+ */
+ png_image_free(&image);
+ }
+ }
+
+ else
+ {
+ /* Failed to read the first argument: */
+ fprintf(stderr, "simpleover: %s: %s\n", argv[1], image.message);
+ }
+ }
+
+ else
+ {
+ /* Usage message */
+ fprintf(stderr,
+ "simpleover: usage: simpleover background.png [output.png]\n"
+ " Output 'background.png' as a 24-bit RGB PNG file in 'output.png'\n"
+ " or, if not given, stdout. 'background.png' will be composited\n"
+ " on fully saturated green.\n"
+ "\n"
+ " Optionally, before output, process additional PNG files:\n"
+ "\n"
+ " --sprite=width,height,name {[--at=x,y] {sprite.png}}\n"
+ " Produce a transparent sprite of size (width,height) and with\n"
+ " name 'name'.\n"
+ " For each sprite.png composite it using a Porter-Duff 'Over'\n"
+ " operation at offset (x,y) in the sprite (defaulting to (0,0)).\n"
+ " Input PNGs will be truncated to the area of the sprite.\n"
+ "\n"
+ " --add='name' {x,y}\n"
+ " Optionally, before output, composite a sprite, 'name', which\n"
+ " must have been previously produced using --sprite, at each\n"
+ " offset (x,y) in the output image. Each sprite must fit\n"
+ " completely within the output image.\n"
+ "\n"
+ " PNG files are processed in the order they occur on the command\n"
+ " line and thus the first PNG processed appears as the bottommost\n"
+ " in the output image.\n");
+ }
+
+ return result;
+}
+#endif /* SIMPLIFIED_READ */
diff --git a/contrib/gregbook/COPYING b/contrib/gregbook/COPYING
index d60c31a..a3e9774 100644
--- a/contrib/gregbook/COPYING
+++ b/contrib/gregbook/COPYING
@@ -1,12 +1,12 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
- Preamble
+ Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -56,7 +56,7 @@
The precise terms and conditions for copying, distribution and
modification follow.
- GNU GENERAL PUBLIC LICENSE
+ GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -255,7 +255,7 @@
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
- NO WARRANTY
+ NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
- END OF TERMS AND CONDITIONS
+ END OF TERMS AND CONDITIONS
- How to Apply These Terms to Your New Programs
+ How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
diff --git a/contrib/gregbook/Makefile.mingw32 b/contrib/gregbook/Makefile.mingw32
index e70a59a..3a3ff60 100644
--- a/contrib/gregbook/Makefile.mingw32
+++ b/contrib/gregbook/Makefile.mingw32
@@ -47,7 +47,8 @@
#CC = i386-mingw32msvc-gcc # e.g., Linux -> Win32 cross-compilation
LD = $(CC)
RM = rm -f
-CFLAGS = -O -Wall $(INCS) $(MINGW_CCFLAGS)
+CPPFLAGS = $(INCS)
+CFLAGS = -O -Wall $(MINGW_CCFLAGS)
# [note that -Wall is a gcc-specific compilation flag ("most warnings on")]
# [-ansi, -pedantic and -W can also be used]
LDFLAGS = $(MINGW_LDFLAGS)
@@ -85,10 +86,10 @@
# implicit make rules -------------------------------------------------------
.c$(O):
- $(CC) -c $(CFLAGS) $<
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
%.pic$(O): %.c
- $(CC) -c $(CFLAGS) -DPNG_BUILD_DLL -o $@ $<
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) -DPNG_BUILD_DLL -o $@ $<
# dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/Makefile.sgi b/contrib/gregbook/Makefile.sgi
index e3ca6ce..94d61b4 100644
--- a/contrib/gregbook/Makefile.sgi
+++ b/contrib/gregbook/Makefile.sgi
@@ -23,9 +23,9 @@
# macros --------------------------------------------------------------------
-PNGINC = -I/usr/local/include/libpng12
-PNGLIB = -L/usr/local/lib -lpng12 # dynamically linked against libpng
-#PNGLIB = /usr/local/lib/libpng12.a # statically linked against libpng
+PNGINC = -I/usr/local/include/libpng16
+PNGLIB = -L/usr/local/lib -lpng16 # dynamically linked against libpng
+#PNGLIB = /usr/local/lib/libpng16.a # statically linked against libpng
# or:
#PNGINC = -I../..
#PNGLIB = -L../.. -lpng
@@ -53,7 +53,8 @@
LD = cc
RM = rm -f
# ABI must be the same as that used to build libpng.
-ABI=
+ABI =
+CPPFLAGS =
CFLAGS = $(ABI) -O -fullwarn $(INCS)
LDFLAGS = $(ABI)
O = .o
@@ -73,7 +74,7 @@
# implicit make rules -------------------------------------------------------
.c$(O):
- $(CC) -c $(CFLAGS) $<
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
# dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/Makefile.unx b/contrib/gregbook/Makefile.unx
index 7ff65bf..1a73e03 100644
--- a/contrib/gregbook/Makefile.unx
+++ b/contrib/gregbook/Makefile.unx
@@ -26,14 +26,14 @@
# macros --------------------------------------------------------------------
#PNGDIR = /usr/local/lib
-#PNGINC = -I/usr/local/include/libpng12
-#PNGLIBd = -L$(PNGDIR) -lpng12 # dynamically linked, installed libpng
-#PNGLIBs = $(PNGDIR)/libpng12.a # statically linked, installed libpng
+#PNGINC = -I/usr/local/include/libpng16
+#PNGLIBd = -L$(PNGDIR) -lpng16 # dynamically linked, installed libpng
+#PNGLIBs = $(PNGDIR)/libpng16.a # statically linked, installed libpng
# or:
PNGDIR = ../..# this one is for libpng-x.y.z/contrib/gregbook builds
#PNGDIR = ../libpng
PNGINC = -I$(PNGDIR)
-PNGLIBd = -Wl,-rpath,$(PNGDIR) -L$(PNGDIR) -lpng12 # dynamically linked
+PNGLIBd = -Wl,-rpath,$(PNGDIR) -L$(PNGDIR) -lpng16 # dynamically linked
PNGLIBs = $(PNGDIR)/libpng.a # statically linked, local libpng
ZDIR = /usr/local/lib
@@ -59,14 +59,16 @@
RLIBSd = $(PNGLIBd) $(ZLIBd) $(XLIB) -lm
RLIBSs = $(PNGLIBs) $(ZLIBs) $(XLIB) -lm
WLIBSd = $(PNGLIBd) $(ZLIBd) -lm
-WLIBSs = $(PNGLIBs) $(ZLIBs)
+WLIBSs = $(PNGLIBs) $(ZLIBs) -lm
CC = gcc
LD = gcc
RM = rm -f
-CFLAGS = -O -Wall $(INCS) -DFEATURE_LOOP
+CPPFLAGS = $(INCS) -DFEATURE_LOOP
+CFLAGS = -O -Wall
+#CFLAGS = -O -W -Wall -Wextra -pedantic -ansi
# [note that -Wall is a gcc-specific compilation flag ("most warnings on")]
-# [-ansi, -pedantic and -W can also be used]
+# [-ansi, -pedantic, -Wextra, and -W can also be used]
LDFLAGS =
O = .o
E =
@@ -92,7 +94,7 @@
# implicit make rules -------------------------------------------------------
.c$(O):
- $(CC) -c $(CFLAGS) $<
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
# dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/Makefile.w32 b/contrib/gregbook/Makefile.w32
index 3c08085..ab7dcf7 100644
--- a/contrib/gregbook/Makefile.w32
+++ b/contrib/gregbook/Makefile.w32
@@ -18,7 +18,7 @@
# "c:\windows\command.com /e:4096" as the program command line and set the
# working directory to this directory. Then double-click to open the new
# DOS-prompt window with a bigger environment and retry the commands above.
-#
+#
# This makefile assumes libpng and zlib have already been built or downloaded
# and are in subdirectories at the same level as the current subdirectory
# (as indicated by the PNGPATH and ZPATH macros below). Edit as appropriate.
@@ -53,7 +53,8 @@
CC = cl
LD = link
RM = del
-CFLAGS = -nologo -O -W3 $(INCS) $(cvars)
+CPPFLAGS = $(INCS)
+CFLAGS = -nologo -O -W3 $(cvars)
# [note that -W3 is an MSVC-specific compilation flag ("all warnings on")]
# [see %devstudio%\vc\include\win32.mak for cvars macro definition]
O = .obj
@@ -76,7 +77,7 @@
# implicit make rules -------------------------------------------------------
.c$(O):
- $(CC) -c $(CFLAGS) $<
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
# dependencies --------------------------------------------------------------
diff --git a/contrib/gregbook/README b/contrib/gregbook/README
index cb6e525..7b1f6a3 100644
--- a/contrib/gregbook/README
+++ b/contrib/gregbook/README
@@ -63,9 +63,9 @@
- Prerequisites (in order of compilation):
- - zlib http://zlib.net/
- - libpng http://www.libpng.org/pub/png/libpng.html
- - pngbook http://www.libpng.org/pub/png/book/sources.html
+ - zlib http://zlib.net/
+ - libpng http://www.libpng.org/pub/png/libpng.html
+ - pngbook http://www.libpng.org/pub/png/book/sources.html
The pngbook demo programs are explicitly designed to demonstrate proper
coding techniques for using the libpng reference library. As a result,
@@ -104,7 +104,7 @@
unpacked the source code.
For MSVC, set up the necessary environment variables by invoking
-
+
%devstudio%\vc\bin\vcvars32.bat
where where %devstudio% is the installation directory for MSVC /
@@ -165,19 +165,19 @@
rpng is a simple PNG viewer that can display transparent PNGs with a
specified background color; for example,
- rpng -bgcolor #ff0000 toucan.png
+ rpng -bgcolor \#ff0000 toucan.png
would display the image with a red background. rpng2 is a progressive
viewer that simulates a web browser in some respects; it can display
images against either a background color or a dynamically generated
background image. For example:
- rpng2 -bgpat 16 toucan.png
+ rpng2 -bgpat 16 toucan.png
wpng is a purely command-line image converter from binary PBMPLUS/NetPBM
format (.pgm or .ppm) to PNG; for example,
- wpng -time < toucan-notrans.ppm > toucan-notrans.png
+ wpng -time < toucan-notrans.ppm > toucan-notrans.png
would convert the specified PPM file (using redirection) to PNG, auto-
matically setting the PNG modification-time chunk.
diff --git a/contrib/gregbook/makevms.com b/contrib/gregbook/makevms.com
index bd37dc0..f32bcab 100644
--- a/contrib/gregbook/makevms.com
+++ b/contrib/gregbook/makevms.com
@@ -20,13 +20,13 @@
$ then
$ write sys$output "zlib include not found. Exiting..."
$ exit 2
-$ endif
+$ endif
$!
$ if pngpath .eqs. ""
$ then
$ write sys$output "libpng include not found. Exiting..."
$ exit 2
-$ endif
+$ endif
$!
$! Look for the compiler used.
$!
diff --git a/contrib/gregbook/readpng.c b/contrib/gregbook/readpng.c
index abfc1fb..9167403 100644
--- a/contrib/gregbook/readpng.c
+++ b/contrib/gregbook/readpng.c
@@ -55,8 +55,9 @@
#include <stdio.h>
#include <stdlib.h>
+#include <zlib.h>
-#include "png.h" /* libpng header; includes zlib.h */
+#include "png.h" /* libpng header */
#include "readpng.h" /* typedefs, common macros, public prototypes */
/* future versions of libpng will provide this macro: */
@@ -99,7 +100,8 @@
/* could pass pointers to user-defined error handlers instead of NULLs: */
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), NULL, NULL,
+ NULL);
if (!png_ptr)
return 4; /* out of memory */
@@ -214,6 +216,10 @@
* libpng function */
if (setjmp(png_jmpbuf(png_ptr))) {
+ free(image_data);
+ image_data = NULL;
+ free(row_pointers);
+ row_pointers = NULL;
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
@@ -229,8 +235,14 @@
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand(png_ptr);
+#ifdef PNG_READ_16_TO_8_SUPPORTED
if (bit_depth == 16)
+# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ png_set_scale_16(png_ptr);
+# else
png_set_strip_16(png_ptr);
+# endif
+#endif
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
@@ -263,7 +275,8 @@
return NULL;
}
- Trace((stderr, "readpng_get_image: channels = %d, rowbytes = %ld, height = %ld\n", *pChannels, rowbytes, height));
+ Trace((stderr, "readpng_get_image: channels = %d, rowbytes = %ld, height = %ld\n",
+ *pChannels, rowbytes, height));
/* set the individual row_pointers to point at the correct offsets */
diff --git a/contrib/gregbook/readpng2.c b/contrib/gregbook/readpng2.c
index 2ee6b68..a9e7ba5 100644
--- a/contrib/gregbook/readpng2.c
+++ b/contrib/gregbook/readpng2.c
@@ -4,7 +4,7 @@
---------------------------------------------------------------------------
- Copyright (c) 1998-2007 Greg Roelofs. All rights reserved.
+ Copyright (c) 1998-2015 Greg Roelofs. All rights reserved.
This software is provided "as is," without warranty of any kind,
express or implied. In no event shall the author or contributors
@@ -51,12 +51,19 @@
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ---------------------------------------------------------------------------
+
+ Changelog:
+ %RDATE% - Check return value of png_get_bKGD() (Glenn R-P)
+
---------------------------------------------------------------------------*/
#include <stdlib.h> /* for exit() prototype */
+#include <setjmp.h>
-#include "png.h" /* libpng header; includes zlib.h and setjmp.h */
+#include <zlib.h>
+#include "png.h" /* libpng header from the local directory */
#include "readpng2.h" /* typedefs, common macros, public prototypes */
@@ -67,90 +74,17 @@
png_uint_32 row_num, int pass);
static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr);
static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg);
+static void readpng2_warning_handler(png_structp png_ptr, png_const_charp msg);
void readpng2_version_info(void)
{
-#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
- (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
- defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
- /*
- * WARNING: This preprocessor approach means that the following code
- * cannot be used with a libpng DLL older than 1.2.0--the
- * compiled-in symbols for the new functions will not exist.
- * (Could use dlopen() and dlsym() on Unix and corresponding
- * calls for Windows, but not portable...)
- */
- {
- int mmxsupport = png_mmx_support();
- if (mmxsupport < 0)
- fprintf(stderr, " Compiled with libpng %s; using libpng %s "
- "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
- else {
- int compilerID;
- png_uint_32 mmx_mask = png_get_mmx_flagmask(
- PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
+ fprintf(stderr, " Compiled with libpng %s; using libpng %s\n",
+ PNG_LIBPNG_VER_STRING, png_libpng_ver);
- fprintf(stderr, " Compiled with libpng %s; using libpng %s "
- "with MMX support\n (%s version).", PNG_LIBPNG_VER_STRING,
- png_libpng_ver, compilerID == 1? "MSVC++" :
- (compilerID == 2? "GNU C" : "unknown"));
- fprintf(stderr, " Processor (x86%s) %s MMX instructions.\n",
-#if defined(__x86_64__)
- "_64",
-#else
- "",
-#endif
- mmxsupport? "supports" : "does not support");
- if (mmxsupport > 0) {
- int num_optims = 0;
-
- fprintf(stderr,
- " Potential MMX optimizations supported by libpng:\n");
- if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)
- ++num_optims;
- if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_UP)
- ++num_optims;
- if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)
- ++num_optims;
- if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)
- ++num_optims;
- if (num_optims)
- fprintf(stderr,
- " decoding %s row filters (reading)\n",
- (num_optims == 4)? "all non-trivial" : "some");
- if (mmx_mask & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) {
- fprintf(stderr, " combining rows (reading)\n");
- ++num_optims;
- }
- if (mmx_mask & PNG_ASM_FLAG_MMX_READ_INTERLACE) {
- fprintf(stderr,
- " expanding interlacing (reading)\n");
- ++num_optims;
- }
- mmx_mask &= ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
- | PNG_ASM_FLAG_MMX_READ_INTERLACE \
- | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
- | PNG_ASM_FLAG_MMX_READ_FILTER_UP \
- | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
- | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
- if (mmx_mask) {
- fprintf(stderr, " other (unknown)\n");
- ++num_optims;
- }
- if (num_optims == 0)
- fprintf(stderr, " (none)\n");
- }
- }
- }
-#else
- fprintf(stderr, " Compiled with libpng %s; using libpng %s "
- "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
-#endif
-
- fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n",
+ fprintf(stderr, " and with zlib %s; using zlib %s.\n",
ZLIB_VERSION, zlib_version);
}
@@ -175,8 +109,8 @@
/* could also replace libpng warning-handler (final NULL), but no need: */
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
- readpng2_error_handler, NULL);
+ png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), mainprog_ptr,
+ readpng2_error_handler, readpng2_warning_handler);
if (!png_ptr)
return 4; /* out of memory */
@@ -203,36 +137,30 @@
}
-#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
/* prepare the reader to ignore all recognized chunks whose data won't be
* used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT,
* IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */
{
- /* These byte strings were copied from png.h. If a future libpng
- * version recognizes more chunks, add them to this list. If a
- * future version of readpng2.c recognizes more chunks, delete them
- * from this list. */
- static const png_byte chunks_to_ignore[] = {
- 99, 72, 82, 77, '\0', /* cHRM */
- 104, 73, 83, 84, '\0', /* hIST */
- 105, 67, 67, 80, '\0', /* iCCP */
- 105, 84, 88, 116, '\0', /* iTXt */
- 111, 70, 70, 115, '\0', /* oFFs */
- 112, 67, 65, 76, '\0', /* pCAL */
- 112, 72, 89, 115, '\0', /* pHYs */
- 115, 66, 73, 84, '\0', /* sBIT */
- 115, 67, 65, 76, '\0', /* sCAL */
- 115, 80, 76, 84, '\0', /* sPLT */
- 115, 84, 69, 82, '\0', /* sTER */
- 116, 69, 88, 116, '\0', /* tEXt */
- 116, 73, 77, 69, '\0', /* tIME */
- 122, 84, 88, 116, '\0' /* zTXt */
- };
+ /* These byte strings were copied from png.h. If a future version
+ * of readpng2.c recognizes more chunks, add them to this list.
+ */
+ static PNG_CONST png_byte chunks_to_process[] = {
+ 98, 75, 71, 68, '\0', /* bKGD */
+ 103, 65, 77, 65, '\0', /* gAMA */
+ 115, 82, 71, 66, '\0', /* sRGB */
+ };
- png_set_keep_unknown_chunks(png_ptr, 1 /* PNG_HANDLE_CHUNK_NEVER */,
- chunks_to_ignore, sizeof(chunks_to_ignore)/5);
+ /* Ignore all chunks except for IHDR, PLTE, tRNS, IDAT, and IEND */
+ png_set_keep_unknown_chunks(png_ptr, -1 /* PNG_HANDLE_CHUNK_NEVER */,
+ NULL, -1);
+
+ /* But do not ignore chunks in the "chunks_to_process" list */
+ png_set_keep_unknown_chunks(png_ptr,
+ 0 /* PNG_HANDLE_CHUNK_AS_DEFAULT */, chunks_to_process,
+ sizeof(chunks_to_process)/5);
}
-#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
+#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */
/* instead of doing png_init_io() here, now we set up our callback
@@ -242,96 +170,6 @@
readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);
- /*
- * may as well enable or disable MMX routines here, if supported;
- *
- * to enable all: mask = png_get_mmx_flagmask (
- * PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
- * flags = png_get_asm_flags (png_ptr);
- * flags |= mask;
- * png_set_asm_flags (png_ptr, flags);
- *
- * to disable all: mask = png_get_mmx_flagmask (
- * PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
- * flags = png_get_asm_flags (png_ptr);
- * flags &= ~mask;
- * png_set_asm_flags (png_ptr, flags);
- */
-
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
- defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
- /*
- * WARNING: This preprocessor approach means that the following code
- * cannot be used with a libpng DLL older than 1.2.0--the
- * compiled-in symbols for the new functions will not exist.
- * (Could use dlopen() and dlsym() on Unix and corresponding
- * calls for Windows, but not portable...)
- */
- {
-#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
- png_uint_32 mmx_disable_mask = 0;
- png_uint_32 asm_flags, mmx_mask;
- int compilerID;
-
- if (mainprog_ptr->nommxfilters)
- mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
- | PNG_ASM_FLAG_MMX_READ_FILTER_UP \
- | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
- | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
- if (mainprog_ptr->nommxcombine)
- mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_COMBINE_ROW;
- if (mainprog_ptr->nommxinterlace)
- mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_INTERLACE;
- asm_flags = png_get_asm_flags(png_ptr);
- png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask);
-
-
- /* Now query libpng's asm settings, just for yuks. Note that this
- * differs from the querying of its *potential* MMX capabilities
- * in readpng2_version_info(); this is true runtime verification. */
-
- asm_flags = png_get_asm_flags(png_ptr);
- mmx_mask = png_get_mmx_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE,
- &compilerID);
- if (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED)
- fprintf(stderr,
- " MMX support (%s version) is compiled into libpng\n",
- compilerID == 1? "MSVC++" :
- (compilerID == 2? "GNU C" : "unknown"));
- else
- fprintf(stderr, " MMX support is not compiled into libpng\n");
- fprintf(stderr, " MMX instructions are %ssupported by CPU\n",
- (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)? "" : "not ");
- fprintf(stderr, " MMX read support for combining rows is %sabled\n",
- (asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)? "en" : "dis");
- fprintf(stderr,
- " MMX read support for expanding interlacing is %sabled\n",
- (asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)? "en" : "dis");
- fprintf(stderr, " MMX read support for \"sub\" filter is %sabled\n",
- (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "en" : "dis");
- fprintf(stderr, " MMX read support for \"up\" filter is %sabled\n",
- (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "en" : "dis");
- fprintf(stderr, " MMX read support for \"avg\" filter is %sabled\n",
- (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "en" : "dis");
- fprintf(stderr, " MMX read support for \"Paeth\" filter is %sabled\n",
- (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "en" : "dis");
- asm_flags &= (mmx_mask & ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
- | PNG_ASM_FLAG_MMX_READ_INTERLACE \
- | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
- | PNG_ASM_FLAG_MMX_READ_FILTER_UP \
- | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
- | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ));
- if (asm_flags)
- fprintf(stderr,
- " additional MMX support is also enabled (0x%02lx)\n",
- asm_flags);
-#else /* !PNG_ASSEMBLER_CODE_SUPPORTED */
- fprintf(stderr, " MMX querying is disabled in libpng.\n");
-#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
- }
-#endif
-
-
/* make sure we save our pointers for use in readpng2_decode_data() */
mainprog_ptr->png_ptr = png_ptr;
@@ -380,7 +218,11 @@
mainprog_info *mainprog_ptr;
int color_type, bit_depth;
png_uint_32 width, height;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
double gamma;
+#else
+ png_fixed_point gamma;
+#endif
/* setjmp() doesn't make sense here, because we'd either have to exit(),
@@ -424,36 +266,38 @@
/* since we know we've read all of the PNG file's "header" (i.e., up
* to IDAT), we can check for a background color here */
- if (mainprog_ptr->need_bgcolor &&
- png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
+ if (mainprog_ptr->need_bgcolor)
{
png_color_16p pBackground;
/* it is not obvious from the libpng documentation, but this function
* takes a pointer to a pointer, and it always returns valid red,
* green and blue values, regardless of color_type: */
- png_get_bKGD(png_ptr, info_ptr, &pBackground);
+ if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
+ {
- /* however, it always returns the raw bKGD data, regardless of any
- * bit-depth transformations, so check depth and adjust if necessary */
- if (bit_depth == 16) {
- mainprog_ptr->bg_red = pBackground->red >> 8;
- mainprog_ptr->bg_green = pBackground->green >> 8;
- mainprog_ptr->bg_blue = pBackground->blue >> 8;
- } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
- if (bit_depth == 1)
- mainprog_ptr->bg_red = mainprog_ptr->bg_green =
- mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
- else if (bit_depth == 2)
- mainprog_ptr->bg_red = mainprog_ptr->bg_green =
- mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
- else /* bit_depth == 4 */
- mainprog_ptr->bg_red = mainprog_ptr->bg_green =
- mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
- } else {
- mainprog_ptr->bg_red = (uch)pBackground->red;
- mainprog_ptr->bg_green = (uch)pBackground->green;
- mainprog_ptr->bg_blue = (uch)pBackground->blue;
+ /* however, it always returns the raw bKGD data, regardless of any
+ * bit-depth transformations, so check depth and adjust if necessary
+ */
+ if (bit_depth == 16) {
+ mainprog_ptr->bg_red = pBackground->red >> 8;
+ mainprog_ptr->bg_green = pBackground->green >> 8;
+ mainprog_ptr->bg_blue = pBackground->blue >> 8;
+ } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ if (bit_depth == 1)
+ mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+ mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
+ else if (bit_depth == 2)
+ mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+ mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
+ else /* bit_depth == 4 */
+ mainprog_ptr->bg_red = mainprog_ptr->bg_green =
+ mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
+ } else {
+ mainprog_ptr->bg_red = (uch)pBackground->red;
+ mainprog_ptr->bg_green = (uch)pBackground->green;
+ mainprog_ptr->bg_blue = (uch)pBackground->blue;
+ }
}
}
@@ -469,8 +313,14 @@
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand(png_ptr);
+#ifdef PNG_READ_16_TO_8_SUPPORTED
if (bit_depth == 16)
+# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ png_set_scale_16(png_ptr);
+# else
png_set_strip_16(png_ptr);
+# endif
+#endif
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
@@ -491,11 +341,19 @@
* "gamma" value for the entire display system, i.e., the product of
* LUT_exponent and CRT_exponent. */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma);
else
png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455);
-
+#else
+ if (png_get_gAMA_fixed(png_ptr, info_ptr, &gamma))
+ png_set_gamma_fixed(png_ptr,
+ (png_fixed_point)(100000*mainprog_ptr->display_exponent+.5), gamma);
+ else
+ png_set_gamma_fixed(png_ptr,
+ (png_fixed_point)(100000*mainprog_ptr->display_exponent+.5), 45455);
+#endif
/* we'll let libpng expand interlaced images, too */
@@ -597,6 +455,8 @@
/* all done */
+ (void)info_ptr; /* Unused */
+
return;
}
@@ -617,7 +477,12 @@
}
-
+static void readpng2_warning_handler(png_structp png_ptr, png_const_charp msg)
+{
+ fprintf(stderr, "readpng2 libpng warning: %s\n", msg);
+ fflush(stderr);
+ (void)png_ptr; /* Unused */
+}
static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg)
@@ -644,5 +509,12 @@
exit(99);
}
+ /* Now we have our data structure we can use the information in it
+ * to return control to our own higher level code (all the points
+ * where 'setjmp' is called in this file.) This will work with other
+ * error handling mechanisms as well - libpng always calls png_error
+ * when it can proceed no further, thus, so long as the error handler
+ * is intercepted, application code can do its own error recovery.
+ */
longjmp(mainprog_ptr->jmpbuf, 1);
}
diff --git a/contrib/gregbook/readpng2.h b/contrib/gregbook/readpng2.h
index fbfffb4..6b3660d 100644
--- a/contrib/gregbook/readpng2.h
+++ b/contrib/gregbook/readpng2.h
@@ -96,11 +96,6 @@
int rowbytes;
int channels;
int need_bgcolor;
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- int nommxfilters;
- int nommxcombine;
- int nommxinterlace;
-#endif
int state;
uch bg_red;
uch bg_green;
diff --git a/contrib/gregbook/readppm.c b/contrib/gregbook/readppm.c
index be9a56d..7fefc39 100644
--- a/contrib/gregbook/readppm.c
+++ b/contrib/gregbook/readppm.c
@@ -163,8 +163,12 @@
/* now we can go ahead and just read the whole image */
- fread(image_data, 1L, rowbytes*height, saved_infile);
-
+ if (fread(image_data, 1L, rowbytes*height, saved_infile) <
+ rowbytes*height) {
+ free (image_data);
+ image_data = NULL;
+ return NULL;
+ }
return image_data;
}
diff --git a/contrib/gregbook/rpng-win.c b/contrib/gregbook/rpng-win.c
index 2020961..f53ddc8 100644
--- a/contrib/gregbook/rpng-win.c
+++ b/contrib/gregbook/rpng-win.c
@@ -85,7 +85,33 @@
#include <string.h>
#include <time.h>
#include <windows.h>
+#ifdef __CYGWIN__
+/* getch replacement. Turns out, we don't really need this,
+ * but leave it here if we ever enable any of the uses of
+ * _getch in the main code
+ */
+#include <unistd.h>
+#include <termio.h>
+#include <sys/ioctl.h>
+int repl_getch( void )
+{
+ char ch;
+ int fd = fileno(stdin);
+ struct termio old_tty, new_tty;
+
+ ioctl(fd, TCGETA, &old_tty);
+ new_tty = old_tty;
+ new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
+ ioctl(fd, TCSETA, &new_tty);
+ fread(&ch, 1, sizeof(ch), stdin);
+ ioctl(fd, TCSETA, &old_tty);
+
+ return ch;
+}
+#define _getch repl_getch
+#else
#include <conio.h> /* only for _getch() */
+#endif
/* #define DEBUG : this enables the Trace() macros */
@@ -153,7 +179,7 @@
filename = (char *)NULL;
-
+#ifndef __CYGWIN__
/* First reenable console output, which normally goes to the bit bucket
* for windowed apps. Closing the console window will terminate the
* app. Thanks to David.Geldreich@realviz.com for supplying the magical
@@ -162,6 +188,7 @@
AllocConsole();
freopen("CONOUT$", "a", stderr);
freopen("CONOUT$", "a", stdout);
+#endif
/* Next set the default value for our display-system exponent, i.e.,
@@ -279,7 +306,9 @@
/* print usage screen if any errors up to this point */
if (error) {
+#ifndef __CYGWIN__
int ch;
+#endif
fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
readpng_version_info();
@@ -293,11 +322,15 @@
"\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
"\t\t used with transparent images\n"
"\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+#ifndef __CYGWIN__
"Press Q or Esc to quit this usage screen.\n"
+#endif
"\n", PROGNAME, default_display_exponent);
+#ifndef __CYGWIN__
do
ch = _getch();
while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
exit(1);
}
@@ -333,18 +366,24 @@
if (error) {
+#ifndef __CYGWIN__
int ch;
+#endif
fprintf(stderr, PROGNAME ": aborting.\n");
+#ifndef __CYGWIN__
do
ch = _getch();
while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
exit(2);
} else {
fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
+#ifndef __CYGWIN__
fprintf(stderr,
"\n [console window: closing this window will terminate %s]\n\n",
PROGNAME);
+#endif
}
@@ -417,7 +456,12 @@
/* wait for the user to tell us when to quit */
printf(
- "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
+#ifndef __CYGWIN__
+ "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"
+#else
+ "Done. Press mouse button 1 (within image window) to quit.\n"
+#endif
+ );
fflush(stdout);
while (GetMessage(&msg, NULL, 0, 0)) {
diff --git a/contrib/gregbook/rpng-x.c b/contrib/gregbook/rpng-x.c
index 0a98170..ddd7c58 100644
--- a/contrib/gregbook/rpng-x.c
+++ b/contrib/gregbook/rpng-x.c
@@ -26,6 +26,8 @@
- 1.14: added support for X resources (thanks to Gerhard Niklasch)
- 2.00: dual-licensed (added GNU GPL)
- 2.01: fixed improper display of usage screen on PNG error(s)
+ - 2.02: Added "void(argc);" statement to quiet pedantic compiler warnings
+ about unused variable (GR-P)
---------------------------------------------------------------------------
@@ -80,9 +82,9 @@
#define PROGNAME "rpng-x"
#define LONGNAME "Simple PNG Viewer for X"
-#define VERSION "2.01 of 16 March 2008"
-#define RESNAME "rpng" /* our X resource application name */
-#define RESCLASS "Rpng" /* our X resource class name */
+#define VERSION "2.02 of 15 June 2014"
+#define RESNAME "rpng" /* our X resource application name */
+#define RESCLASS "Rpng" /* our X resource class name */
#include <stdio.h>
#include <stdlib.h>
@@ -252,8 +254,8 @@
else {
bgstr = *argv;
if (strlen(bgstr) != 7 || bgstr[0] != '#')
- ++error;
- else
+ ++error;
+ else
have_bg = TRUE;
}
} else {
@@ -279,15 +281,17 @@
"Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n"
" xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
" exp \ttransfer-function exponent (``gamma'') of the display\n"
- "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
+ "\t\t system in floating-point format (e.g., ``%.1f''); equal\n",
+ PROGNAME, default_display_exponent);
+
+ fprintf(stderr, "\n"
"\t\t to the product of the lookup-table exponent (varies)\n"
"\t\t and the CRT exponent (usually 2.2); must be positive\n"
" bg \tdesired background color in 7-character hex RGB format\n"
"\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
"\t\t used with transparent images\n"
"\nPress Q, Esc or mouse button 1 (within image window, after image\n"
- "is displayed) to quit.\n"
- "\n", PROGNAME, default_display_exponent);
+ "is displayed) to quit.\n");
exit(1);
}
@@ -419,6 +423,8 @@
rpng_x_cleanup();
+ (void)argc; /* Unused */
+
return 0;
}
diff --git a/contrib/gregbook/rpng2-win.c b/contrib/gregbook/rpng2-win.c
index 9f7ad78..a7b9dfa 100644
--- a/contrib/gregbook/rpng2-win.c
+++ b/contrib/gregbook/rpng2-win.c
@@ -32,6 +32,9 @@
- 2.01: fixed 64-bit typo in readpng2.c
- 2.02: fixed improper display of usage screen on PNG error(s); fixed
unexpected-EOF and file-read-error cases
+ - 2.03: removed runtime MMX-enabling/disabling and obsolete -mmx* options
+ - 2.04:
+ (GR-P)
---------------------------------------------------------------------------
@@ -95,7 +98,33 @@
#include <time.h>
#include <math.h> /* only for PvdM background code */
#include <windows.h>
+#ifdef __CYGWIN__
+/* getch replacement. Turns out, we don't really need this,
+ * but leave it here if we ever enable any of the uses of
+ * _getch in the main code
+ */
+#include <unistd.h>
+#include <termio.h>
+#include <sys/ioctl.h>
+int repl_getch( void )
+{
+ char ch;
+ int fd = fileno(stdin);
+ struct termio old_tty, new_tty;
+
+ ioctl(fd, TCGETA, &old_tty);
+ new_tty = old_tty;
+ new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
+ ioctl(fd, TCSETA, &new_tty);
+ fread(&ch, 1, sizeof(ch), stdin);
+ ioctl(fd, TCSETA, &old_tty);
+
+ return ch;
+}
+#define _getch repl_getch
+#else
#include <conio.h> /* only for _getch() */
+#endif
/* all for PvdM background code: */
#ifndef PI
@@ -269,7 +298,7 @@
filename = (char *)NULL;
memset(&rpng2_info, 0, sizeof(mainprog_info));
-
+#ifndef __CYGWIN__
/* Next reenable console output, which normally goes to the bit bucket
* for windowed apps. Closing the console window will terminate the
* app. Thanks to David.Geldreich@realviz.com for supplying the magical
@@ -278,7 +307,7 @@
AllocConsole();
freopen("CONOUT$", "a", stderr);
freopen("CONOUT$", "a", stdout);
-
+#endif
/* Set the default value for our display-system exponent, i.e., the
* product of the CRT exponent and the exponent corresponding to
@@ -395,18 +424,6 @@
}
} else if (!strncmp(*argv, "-timing", 2)) {
timing = TRUE;
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- } else if (!strncmp(*argv, "-nommxfilters", 7)) {
- rpng2_info.nommxfilters = TRUE;
- } else if (!strncmp(*argv, "-nommxcombine", 7)) {
- rpng2_info.nommxcombine = TRUE;
- } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
- rpng2_info.nommxinterlace = TRUE;
- } else if (!strcmp(*argv, "-nommx")) {
- rpng2_info.nommxfilters = TRUE;
- rpng2_info.nommxcombine = TRUE;
- rpng2_info.nommxinterlace = TRUE;
-#endif
} else {
if (**argv != '-') {
filename = *argv;
@@ -424,15 +441,14 @@
/* print usage screen if any errors up to this point */
if (error) {
+#ifndef __CYGWIN__
int ch;
+#endif
fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
readpng2_version_info();
fprintf(stderr, "\n"
"Usage: %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n"
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- " %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
-#endif
" %*s file.png\n\n"
" exp \ttransfer-function exponent (``gamma'') of the display\n"
"\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
@@ -445,21 +461,24 @@
"\t\t transparent images; overrides -bgcolor option\n"
" -timing\tenables delay for every block read, to simulate modem\n"
"\t\t download of image (~36 Kbps)\n"
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- " -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
- "\t\t combining rows, and expanding interlacing, respectively\n"
-#endif
"\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
+#ifndef __CYGWIN__
"Press Q or Esc to quit this usage screen. ",
+#else
+ ,
+#endif
PROGNAME,
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
+#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
+ !(defined(__CYGWIN__) || defined(__MINGW32__))
(int)strlen(PROGNAME), " ",
#endif
(int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
fflush(stderr);
+#ifndef __CYGWIN__
do
ch = _getch();
while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
exit(1);
}
@@ -496,18 +515,24 @@
if (error) {
+#ifndef __CYGWIN__
int ch;
+#endif
fprintf(stderr, PROGNAME ": aborting.\n");
+#ifndef __CYGWIN__
do
ch = _getch();
while (ch != 'q' && ch != 'Q' && ch != 0x1B);
+#endif
exit(2);
} else {
fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
+#ifndef __CYGWIN__
fprintf(stderr,
"\n [console window: closing this window will terminate %s]\n\n",
PROGNAME);
+#endif
fflush(stderr);
}
@@ -567,7 +592,7 @@
"(unexpectedly) while reading PNG image file\n");
exit(3);
} else /* if (error) */ {
- // will print error message below
+ /* will print error message below */
}
break;
}
@@ -1150,7 +1175,12 @@
rpng2_info.state = kDone;
printf(
- "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
+#ifndef __CYGWIN__
+ "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"
+#else
+ "Done. Press mouse button 1 (within image window) to quit.\n"
+#endif
+ );
fflush(stdout);
}
diff --git a/contrib/gregbook/rpng2-x.c b/contrib/gregbook/rpng2-x.c
index 873af23..0c8ddeb 100644
--- a/contrib/gregbook/rpng2-x.c
+++ b/contrib/gregbook/rpng2-x.c
@@ -40,10 +40,13 @@
- 2.02: fixed improper display of usage screen on PNG error(s); fixed
unexpected-EOF and file-read-error cases; fixed Trace() cut-and-
paste bugs
-
+ - 2.03: deleted runtime MMX-enabling/disabling and obsolete -mmx* options
+ - 2.04: Added "void(foo);" statements to quiet pedantic compiler warnings
+ about unused variables (GR-P)
+ - 2.05: Use nanosleep() instead of usleep(), which is deprecated (GR-P).
---------------------------------------------------------------------------
- Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
+ Copyright (c) 1998-2010, 2014-2015 Greg Roelofs. All rights reserved.
This software is provided "as is," without warranty of any kind,
express or implied. In no event shall the author or contributors
@@ -94,9 +97,9 @@
#define PROGNAME "rpng2-x"
#define LONGNAME "Progressive PNG Viewer for X"
-#define VERSION "2.02 of 16 March 2008"
-#define RESNAME "rpng2" /* our X resource application name */
-#define RESCLASS "Rpng" /* our X resource class name */
+#define VERSION "2.04 of 15 June 2014"
+#define RESNAME "rpng2" /* our X resource application name */
+#define RESCLASS "Rpng" /* our X resource class name */
#include <stdio.h>
#include <stdlib.h>
@@ -110,6 +113,19 @@
#include <X11/Xos.h>
#include <X11/keysym.h> /* defines XK_* macros */
+#if _POSIX_C_SOURCE >= 199309L /* have nanosleep() */
+# undef usleep
+# define usleep(usec) { \
+ struct timespec ts; \
+ ts.tv_sec = 0; \
+ ts.tv_nsec = (usec) * 1000; \
+ nanosleep(&ts, NULL); }
+# endif
+
+#ifndef usleep /* have neither nanosleep() nor usleep() */
+# define usleep(x) sleep(((x)+499999)/1000000)
+#endif
+
#ifdef VMS
# include <unistd.h>
#endif
@@ -131,7 +147,7 @@
(e.type == KeyPress && /* v--- or 1 for shifted keys */ \
((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape)))
-#define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
+#define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */
#define rgb1_max bg_freq
#define rgb1_min bg_gray
@@ -301,7 +317,7 @@
int have_bg = FALSE;
#ifdef FEATURE_LOOP
int loop = FALSE;
- long loop_interval = -1; /* seconds (100,000 max) */
+ long loop_interval = -1; /* seconds (100,000 max) */
#endif
double LUT_exponent; /* just the lookup table */
double CRT_exponent = 2.2; /* just the monitor */
@@ -435,18 +451,6 @@
loop_interval = 100000;
}
#endif
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- } else if (!strncmp(*argv, "-nommxfilters", 7)) {
- rpng2_info.nommxfilters = TRUE;
- } else if (!strncmp(*argv, "-nommxcombine", 7)) {
- rpng2_info.nommxcombine = TRUE;
- } else if (!strncmp(*argv, "-nommxinterlace", 7)) {
- rpng2_info.nommxinterlace = TRUE;
- } else if (!strcmp(*argv, "-nommx")) {
- rpng2_info.nommxfilters = TRUE;
- rpng2_info.nommxcombine = TRUE;
- rpng2_info.nommxinterlace = TRUE;
-#endif
} else {
if (**argv != '-') {
filename = *argv;
@@ -467,50 +471,47 @@
fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
readpng2_version_info();
fprintf(stderr, "\n"
- "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- " %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n"
-#endif
+ "Usage: ");
+ fprintf(stderr,
+ "%s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
+ " %*s [-usleep dur | -timing] [-pause]\n",
+ PROGNAME, (int)strlen(PROGNAME), " ");
+ fprintf(stderr,
#ifdef FEATURE_LOOP
- " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n"
-#else
- " %*s [-usleep dur | -timing] [-pause] file.png\n\n"
+ " [-loop [sec]]"
#endif
+ " file.png\n\n");
+ fprintf(stderr,
" xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
" exp \ttransfer-function exponent (``gamma'') of the display\n"
"\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
- "\t\t to the product of the lookup-table exponent (varies)\n"
+ "\t\t to the product of the lookup-table exponent (varies)\n",
+ default_display_exponent);
+ fprintf(stderr,
"\t\t and the CRT exponent (usually 2.2); must be positive\n"
" bg \tdesired background color in 7-character hex RGB format\n"
"\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
"\t\t used with transparent images; overrides -bgpat\n"
" pat \tdesired background pattern number (0-%d); used with\n"
- "\t\t transparent images; overrides -bgcolor\n"
+ "\t\t transparent images; overrides -bgcolor\n",
+ num_bgpat-1);
#ifdef FEATURE_LOOP
+ fprintf(stderr,
" -loop\tloops through background images after initial display\n"
"\t\t is complete (depends on -bgpat)\n"
- " sec \tseconds to display each background image (default = 2)\n"
+ " sec \tseconds to display each background image (default = 2)\n");
#endif
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- " -nommx*\tdisable optimized MMX routines for decoding row filters,\n"
- "\t\t combining rows, and expanding interlacing, respectively\n"
-#endif
+ fprintf(stderr,
" dur \tduration in microseconds to wait after displaying each\n"
"\t\t row (for demo purposes)\n"
" -timing\tenables delay for every block read, to simulate modem\n"
"\t\t download of image (~36 Kbps)\n"
" -pause\tpauses after displaying each pass until mouse clicked\n"
"\nPress Q, Esc or mouse button 1 (within image window, after image\n"
- "is displayed) to quit.\n"
- "\n", PROGNAME,
-#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
- (int)strlen(PROGNAME), " ",
-#endif
- (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1);
+ "is displayed) to quit.\n");
exit(1);
}
-
if (!(infile = fopen(filename, "rb"))) {
fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
++error;
@@ -612,7 +613,7 @@
"(unexpectedly) while reading PNG image file\n");
exit(3);
} else /* if (error) */ {
- // will print error message below
+ /* will print error message below */
}
break;
}
@@ -756,6 +757,8 @@
Trace((stderr, "about to call rpng2_x_cleanup()\n"))
rpng2_x_cleanup();
+ (void)argc; /* Unused */
+
return 0;
}
@@ -802,8 +805,9 @@
if (rpng2_x_create_window()) {
/* GRR TEMPORARY HACK: this is fundamentally no different from cases
- * above; libpng should longjmp() back to us when png_ptr goes away.
- * If we/it segfault instead, seems like a libpng bug... */
+ * above; libpng should call our error handler to longjmp() back to us
+ * when png_ptr goes away. If we/it segfault instead, seems like a
+ * libpng bug... */
/* we're here via libpng callback, so if window fails, clean and bail */
readpng2_cleanup(&rpng2_info);
@@ -1297,7 +1301,7 @@
for (row = 0; row < rpng2_info.height; ++row) {
src = bg_data + row*bg_rowbytes;
dest = ximage->data + row*ximage_rowbytes;
- if (bpp == 32) { /* slightly optimized version */
+ if (bpp == 32) { /* slightly optimized version */
for (i = rpng2_info.width; i > 0; --i) {
red = *src++;
green = *src++;
@@ -1846,6 +1850,9 @@
XFlush(display);
}
+ (void)startcol;
+ (void)width;
+
} /* end function rpng2_x_redisplay_image() */
diff --git a/contrib/gregbook/writepng.c b/contrib/gregbook/writepng.c
index e6d81ea..c5c9534 100644
--- a/contrib/gregbook/writepng.c
+++ b/contrib/gregbook/writepng.c
@@ -55,8 +55,9 @@
#include <stdlib.h> /* for exit() prototype */
+#include <zlib.h>
-#include "png.h" /* libpng header; includes zlib.h and setjmp.h */
+#include "png.h" /* libpng header, includes setjmp.h */
#include "writepng.h" /* typedefs, common macros, public prototypes */
@@ -89,7 +90,7 @@
/* could also replace libpng warning-handler (final NULL), but no need: */
- png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
+ png_ptr = png_create_write_struct(png_get_libpng_ver(NULL), mainprog_ptr,
writepng_error_handler, NULL);
if (!png_ptr)
return 4; /* out of memory */
@@ -104,7 +105,8 @@
/* setjmp() must be called in every function that calls a PNG-writing
* libpng function, unless an alternate error handler was installed--
* but compatible error handlers must either use longjmp() themselves
- * (as in this program) or exit immediately, so here we go: */
+ * (as in this program) or some other method to return control to
+ * application code, so here we go: */
if (setjmp(mainprog_ptr->jmpbuf)) {
png_destroy_write_struct(&png_ptr, &info_ptr);
@@ -388,5 +390,12 @@
exit(99);
}
+ /* Now we have our data structure we can use the information in it
+ * to return control to our own higher level code (all the points
+ * where 'setjmp' is called in this file.) This will work with other
+ * error handling mechanisms as well - libpng always calls png_error
+ * when it can proceed no further, thus, so long as the error handler
+ * is intercepted, application code can do its own error recovery.
+ */
longjmp(mainprog_ptr->jmpbuf, 1);
}
diff --git a/contrib/libtests/fakepng.c b/contrib/libtests/fakepng.c
new file mode 100644
index 0000000..6512c14
--- /dev/null
+++ b/contrib/libtests/fakepng.c
@@ -0,0 +1,65 @@
+/* Fake a PNG - just write it out directly.
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2014.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ */
+
+#include <stdio.h>
+#include <zlib.h> /* for crc32 */
+
+void
+put_uLong(uLong val)
+{
+ putchar(val >> 24);
+ putchar(val >> 16);
+ putchar(val >> 8);
+ putchar(val >> 0);
+}
+
+void
+put_chunk(const unsigned char *chunk, uInt length)
+{
+ uLong crc;
+
+ put_uLong(length-4); /* Exclude the tag */
+
+ fwrite(chunk, length, 1, stdout);
+
+ crc = crc32(0, Z_NULL, 0);
+ put_uLong(crc32(crc, chunk, length));
+}
+
+const unsigned char signature[] =
+{
+ 137, 80, 78, 71, 13, 10, 26, 10
+};
+
+const unsigned char IHDR[] =
+{
+ 73, 72, 68, 82, /* IHDR */
+ 0, 0, 0, 1, /* width */
+ 0, 0, 0, 1, /* height */
+ 1, /* bit depth */
+ 0, /* color type: greyscale */
+ 0, /* compression method */
+ 0, /* filter method */
+ 0 /* interlace method: none */
+};
+
+const unsigned char unknown[] =
+{
+ 'u', 'n', 'K', 'n' /* "unKn" - private safe to copy */
+};
+
+int
+main(void)
+{
+ fwrite(signature, sizeof signature, 1, stdout);
+ put_chunk(IHDR, sizeof IHDR);
+
+ for (;;)
+ put_chunk(unknown, sizeof unknown);
+}
diff --git a/contrib/libtests/gentests.sh b/contrib/libtests/gentests.sh
new file mode 100755
index 0000000..f0f8d23
--- /dev/null
+++ b/contrib/libtests/gentests.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 John Cunningham Bowler
+#
+# Last changed in libpng 1.6.0 [February 14, 2013]
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+#
+# Generate a set of PNG test images. The images are generated in a
+# sub-directory called 'tests' by default, however a command line argument will
+# change that name. The generation requires a built version of makepng in the
+# current directory.
+#
+usage(){
+ exec >&2
+ echo "$0 [<directory>]"
+ echo ' Generate a set of PNG test files in "directory" ("tests" by default)'
+ exit 1
+}
+
+mp="$PWD/makepng"
+test -x "$mp" || {
+ exec >&2
+ echo "$0: the 'makepng' program must exist"
+ echo " in the directory within which this program:"
+ echo " $mp"
+ echo " is executed"
+ usage
+}
+
+# Just one argument: the directory
+testdir="tests"
+test $# -gt 1 && {
+ testdir="$1"
+ shift
+}
+test $# -eq 0 || usage
+
+# Take care not to clobber something
+if test -e "$testdir"
+then
+ test -d "$testdir" || usage
+else
+ # mkdir -p isn't portable, so do the following
+ mkdir "$testdir" 2>/dev/null || mkdir -p "$testdir" || usage
+fi
+
+# This fails in a very satisfactory way if it's not accessible
+cd "$testdir"
+:>"test$$.png" || {
+ exec >&2
+ echo "$testdir: directory not writable"
+ usage
+}
+rm "test$$.png" || {
+ exec >&2
+ echo "$testdir: you have create but not write privileges here."
+ echo " This is unexpected. You have a spurion; "'"'"test$$.png"'"'"."
+ echo " You need to remove this yourself. Try a different directory."
+ exit 1
+}
+
+# Now call makepng ($mp) to create every file we can think of with a
+# reasonable name
+doit(){
+ for gamma in "" --sRGB --linear --1.8
+ do
+ case "$gamma" in
+ "")
+ gname=;;
+ --sRGB)
+ gname="-srgb";;
+ --linear)
+ gname="-lin";;
+ --1.8)
+ gname="-18";;
+ *)
+ gname="-$gamma";;
+ esac
+ "$mp" $gamma "$1" "$2" "test-$1-$2$gname.png"
+ done
+}
+#
+for ct in gray palette
+do
+ for bd in 1 2 4 8
+ do
+ doit "$ct" "$bd"
+ done
+done
+#
+doit "gray" "16"
+#
+for ct in gray-alpha rgb rgb-alpha
+do
+ for bd in 8 16
+ do
+ doit "$ct" "$bd"
+ done
+done
diff --git a/contrib/libtests/makepng.c b/contrib/libtests/makepng.c
new file mode 100644
index 0000000..60d96d1
--- /dev/null
+++ b/contrib/libtests/makepng.c
@@ -0,0 +1,1941 @@
+/* makepng.c */
+#define _ISOC99_SOURCE
+/* Copyright: */
+#define COPYRIGHT "\251 2013,2015 John Cunningham Bowler"
+/*
+ * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Make a test PNG image. The arguments are as follows:
+ *
+ * makepng [--sRGB|--linear|--1.8] [--tRNS] [--nofilters] \
+ * color-type bit-depth [file-name]
+ *
+ * The color-type may be numeric (and must match the numbers used by the PNG
+ * specification) or one of the format names listed below. The bit-depth is the
+ * component bit depth, or the pixel bit-depth for a color-mapped image.
+ *
+ * Without any options no color-space information is written, with the options
+ * an sRGB or the appropriate gAMA chunk is written. "1.8" refers to the
+ * display system used on older Apple computers to correct for high ambient
+ * light levels in the viewing environment; it applies a transform of
+ * approximately value^(1/1.45) to the color values and so a gAMA chunk of 65909
+ * is written (1.45/2.2).
+ *
+ * The image data is generated internally. Unless --color is given the images
+ * used are as follows:
+ *
+ * 1 channel: a square image with a diamond, the least luminous colors are on
+ * the edge of the image, the most luminous in the center.
+ *
+ * 2 channels: the color channel increases in luminosity from top to bottom, the
+ * alpha channel increases in opacity from left to right.
+ *
+ * 3 channels: linear combinations of, from the top-left corner clockwise,
+ * black, green, white, red.
+ *
+ * 4 channels: linear combinations of, from the top-left corner clockwise,
+ * transparent, red, green, blue.
+ *
+ * For color-mapped images a four channel color-map is used and if --tRNS is
+ * given the PNG file has a tRNS chunk, as follows:
+ *
+ * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
+ * 2-bit: entry 0: transparent-green
+ * entry 1: 40%-red
+ * entry 2: 80%-blue
+ * entry 3: opaque-white
+ * 4-bit: the 16 combinations of the 2-bit case
+ * 8-bit: the 256 combinations of the 4-bit case
+ *
+ * The palette always has 2^bit-depth entries and the tRNS chunk one fewer. The
+ * image is the 1-channel diamond, but using palette index, not luminosity.
+ *
+ * For formats other than color-mapped ones if --tRNS is specified a tRNS chunk
+ * is generated with all channels equal to the low bits of 0x0101.
+ *
+ * Image size is determined by the final pixel depth in bits, i.e. channels x
+ * bit-depth, as follows:
+ *
+ * 8 bits or less: 64x64
+ * 16 bits: 256x256
+ * More than 16 bits: 1024x1024
+ *
+ * Row filtering is the libpng default but may be turned off (the 'none' filter
+ * is used on every row) with the --nofilters option.
+ *
+ * The images are not interlaced.
+ *
+ * If file-name is given then the PNG is written to that file, else it is
+ * written to stdout. Notice that stdout is not supported on systems where, by
+ * default, it assumes text output; this program makes no attempt to change the
+ * text mode of stdout!
+ *
+ * makepng --color=<color> ...
+ *
+ * If --color is given then the whole image has that color, color-mapped images
+ * will have exactly one palette entry and all image files with be 16x16 in
+ * size. The color value is 1 to 4 decimal numbers as appropriate for the color
+ * type.
+ *
+ * makepng --small ...
+ *
+ * If --small is given the images are no larger than required to include every
+ * possible pixel value for the format.
+ *
+ * For formats with pixels 8 bits or fewer in size the images consist of a
+ * single row with 2^pixel-depth pixels, one of every possible value.
+ *
+ * For formats with 16-bit pixels a 256x256 image is generated containing every
+ * possible pixel value.
+ *
+ * For larger pixel sizes a 256x256 image is generated where the first row
+ * consists of each pixel that has identical byte values throughout the pixel
+ * followed by rows where the byte values differ within the pixel.
+ *
+ * In all cases the pixel values are arranged in such a way that the SUB and UP
+ * filters give byte sequences for maximal zlib compression. By default (if
+ * --nofilters is not given) the SUB filter is used on the first row and the UP
+ * filter on all following rows.
+ *
+ * The --small option is meant to provide good test-case coverage, however the
+ * images are not easy to examine visually. Without the --small option the
+ * images contain identical color values; the pixel values are adjusted
+ * according to the gamma encoding with no gamma encoding being interpreted as
+ * sRGB.
+ *
+ * LICENSING
+ * =========
+ *
+ * This code is copyright of the authors, see the COPYRIGHT define above. The
+ * code is licensed as above, using the libpng license. The code generates
+ * images which are solely the product of the code; the options choose which of
+ * the many possibilities to generate. The images that result (but not the code
+ * which generates them) are licensed as defined here:
+ *
+ * IMPORTANT: the COPYRIGHT #define must contain ISO-Latin-1 characters, the
+ * IMAGE_LICENSING #define must contain UTF-8 characters. The 'copyright'
+ * symbol 0xA9U (\251) in ISO-Latin-1 encoding and 0xC20xA9 (\302\251) in UTF-8.
+ */
+#define IMAGE_LICENSING "Dedicated to the public domain per Creative Commons "\
+ "license \"CC0 1.0\"; https://creativecommons.org/publicdomain/zero/1.0/"
+
+#include <stddef.h> /* for offsetof */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdint.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+#include <zlib.h>
+
+/* Work round for GCC complaints about casting a (double) function result to
+ * an unsigned:
+ */
+static unsigned int
+flooru(double d)
+{
+ d = floor(d);
+ return (unsigned int)d;
+}
+
+static png_byte
+floorb(double d)
+{
+ d = floor(d);
+ return (png_byte)d;
+}
+
+/* This structure is used for inserting extra chunks (the --insert argument, not
+ * documented above.)
+ */
+typedef struct chunk_insert
+{
+ struct chunk_insert *next;
+ void (*insert)(png_structp, png_infop, int, png_charpp);
+ int nparams;
+ png_charp parameters[1];
+} chunk_insert;
+
+static unsigned int
+channels_of_type(int color_type)
+{
+ if (color_type & PNG_COLOR_MASK_PALETTE)
+ return 1;
+
+ else
+ {
+ int channels = 1;
+
+ if (color_type & PNG_COLOR_MASK_COLOR)
+ channels = 3;
+
+ if (color_type & PNG_COLOR_MASK_ALPHA)
+ return channels + 1;
+
+ else
+ return channels;
+ }
+}
+
+static unsigned int
+pixel_depth_of_type(int color_type, int bit_depth)
+{
+ return channels_of_type(color_type) * bit_depth;
+}
+
+static unsigned int
+image_size_of_type(int color_type, int bit_depth, unsigned int *colors,
+ int small)
+{
+ if (*colors)
+ return 16;
+
+ else
+ {
+ int pixel_depth = pixel_depth_of_type(color_type, bit_depth);
+
+ if (small)
+ {
+ if (pixel_depth <= 8) /* there will be one row */
+ return 1 << pixel_depth;
+
+ else
+ return 256;
+ }
+
+ else if (pixel_depth < 8)
+ return 64;
+
+ else if (pixel_depth > 16)
+ return 1024;
+
+ else
+ return 256;
+ }
+}
+
+static void
+set_color(png_colorp color, png_bytep trans, unsigned int red,
+ unsigned int green, unsigned int blue, unsigned int alpha,
+ png_const_bytep gamma_table)
+{
+ color->red = gamma_table[red];
+ color->green = gamma_table[green];
+ color->blue = gamma_table[blue];
+ *trans = (png_byte)alpha;
+}
+
+static int
+generate_palette(png_colorp palette, png_bytep trans, int bit_depth,
+ png_const_bytep gamma_table, unsigned int *colors)
+{
+ /*
+ * 1-bit: entry 0 is transparent-red, entry 1 is opaque-white
+ * 2-bit: entry 0: transparent-green
+ * entry 1: 40%-red
+ * entry 2: 80%-blue
+ * entry 3: opaque-white
+ * 4-bit: the 16 combinations of the 2-bit case
+ * 8-bit: the 256 combinations of the 4-bit case
+ */
+ switch (colors[0])
+ {
+ default:
+ fprintf(stderr, "makepng: --colors=...: invalid count %u\n",
+ colors[0]);
+ exit(1);
+
+ case 1:
+ set_color(palette+0, trans+0, colors[1], colors[1], colors[1], 255,
+ gamma_table);
+ return 1;
+
+ case 2:
+ set_color(palette+0, trans+0, colors[1], colors[1], colors[1],
+ colors[2], gamma_table);
+ return 1;
+
+ case 3:
+ set_color(palette+0, trans+0, colors[1], colors[2], colors[3], 255,
+ gamma_table);
+ return 1;
+
+ case 4:
+ set_color(palette+0, trans+0, colors[1], colors[2], colors[3],
+ colors[4], gamma_table);
+ return 1;
+
+ case 0:
+ if (bit_depth == 1)
+ {
+ set_color(palette+0, trans+0, 255, 0, 0, 0, gamma_table);
+ set_color(palette+1, trans+1, 255, 255, 255, 255, gamma_table);
+ return 2;
+ }
+
+ else
+ {
+ unsigned int size = 1U << (bit_depth/2); /* 2, 4 or 16 */
+ unsigned int x, y;
+ volatile unsigned int ip = 0;
+
+ for (x=0; x<size; ++x) for (y=0; y<size; ++y)
+ {
+ ip = x + (size * y);
+
+ /* size is at most 16, so the scaled value below fits in 16 bits
+ */
+# define interp(pos, c1, c2) ((pos * c1) + ((size-pos) * c2))
+# define xyinterp(x, y, c1, c2, c3, c4) (((size * size / 2) +\
+ (interp(x, c1, c2) * y + (size-y) * interp(x, c3, c4))) /\
+ (size*size))
+
+ set_color(palette+ip, trans+ip,
+ /* color: green, red,blue,white */
+ xyinterp(x, y, 0, 255, 0, 255),
+ xyinterp(x, y, 255, 0, 0, 255),
+ xyinterp(x, y, 0, 0, 255, 255),
+ /* alpha: 0, 102, 204, 255) */
+ xyinterp(x, y, 0, 102, 204, 255),
+ gamma_table);
+ }
+
+ return ip+1;
+ }
+ }
+}
+
+static void
+set_value(png_bytep row, size_t rowbytes, png_uint_32 x, unsigned int bit_depth,
+ png_uint_32 value, png_const_bytep gamma_table, double conv)
+{
+ unsigned int mask = (1U << bit_depth)-1;
+
+ x *= bit_depth; /* Maximum x is 4*1024, maximum bit_depth is 16 */
+
+ if (value <= mask)
+ {
+ png_uint_32 offset = x >> 3;
+
+ if (offset < rowbytes && (bit_depth < 16 || offset+1 < rowbytes))
+ {
+ row += offset;
+
+ switch (bit_depth)
+ {
+ case 1:
+ case 2:
+ case 4:
+ /* Don't gamma correct - values get smashed */
+ {
+ unsigned int shift = (8 - bit_depth) - (x & 0x7U);
+
+ mask <<= shift;
+ value = (value << shift) & mask;
+ *row = (png_byte)((*row & ~mask) | value);
+ }
+ return;
+
+ default:
+ fprintf(stderr, "makepng: bad bit depth (internal error)\n");
+ exit(1);
+
+ case 16:
+ value = flooru(65535*pow(value/65535.,conv)+.5);
+ *row++ = (png_byte)(value >> 8);
+ *row = (png_byte)value;
+ return;
+
+ case 8:
+ *row = gamma_table[value];
+ return;
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "makepng: row buffer overflow (internal error)\n");
+ exit(1);
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "makepng: component overflow (internal error)\n");
+ exit(1);
+ }
+}
+
+static int /* filter mask for row */
+generate_row(png_bytep row, size_t rowbytes, unsigned int y, int color_type,
+ int bit_depth, png_const_bytep gamma_table, double conv,
+ unsigned int *colors, int small)
+{
+ int filters = 0; /* file *MASK*, 0 means the default, not NONE */
+ png_uint_32 size_max =
+ image_size_of_type(color_type, bit_depth, colors, small)-1;
+ png_uint_32 depth_max = (1U << bit_depth)-1; /* up to 65536 */
+
+ if (colors[0] == 0) if (small)
+ {
+ unsigned int pixel_depth = pixel_depth_of_type(color_type, bit_depth);
+
+ /* For pixel depths less than 16 generate a single row containing all the
+ * possible pixel values. For 16 generate all 65536 byte pair
+ * combinations in a 256x256 pixel array.
+ */
+ switch (pixel_depth)
+ {
+ case 1:
+ assert(y == 0 && rowbytes == 1 && size_max == 1);
+ row[0] = 0x6CU; /* binary: 01101100, only top 2 bits used */
+ filters = PNG_FILTER_NONE;
+ break;
+
+ case 2:
+ assert(y == 0 && rowbytes == 1 && size_max == 3);
+ row[0] = 0x1BU; /* binary 00011011, all bits used */
+ filters = PNG_FILTER_NONE;
+ break;
+
+ case 4:
+ assert(y == 0 && rowbytes == 8 && size_max == 15);
+ row[0] = 0x01U;
+ row[1] = 0x23U; /* SUB gives 0x22U for all following bytes */
+ row[2] = 0x45U;
+ row[3] = 0x67U;
+ row[4] = 0x89U;
+ row[5] = 0xABU;
+ row[6] = 0xCDU;
+ row[7] = 0xEFU;
+ filters = PNG_FILTER_SUB;
+ break;
+
+ case 8:
+ /* The row will have all the pixel values in order starting with
+ * '1', the SUB filter will change every byte into '1' (including
+ * the last, which generates pixel value '0'). Since the SUB filter
+ * has value 1 this should result in maximum compression.
+ */
+ assert(y == 0 && rowbytes == 256 && size_max == 255);
+ for (;;)
+ {
+ row[size_max] = 0xFFU & (size_max+1);
+ if (size_max == 0)
+ break;
+ --size_max;
+ }
+ filters = PNG_FILTER_SUB;
+ break;
+
+ case 16:
+ /* Rows are generated such that each row has a constant difference
+ * between the first and second byte of each pixel and so that the
+ * difference increases by 1 at each row. The rows start with the
+ * first byte value of 0 and the value increases to 255 across the
+ * row.
+ *
+ * The difference starts at 1, so the first row is:
+ *
+ * 0 1 1 2 2 3 3 4 ... 254 255 255 0
+ *
+ * This means that running the SUB filter on the first row produces:
+ *
+ * [SUB==1] 0 1 0 1 0 1...
+ *
+ * Then the difference is 2 on the next row, giving:
+ *
+ * 0 2 1 3 2 4 3 5 ... 254 0 255 1
+ *
+ * When the UP filter is run on this libpng produces:
+ *
+ * [UP ==2] 0 1 0 1 0 1...
+ *
+ * And so on for all the remain rows to the final two * rows:
+ *
+ * row 254: 0 255 1 0 2 1 3 2 4 3 ... 254 253 255 254
+ * row 255: 0 0 1 1 2 2 3 3 4 4 ... 254 254 255 255
+ */
+ assert(rowbytes == 512 && size_max == 255);
+ for (;;)
+ {
+ row[2*size_max ] = 0xFFU & size_max;
+ row[2*size_max+1] = 0xFFU & (size_max+y+1);
+ if (size_max == 0)
+ break;
+ --size_max;
+ }
+ /* The first row must include PNG_FILTER_UP so that libpng knows we
+ * need to keep it for the following row:
+ */
+ filters = (y == 0 ? PNG_FILTER_SUB+PNG_FILTER_UP : PNG_FILTER_UP);
+ break;
+
+ case 24:
+ case 32:
+ case 48:
+ case 64:
+ /* The rows are filled by an alogorithm similar to the above, in the
+ * first row pixel bytes are all equal, increasing from 0 by 1 for
+ * each pixel. In the second row the bytes within a pixel are
+ * incremented 1,3,5,7,... from the previous row byte. Using an odd
+ * number ensures all the possible byte values are used.
+ */
+ assert(size_max == 255 && rowbytes == 256*(pixel_depth>>3));
+ pixel_depth >>= 3; /* now in bytes */
+ while (rowbytes > 0)
+ {
+ const size_t pixel_index = --rowbytes/pixel_depth;
+
+ if (y == 0)
+ row[rowbytes] = 0xFFU & pixel_index;
+
+ else
+ {
+ const size_t byte_offset =
+ rowbytes - pixel_index * pixel_depth;
+
+ row[rowbytes] =
+ 0xFFU & (pixel_index + (byte_offset * 2*y) + 1);
+ }
+ }
+ filters = (y == 0 ? PNG_FILTER_SUB+PNG_FILTER_UP : PNG_FILTER_UP);
+ break;
+
+ default:
+ assert(0/*NOT REACHED*/);
+ }
+ }
+
+ else switch (channels_of_type(color_type))
+ {
+ /* 1 channel: a square image with a diamond, the least luminous colors are on
+ * the edge of the image, the most luminous in the center.
+ */
+ case 1:
+ {
+ png_uint_32 x;
+ png_uint_32 base = 2*size_max - abs(2*y-size_max);
+
+ for (x=0; x<=size_max; ++x)
+ {
+ png_uint_32 luma = base - abs(2*x-size_max);
+
+ /* 'luma' is now in the range 0..2*size_max, we need
+ * 0..depth_max
+ */
+ luma = (luma*depth_max + size_max) / (2*size_max);
+ set_value(row, rowbytes, x, bit_depth, luma, gamma_table, conv);
+ }
+ }
+ break;
+
+ /* 2 channels: the color channel increases in luminosity from top to bottom,
+ * the alpha channel increases in opacity from left to right.
+ */
+ case 2:
+ {
+ png_uint_32 alpha = (depth_max * y * 2 + size_max) / (2 * size_max);
+ png_uint_32 x;
+
+ for (x=0; x<=size_max; ++x)
+ {
+ set_value(row, rowbytes, 2*x, bit_depth,
+ (depth_max * x * 2 + size_max) / (2 * size_max), gamma_table,
+ conv);
+ set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
+ conv);
+ }
+ }
+ break;
+
+ /* 3 channels: linear combinations of, from the top-left corner clockwise,
+ * black, green, white, red.
+ */
+ case 3:
+ {
+ /* x0: the black->red scale (the value of the red component) at the
+ * start of the row (blue and green are 0).
+ * x1: the green->white scale (the value of the red and blue
+ * components at the end of the row; green is depth_max).
+ */
+ png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
+ png_uint_32 x;
+
+ /* Interpolate x/depth_max from start to end:
+ *
+ * start end difference
+ * red: Y Y 0
+ * green: 0 depth_max depth_max
+ * blue: 0 Y Y
+ */
+ for (x=0; x<=size_max; ++x)
+ {
+ set_value(row, rowbytes, 3*x+0, bit_depth, /* red */ Y,
+ gamma_table, conv);
+ set_value(row, rowbytes, 3*x+1, bit_depth, /* green */
+ (depth_max * x * 2 + size_max) / (2 * size_max),
+ gamma_table, conv);
+ set_value(row, rowbytes, 3*x+2, bit_depth, /* blue */
+ (Y * x * 2 + size_max) / (2 * size_max),
+ gamma_table, conv);
+ }
+ }
+ break;
+
+ /* 4 channels: linear combinations of, from the top-left corner clockwise,
+ * transparent, red, green, blue.
+ */
+ case 4:
+ {
+ /* x0: the transparent->blue scale (the value of the blue and alpha
+ * components) at the start of the row (red and green are 0).
+ * x1: the red->green scale (the value of the red and green
+ * components at the end of the row; blue is 0 and alpha is
+ * depth_max).
+ */
+ png_uint_32 Y = (depth_max * y * 2 + size_max) / (2 * size_max);
+ png_uint_32 x;
+
+ /* Interpolate x/depth_max from start to end:
+ *
+ * start end difference
+ * red: 0 depth_max-Y depth_max-Y
+ * green: 0 Y Y
+ * blue: Y 0 -Y
+ * alpha: Y depth_max depth_max-Y
+ */
+ for (x=0; x<=size_max; ++x)
+ {
+ set_value(row, rowbytes, 4*x+0, bit_depth, /* red */
+ ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
+ gamma_table, conv);
+ set_value(row, rowbytes, 4*x+1, bit_depth, /* green */
+ (Y * x * 2 + size_max) / (2 * size_max),
+ gamma_table, conv);
+ set_value(row, rowbytes, 4*x+2, bit_depth, /* blue */
+ Y - (Y * x * 2 + size_max) / (2 * size_max),
+ gamma_table, conv);
+ set_value(row, rowbytes, 4*x+3, bit_depth, /* alpha */
+ Y + ((depth_max-Y) * x * 2 + size_max) / (2 * size_max),
+ gamma_table, conv);
+ }
+ }
+ break;
+
+ default:
+ fprintf(stderr, "makepng: internal bad channel count\n");
+ exit(2);
+ }
+
+ else if (color_type & PNG_COLOR_MASK_PALETTE)
+ {
+ /* Palette with fixed color: the image rows are all 0 and the image width
+ * is 16.
+ */
+ memset(row, 0, rowbytes);
+ }
+
+ else if (colors[0] == channels_of_type(color_type))
+ switch (channels_of_type(color_type))
+ {
+ case 1:
+ {
+ const png_uint_32 luma = colors[1];
+ png_uint_32 x;
+
+ for (x=0; x<=size_max; ++x)
+ set_value(row, rowbytes, x, bit_depth, luma, gamma_table,
+ conv);
+ }
+ break;
+
+ case 2:
+ {
+ const png_uint_32 luma = colors[1];
+ const png_uint_32 alpha = colors[2];
+ png_uint_32 x;
+
+ for (x=0; x<size_max; ++x)
+ {
+ set_value(row, rowbytes, 2*x, bit_depth, luma, gamma_table,
+ conv);
+ set_value(row, rowbytes, 2*x+1, bit_depth, alpha, gamma_table,
+ conv);
+ }
+ }
+ break;
+
+ case 3:
+ {
+ const png_uint_32 red = colors[1];
+ const png_uint_32 green = colors[2];
+ const png_uint_32 blue = colors[3];
+ png_uint_32 x;
+
+ for (x=0; x<=size_max; ++x)
+ {
+ set_value(row, rowbytes, 3*x+0, bit_depth, red, gamma_table,
+ conv);
+ set_value(row, rowbytes, 3*x+1, bit_depth, green, gamma_table,
+ conv);
+ set_value(row, rowbytes, 3*x+2, bit_depth, blue, gamma_table,
+ conv);
+ }
+ }
+ break;
+
+ case 4:
+ {
+ const png_uint_32 red = colors[1];
+ const png_uint_32 green = colors[2];
+ const png_uint_32 blue = colors[3];
+ const png_uint_32 alpha = colors[4];
+ png_uint_32 x;
+
+ for (x=0; x<=size_max; ++x)
+ {
+ set_value(row, rowbytes, 4*x+0, bit_depth, red, gamma_table,
+ conv);
+ set_value(row, rowbytes, 4*x+1, bit_depth, green, gamma_table,
+ conv);
+ set_value(row, rowbytes, 4*x+2, bit_depth, blue, gamma_table,
+ conv);
+ set_value(row, rowbytes, 4*x+3, bit_depth, alpha, gamma_table,
+ conv);
+ }
+ }
+ break;
+
+ default:
+ fprintf(stderr, "makepng: internal bad channel count\n");
+ exit(2);
+ }
+
+ else
+ {
+ fprintf(stderr,
+ "makepng: --color: count(%u) does not match channels(%u)\n",
+ colors[0], channels_of_type(color_type));
+ exit(1);
+ }
+
+ return filters;
+}
+
+
+static void PNGCBAPI
+makepng_warning(png_structp png_ptr, png_const_charp message)
+{
+ const char **ep = png_get_error_ptr(png_ptr);
+ const char *name;
+
+ if (ep != NULL && *ep != NULL)
+ name = *ep;
+
+ else
+ name = "makepng";
+
+ fprintf(stderr, "%s: warning: %s\n", name, message);
+}
+
+static void PNGCBAPI
+makepng_error(png_structp png_ptr, png_const_charp message)
+{
+ makepng_warning(png_ptr, message);
+ png_longjmp(png_ptr, 1);
+}
+
+static int /* 0 on success, else an error code */
+write_png(const char **name, FILE *fp, int color_type, int bit_depth,
+ volatile png_fixed_point gamma, chunk_insert * volatile insert,
+ unsigned int filters, unsigned int *colors, int small, int tRNS)
+{
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ name, makepng_error, makepng_warning);
+ volatile png_infop info_ptr = NULL;
+ volatile png_bytep row = NULL;
+
+ if (png_ptr == NULL)
+ {
+ fprintf(stderr, "makepng: OOM allocating write structure\n");
+ return 1;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_structp nv_ptr = png_ptr;
+ png_infop nv_info = info_ptr;
+
+ png_ptr = NULL;
+ info_ptr = NULL;
+ png_destroy_write_struct(&nv_ptr, &nv_info);
+ if (row != NULL) free(row);
+ return 1;
+ }
+
+ /* Allow benign errors so that we can write PNGs with errors */
+ png_set_benign_errors(png_ptr, 1/*allowed*/);
+
+ /* Max out the text compression level in an attempt to make the license
+ * small. If --small then do the same for the IDAT.
+ */
+ if (small)
+ png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
+
+ png_set_text_compression_level(png_ptr, Z_BEST_COMPRESSION);
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ {
+ const unsigned int size =
+ image_size_of_type(color_type, bit_depth, colors, small);
+ unsigned int ysize;
+ png_fixed_point real_gamma = 45455; /* For sRGB */
+ png_byte gamma_table[256];
+ double conv;
+
+ /* Normally images are square, but with 'small' we want to simply generate
+ * all the pixel values, or all that we reasonably can:
+ */
+ if (small)
+ {
+ const unsigned int pixel_depth =
+ pixel_depth_of_type(color_type, bit_depth);
+
+ if (pixel_depth <= 8U)
+ {
+ assert(size == (1U<<pixel_depth));
+ ysize = 1U;
+ }
+
+ else
+ {
+ assert(size == 256U);
+ ysize = 256U;
+ }
+ }
+
+ else
+ ysize = size;
+
+ /* This function uses the libpng values used on read to carry extra
+ * information about the gamma:
+ */
+ if (gamma == PNG_GAMMA_MAC_18)
+ gamma = 65909;
+
+ else if (gamma > 0 && gamma < 1000)
+ gamma = PNG_FP_1;
+
+ if (gamma > 0)
+ real_gamma = gamma;
+
+ {
+ unsigned int i;
+
+ if (real_gamma == 45455) for (i=0; i<256; ++i)
+ {
+ gamma_table[i] = (png_byte)i;
+ conv = 1.;
+ }
+
+ else
+ {
+ /* Convert 'i' from sRGB (45455) to real_gamma, this makes
+ * the images look the same regardless of the gAMA chunk.
+ */
+ conv = real_gamma;
+ conv /= 45455;
+
+ gamma_table[0] = 0;
+
+ for (i=1; i<255; ++i)
+ gamma_table[i] = floorb(pow(i/255.,conv) * 255 + .5);
+
+ gamma_table[255] = 255;
+ }
+ }
+
+ png_set_IHDR(png_ptr, info_ptr, size, ysize, bit_depth, color_type,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (color_type & PNG_COLOR_MASK_PALETTE)
+ {
+ int npalette;
+ png_color palette[256];
+ png_byte trans[256];
+
+ npalette = generate_palette(palette, trans, bit_depth, gamma_table,
+ colors);
+ png_set_PLTE(png_ptr, info_ptr, palette, npalette);
+
+ if (tRNS)
+ png_set_tRNS(png_ptr, info_ptr, trans, npalette-1,
+ NULL/*transparent color*/);
+
+ /* Reset gamma_table to prevent the image rows being changed */
+ for (npalette=0; npalette<256; ++npalette)
+ gamma_table[npalette] = (png_byte)npalette;
+ }
+
+ else if (tRNS)
+ {
+ png_color_16 col;
+
+ col.red = col.green = col.blue = col.gray =
+ 0x0101U & ((1U<<bit_depth)-1U);
+ col.index = 0U;
+ png_set_tRNS(png_ptr, info_ptr, NULL/*trans*/, 1U, &col);
+ }
+
+ if (gamma == PNG_DEFAULT_sRGB)
+ png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
+
+ else if (gamma > 0) /* Else don't set color space information */
+ {
+ png_set_gAMA_fixed(png_ptr, info_ptr, real_gamma);
+
+ /* Just use the sRGB values here. */
+ png_set_cHRM_fixed(png_ptr, info_ptr,
+ /* color x y */
+ /* white */ 31270, 32900,
+ /* red */ 64000, 33000,
+ /* green */ 30000, 60000,
+ /* blue */ 15000, 6000
+ );
+ }
+
+ /* Insert extra information. */
+ while (insert != NULL)
+ {
+ insert->insert(png_ptr, info_ptr, insert->nparams, insert->parameters);
+ insert = insert->next;
+ }
+
+ /* Write the file header. */
+ png_write_info(png_ptr, info_ptr);
+
+ /* Restrict the filters */
+ png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters);
+
+ {
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ int passes = png_set_interlace_handling(png_ptr);
+# else /* !WRITE_INTERLACING */
+ int passes = 1;
+# endif /* !WRITE_INTERLACING */
+ int pass;
+ png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ row = malloc(rowbytes);
+
+ if (row == NULL)
+ png_error(png_ptr, "OOM allocating row buffer");
+
+ for (pass = 0; pass < passes; ++pass)
+ {
+ unsigned int y;
+
+ for (y=0; y<ysize; ++y)
+ {
+ unsigned int row_filters =
+ generate_row(row, rowbytes, y, color_type, bit_depth,
+ gamma_table, conv, colors, small);
+
+ if (row_filters != 0 && filters == PNG_ALL_FILTERS)
+ png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, row_filters);
+
+ png_write_row(png_ptr, row);
+ }
+ }
+ }
+ }
+
+ /* Finish writing the file. */
+ png_write_end(png_ptr, info_ptr);
+
+ {
+ png_structp nv_ptr = png_ptr;
+ png_infop nv_info = info_ptr;
+
+ png_ptr = NULL;
+ info_ptr = NULL;
+ png_destroy_write_struct(&nv_ptr, &nv_info);
+ }
+ free(row);
+ return 0;
+}
+
+
+static size_t
+load_file(png_const_charp name, png_bytepp result)
+{
+ FILE *fp = tmpfile();
+
+ if (fp != NULL)
+ {
+ FILE *ip = fopen(name, "rb");
+
+ if (ip != NULL)
+ {
+ size_t total = 0;
+ int ch;
+
+ for (;;)
+ {
+ ch = getc(ip);
+ if (ch == EOF) break;
+ putc(ch, fp);
+ ++total;
+ }
+
+ if (ferror(ip))
+ {
+ perror(name);
+ fprintf(stderr, "%s: read error\n", name);
+ (void)fclose(ip);
+ }
+
+ else
+ {
+ (void)fclose(ip);
+
+ if (ferror(fp))
+ {
+ perror("temporary file");
+ fprintf(stderr, "temporary file write error\n");
+ }
+
+ else
+ {
+ rewind(fp);
+
+ if (total > 0)
+ {
+ /* Round up to a multiple of 4 here to allow an iCCP profile
+ * to be padded to a 4x boundary.
+ */
+ png_bytep data = malloc((total+3)&~3);
+
+ if (data != NULL)
+ {
+ size_t new_size = 0;
+
+ for (;;)
+ {
+ ch = getc(fp);
+ if (ch == EOF) break;
+ data[new_size++] = (png_byte)ch;
+ }
+
+ if (ferror(fp) || new_size != total)
+ {
+ perror("temporary file");
+ fprintf(stderr, "temporary file read error\n");
+ free(data);
+ }
+
+ else
+ {
+ (void)fclose(fp);
+ *result = data;
+ return total;
+ }
+ }
+
+ else
+ fprintf(stderr, "%s: out of memory loading file\n", name);
+ }
+
+ else
+ fprintf(stderr, "%s: empty file\n", name);
+ }
+ }
+ }
+
+ else
+ {
+ perror(name);
+ fprintf(stderr, "%s: open failed\n", name);
+ }
+
+ fclose(fp);
+ }
+
+ else
+ fprintf(stderr, "makepng: %s: could not open temporary file\n", name);
+
+ exit(1);
+ return 0;
+}
+
+static png_size_t
+load_fake(png_charp param, png_bytepp profile)
+{
+ char *endptr = NULL;
+ uint64_t size = strtoull(param, &endptr, 0/*base*/);
+
+ /* The 'fake' format is <number>*[string] */
+ if (endptr != NULL && *endptr == '*')
+ {
+ size_t len = strlen(++endptr);
+ size_t result = (size_t)size;
+
+ if (len == 0) len = 1; /* capture the terminating '\0' */
+
+ /* Now repeat that string to fill 'size' bytes. */
+ if (result == size && (*profile = malloc(result)) != NULL)
+ {
+ png_bytep out = *profile;
+
+ if (len == 1)
+ memset(out, *endptr, result);
+
+ else
+ {
+ while (size >= len)
+ {
+ memcpy(out, endptr, len);
+ out += len;
+ size -= len;
+ }
+ memcpy(out, endptr, size);
+ }
+
+ return result;
+ }
+
+ else
+ {
+ fprintf(stderr, "%s: size exceeds system limits\n", param);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+static void
+check_param_count(int nparams, int expect)
+{
+ if (nparams != expect)
+ {
+ fprintf(stderr, "bad parameter count (internal error)\n");
+ exit(1);
+ }
+}
+
+static void
+insert_iCCP(png_structp png_ptr, png_infop info_ptr, int nparams,
+ png_charpp params)
+{
+ png_bytep profile = NULL;
+ png_uint_32 proflen = 0;
+ int result;
+
+ check_param_count(nparams, 2);
+
+ switch (params[1][0])
+ {
+ case '<':
+ {
+ png_size_t filelen = load_file(params[1]+1, &profile);
+ if (filelen > 0xfffffffc) /* Maximum profile length */
+ {
+ fprintf(stderr, "%s: file too long (%lu) for an ICC profile\n",
+ params[1]+1, (unsigned long)filelen);
+ exit(1);
+ }
+
+ proflen = (png_uint_32)filelen;
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ png_size_t fake_len = load_fake(params[1], &profile);
+
+ if (fake_len > 0) /* else a simple parameter */
+ {
+ if (fake_len > 0xffffffff) /* Maximum profile length */
+ {
+ fprintf(stderr,
+ "%s: fake data too long (%lu) for an ICC profile\n",
+ params[1], (unsigned long)fake_len);
+ exit(1);
+ }
+ proflen = (png_uint_32)(fake_len & ~3U);
+ /* Always fix up the profile length. */
+ png_save_uint_32(profile, proflen);
+ break;
+ }
+ }
+
+ default:
+ fprintf(stderr, "--insert iCCP \"%s\": unrecognized\n", params[1]);
+ fprintf(stderr, " use '<' to read a file: \"<filename\"\n");
+ exit(1);
+ }
+
+ result = 1;
+
+ if (proflen & 3)
+ {
+ fprintf(stderr,
+ "makepng: --insert iCCP %s: profile length made a multiple of 4\n",
+ params[1]);
+
+ /* load_file allocates extra space for this padding, the ICC spec requires
+ * padding with zero bytes.
+ */
+ while (proflen & 3)
+ profile[proflen++] = 0;
+ }
+
+ if (profile != NULL && proflen > 3)
+ {
+ png_uint_32 prof_header = png_get_uint_32(profile);
+
+ if (prof_header != proflen)
+ {
+ fprintf(stderr, "--insert iCCP %s: profile length field wrong:\n",
+ params[1]);
+ fprintf(stderr, " actual %lu, recorded value %lu (corrected)\n",
+ (unsigned long)proflen, (unsigned long)prof_header);
+ png_save_uint_32(profile, proflen);
+ }
+ }
+
+ if (result && profile != NULL && proflen >=4)
+ png_set_iCCP(png_ptr, info_ptr, params[0], PNG_COMPRESSION_TYPE_BASE,
+ profile, proflen);
+
+ if (profile)
+ free(profile);
+
+ if (!result)
+ exit(1);
+}
+
+static void
+clear_text(png_text *text, png_charp keyword)
+{
+ text->compression = -1; /* none */
+ text->key = keyword;
+ text->text = NULL;
+ text->text_length = 0; /* libpng calculates this */
+ text->itxt_length = 0; /* libpng calculates this */
+ text->lang = NULL;
+ text->lang_key = NULL;
+}
+
+static void
+set_text(png_structp png_ptr, png_infop info_ptr, png_textp text,
+ png_charp param)
+{
+ switch (param[0])
+ {
+ case '<':
+ {
+ png_bytep file = NULL;
+
+ text->text_length = load_file(param+1, &file);
+ text->text = (png_charp)file;
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ png_bytep data = NULL;
+ png_size_t fake_len = load_fake(param, &data);
+
+ if (fake_len > 0) /* else a simple parameter */
+ {
+ text->text_length = fake_len;
+ text->text = (png_charp)data;
+ break;
+ }
+ }
+
+ default:
+ text->text = param;
+ break;
+ }
+
+ png_set_text(png_ptr, info_ptr, text, 1);
+
+ if (text->text != param)
+ free(text->text);
+}
+
+static void
+insert_tEXt(png_structp png_ptr, png_infop info_ptr, int nparams,
+ png_charpp params)
+{
+ png_text text;
+
+ check_param_count(nparams, 2);
+ clear_text(&text, params[0]);
+ set_text(png_ptr, info_ptr, &text, params[1]);
+}
+
+static void
+insert_zTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
+ png_charpp params)
+{
+ png_text text;
+
+ check_param_count(nparams, 2);
+ clear_text(&text, params[0]);
+ text.compression = 0; /* deflate */
+ set_text(png_ptr, info_ptr, &text, params[1]);
+}
+
+static void
+insert_iTXt(png_structp png_ptr, png_infop info_ptr, int nparams,
+ png_charpp params)
+{
+ png_text text;
+
+ check_param_count(nparams, 4);
+ clear_text(&text, params[0]);
+ text.compression = 2; /* iTXt + deflate */
+ text.lang = params[1];/* language tag */
+ text.lang_key = params[2]; /* translated keyword */
+ set_text(png_ptr, info_ptr, &text, params[3]);
+}
+
+static void
+insert_hIST(png_structp png_ptr, png_infop info_ptr, int nparams,
+ png_charpp params)
+{
+ int i;
+ png_uint_16 freq[256];
+
+ /* libpng takes the count from the PLTE count; we don't check it here but we
+ * do set the array to 0 for unspecified entries.
+ */
+ memset(freq, 0, sizeof freq);
+ for (i=0; i<nparams; ++i)
+ {
+ char *endptr = NULL;
+ unsigned long int l = strtoul(params[i], &endptr, 0/*base*/);
+
+ if (params[i][0] && *endptr == 0 && l <= 65535)
+ freq[i] = (png_uint_16)l;
+
+ else
+ {
+ fprintf(stderr, "hIST[%d]: %s: invalid frequency\n", i, params[i]);
+ exit(1);
+ }
+ }
+
+ png_set_hIST(png_ptr, info_ptr, freq);
+}
+
+static png_byte
+bval(png_const_structrp png_ptr, png_charp param, unsigned int maxval)
+{
+ char *endptr = NULL;
+ unsigned long int l = strtoul(param, &endptr, 0/*base*/);
+
+ if (param[0] && *endptr == 0 && l <= maxval)
+ return (png_byte)l;
+
+ else
+ png_error(png_ptr, "sBIT: invalid sBIT value");
+}
+
+static void
+insert_sBIT(png_structp png_ptr, png_infop info_ptr, int nparams,
+ png_charpp params)
+{
+ const int ct = png_get_color_type(png_ptr, info_ptr);
+ const int c = (ct & PNG_COLOR_MASK_COLOR ? 3 : 1) +
+ (ct & PNG_COLOR_MASK_ALPHA ? 1 : 0);
+ const unsigned int maxval =
+ ct & PNG_COLOR_MASK_PALETTE ? 8U : png_get_bit_depth(png_ptr, info_ptr);
+ png_color_8 sBIT;
+
+ if (nparams != c)
+ png_error(png_ptr, "sBIT: incorrect parameter count");
+
+ if (ct & PNG_COLOR_MASK_COLOR)
+ {
+ sBIT.red = bval(png_ptr, params[0], maxval);
+ sBIT.green = bval(png_ptr, params[1], maxval);
+ sBIT.blue = bval(png_ptr, params[2], maxval);
+ sBIT.gray = 42;
+ }
+
+ else
+ {
+ sBIT.red = sBIT.green = sBIT.blue = 42;
+ sBIT.gray = bval(png_ptr, params[0], maxval);
+ }
+
+ if (ct & PNG_COLOR_MASK_ALPHA)
+ sBIT.alpha = bval(png_ptr, params[nparams-1], maxval);
+
+ else
+ sBIT.alpha = 42;
+
+ png_set_sBIT(png_ptr, info_ptr, &sBIT);
+}
+
+#if 0
+static void
+insert_sPLT(png_structp png_ptr, png_infop info_ptr, int nparams, png_charpp params)
+{
+ fprintf(stderr, "insert sPLT: NYI\n");
+}
+#endif
+
+static int
+find_parameters(png_const_charp what, png_charp param, png_charp *list,
+ int nparams)
+{
+ /* Parameters are separated by '\n' or ':' characters, up to nparams are
+ * accepted (more is an error) and the number found is returned.
+ */
+ int i;
+ for (i=0; *param && i<nparams; ++i)
+ {
+ list[i] = param;
+ while (*++param) if (*param == '\n' || *param == ':')
+ {
+ *param++ = 0; /* Terminate last parameter */
+ break; /* And start a new one. */
+ }
+ }
+
+ if (*param)
+ {
+ fprintf(stderr, "--insert %s: too many parameters (%s)\n", what, param);
+ exit(1);
+ }
+
+ list[i] = NULL; /* terminates list */
+ return i; /* number of parameters filled in */
+}
+
+static void
+bad_parameter_count(png_const_charp what, int nparams)
+{
+ fprintf(stderr, "--insert %s: bad parameter count %d\n", what, nparams);
+ exit(1);
+}
+
+static chunk_insert *
+make_insert(png_const_charp what,
+ void (*insert)(png_structp, png_infop, int, png_charpp),
+ int nparams, png_charpp list)
+{
+ int i;
+ chunk_insert *cip;
+
+ cip = malloc(offsetof(chunk_insert,parameters) +
+ nparams * sizeof (png_charp));
+
+ if (cip == NULL)
+ {
+ fprintf(stderr, "--insert %s: out of memory allocating %d parameters\n",
+ what, nparams);
+ exit(1);
+ }
+
+ cip->next = NULL;
+ cip->insert = insert;
+ cip->nparams = nparams;
+ for (i=0; i<nparams; ++i)
+ cip->parameters[i] = list[i];
+
+ return cip;
+}
+
+static chunk_insert *
+find_insert(png_const_charp what, png_charp param)
+{
+ png_uint_32 chunk = 0;
+ png_charp parameter_list[1024];
+ int i, nparams;
+
+ /* Assemble the chunk name */
+ for (i=0; i<4; ++i)
+ {
+ char ch = what[i];
+
+ if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))
+ chunk = (chunk << 8) + what[i];
+
+ else
+ break;
+ }
+
+ if (i < 4 || what[4] != 0)
+ {
+ fprintf(stderr, "makepng --insert \"%s\": invalid chunk name\n", what);
+ exit(1);
+ }
+
+ /* Assemble the parameter list. */
+ nparams = find_parameters(what, param, parameter_list, 1024);
+
+# define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+
+ switch (chunk)
+ {
+ case CHUNK(105,67,67,80): /* iCCP */
+ if (nparams == 2)
+ return make_insert(what, insert_iCCP, nparams, parameter_list);
+ break;
+
+ case CHUNK(116,69,88,116): /* tEXt */
+ if (nparams == 2)
+ return make_insert(what, insert_tEXt, nparams, parameter_list);
+ break;
+
+ case CHUNK(122,84,88,116): /* zTXt */
+ if (nparams == 2)
+ return make_insert(what, insert_zTXt, nparams, parameter_list);
+ break;
+
+ case CHUNK(105,84,88,116): /* iTXt */
+ if (nparams == 4)
+ return make_insert(what, insert_iTXt, nparams, parameter_list);
+ break;
+
+ case CHUNK(104,73,83,84): /* hIST */
+ if (nparams <= 256)
+ return make_insert(what, insert_hIST, nparams, parameter_list);
+ break;
+
+ case CHUNK(115,66,73,84): /* sBIT */
+ if (nparams <= 4)
+ return make_insert(what, insert_sBIT, nparams, parameter_list);
+ break;
+
+#if 0
+ case CHUNK(115,80,76,84): /* sPLT */
+ return make_insert(what, insert_sPLT, nparams, parameter_list);
+#endif
+
+ default:
+ fprintf(stderr, "makepng --insert \"%s\": unrecognized chunk name\n",
+ what);
+ exit(1);
+ }
+
+ bad_parameter_count(what, nparams);
+ return NULL;
+}
+
+/* This is necessary because libpng expects writeable strings for things like
+ * text chunks (maybe this should be fixed...)
+ */
+static png_charp
+strstash(png_const_charp foo)
+{
+ /* The program indicates a memory allocation error by crashing, this is by
+ * design.
+ */
+ if (foo != NULL)
+ {
+ png_charp bar = malloc(strlen(foo)+1);
+ return strcpy(bar, foo);
+ }
+
+ return NULL;
+}
+
+static png_charp
+strstash_list(const png_const_charp *text)
+{
+ size_t foo = 0;
+ png_charp result, bar;
+ const png_const_charp *line = text;
+
+ while (*line != NULL)
+ foo += strlen(*line++);
+
+ result = bar = malloc(foo+1);
+
+ line = text;
+ while (*line != NULL)
+ {
+ foo = strlen(*line);
+ memcpy(bar, *line++, foo);
+ bar += foo;
+ }
+
+ *bar = 0;
+ return result;
+}
+
+/* These are used to insert Copyright and Licence fields, they allow the text to
+ * have \n unlike the --insert option.
+ */
+static chunk_insert *
+add_tEXt(const char *key, const png_const_charp *text)
+{
+ static char what[5] = { 116, 69, 88, 116, 0 };
+ png_charp parameter_list[3];
+
+ parameter_list[0] = strstash(key);
+ parameter_list[1] = strstash_list(text);
+ parameter_list[2] = NULL;
+
+ return make_insert(what, insert_tEXt, 2, parameter_list);
+}
+
+static chunk_insert *
+add_iTXt(const char *key, const char *language, const char *language_key,
+ const png_const_charp *text)
+{
+ static char what[5] = { 105, 84, 88, 116, 0 };
+ png_charp parameter_list[5];
+
+ parameter_list[0] = strstash(key);
+ parameter_list[1] = strstash(language);
+ parameter_list[2] = strstash(language_key);
+ parameter_list[3] = strstash_list(text);
+ parameter_list[4] = NULL;
+
+ return make_insert(what, insert_iTXt, 4, parameter_list);
+}
+
+/* This is a not-very-good parser for a sequence of numbers (including 0). It
+ * doesn't accept some apparently valid things, but it accepts all the sensible
+ * combinations.
+ */
+static void
+parse_color(char *arg, unsigned int *colors)
+{
+ unsigned int ncolors = 0;
+
+ while (*arg && ncolors < 4)
+ {
+ char *ep = arg;
+
+ unsigned long ul = strtoul(arg, &ep, 0);
+
+ if (ul > 65535)
+ {
+ fprintf(stderr, "makepng --color=...'%s': too big\n", arg);
+ exit(1);
+ }
+
+ if (ep == arg)
+ {
+ fprintf(stderr, "makepng --color=...'%s': not a valid color\n", arg);
+ exit(1);
+ }
+
+ if (*ep) ++ep; /* skip a separator */
+ arg = ep;
+
+ colors[++ncolors] = (unsigned int)ul; /* checked above */
+ }
+
+ if (*arg)
+ {
+ fprintf(stderr, "makepng --color=...'%s': too many values\n", arg);
+ exit(1);
+ }
+
+ *colors = ncolors;
+}
+
+int
+main(int argc, char **argv)
+{
+ FILE *fp = stdout;
+ const char *file_name = NULL;
+ int color_type = 8; /* invalid */
+ int bit_depth = 32; /* invalid */
+ int small = 0; /* make full size images */
+ int tRNS = 0; /* don't output a tRNS chunk */
+ unsigned int colors[5];
+ unsigned int filters = PNG_ALL_FILTERS;
+ png_fixed_point gamma = 0; /* not set */
+ chunk_insert *head_insert = NULL;
+ chunk_insert **insert_ptr = &head_insert;
+
+ memset(colors, 0, sizeof colors);
+
+ while (--argc > 0)
+ {
+ char *arg = *++argv;
+
+ if (strcmp(arg, "--small") == 0)
+ {
+ small = 1;
+ continue;
+ }
+
+ if (strcmp(arg, "--tRNS") == 0)
+ {
+ tRNS = 1;
+ continue;
+ }
+
+ if (strcmp(arg, "--sRGB") == 0)
+ {
+ gamma = PNG_DEFAULT_sRGB;
+ continue;
+ }
+
+ if (strcmp(arg, "--linear") == 0)
+ {
+ gamma = PNG_FP_1;
+ continue;
+ }
+
+ if (strcmp(arg, "--1.8") == 0)
+ {
+ gamma = PNG_GAMMA_MAC_18;
+ continue;
+ }
+
+ if (strcmp(arg, "--nofilters") == 0)
+ {
+ filters = PNG_FILTER_NONE;
+ continue;
+ }
+
+ if (strncmp(arg, "--color=", 8) == 0)
+ {
+ parse_color(arg+8, colors);
+ continue;
+ }
+
+ if (argc >= 3 && strcmp(arg, "--insert") == 0)
+ {
+ png_const_charp what = *++argv;
+ png_charp param = *++argv;
+ chunk_insert *new_insert;
+
+ argc -= 2;
+
+ new_insert = find_insert(what, param);
+
+ if (new_insert != NULL)
+ {
+ *insert_ptr = new_insert;
+ insert_ptr = &new_insert->next;
+ }
+
+ continue;
+ }
+
+ if (arg[0] == '-')
+ {
+ fprintf(stderr, "makepng: %s: invalid option\n", arg);
+ exit(1);
+ }
+
+ if (strcmp(arg, "palette") == 0)
+ {
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ continue;
+ }
+
+ if (strncmp(arg, "gray", 4) == 0)
+ {
+ if (arg[4] == 0)
+ {
+ color_type = PNG_COLOR_TYPE_GRAY;
+ continue;
+ }
+
+ else if (strcmp(arg+4, "a") == 0 ||
+ strcmp(arg+4, "alpha") == 0 ||
+ strcmp(arg+4, "-alpha") == 0)
+ {
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ continue;
+ }
+ }
+
+ if (strncmp(arg, "rgb", 3) == 0)
+ {
+ if (arg[3] == 0)
+ {
+ color_type = PNG_COLOR_TYPE_RGB;
+ continue;
+ }
+
+ else if (strcmp(arg+3, "a") == 0 ||
+ strcmp(arg+3, "alpha") == 0 ||
+ strcmp(arg+3, "-alpha") == 0)
+ {
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ continue;
+ }
+ }
+
+ if (color_type == 8 && isdigit(arg[0]))
+ {
+ color_type = atoi(arg);
+ if (color_type < 0 || color_type > 6 || color_type == 1 ||
+ color_type == 5)
+ {
+ fprintf(stderr, "makepng: %s: not a valid color type\n", arg);
+ exit(1);
+ }
+
+ continue;
+ }
+
+ if (bit_depth == 32 && isdigit(arg[0]))
+ {
+ bit_depth = atoi(arg);
+ if (bit_depth <= 0 || bit_depth > 16 ||
+ (bit_depth & -bit_depth) != bit_depth)
+ {
+ fprintf(stderr, "makepng: %s: not a valid bit depth\n", arg);
+ exit(1);
+ }
+
+ continue;
+ }
+
+ if (argc == 1) /* It's the file name */
+ {
+ fp = fopen(arg, "wb");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "%s: %s: could not open\n", arg, strerror(errno));
+ exit(1);
+ }
+
+ file_name = arg;
+ continue;
+ }
+
+ fprintf(stderr, "makepng: %s: unknown argument\n", arg);
+ exit(1);
+ } /* argument while loop */
+
+ if (color_type == 8 || bit_depth == 32)
+ {
+ fprintf(stderr, "usage: makepng [--small] [--sRGB|--linear|--1.8] "
+ "[--color=...] color-type bit-depth [file-name]\n"
+ " Make a test PNG file, by default writes to stdout.\n"
+ " Other options are available, UTSL.\n");
+ exit(1);
+ }
+
+ /* Check the colors */
+ {
+ const unsigned int lim = (color_type == PNG_COLOR_TYPE_PALETTE ? 255U :
+ (1U<<bit_depth)-1);
+ unsigned int i;
+
+ for (i=1; i<=colors[0]; ++i)
+ if (colors[i] > lim)
+ {
+ fprintf(stderr, "makepng: --color=...: %u out of range [0..%u]\n",
+ colors[i], lim);
+ exit(1);
+ }
+ }
+
+ /* small and colors are incomparible (will probably crash if both are used at
+ * the same time!)
+ */
+ if (small && colors[0] != 0)
+ {
+ fprintf(stderr, "makepng: --color --small: only one at a time!\n");
+ exit(1);
+ }
+
+ /* Restrict the filters for more speed to those we know are used for the
+ * generated images.
+ */
+ if (filters == PNG_ALL_FILTERS && !small/*small provides defaults*/)
+ {
+ if ((color_type & PNG_COLOR_MASK_PALETTE) != 0 || bit_depth < 8)
+ filters = PNG_FILTER_NONE;
+
+ else if (color_type & PNG_COLOR_MASK_COLOR) /* rgb */
+ {
+ if (bit_depth == 8)
+ filters &= ~(PNG_FILTER_NONE | PNG_FILTER_AVG);
+
+ else
+ filters = PNG_FILTER_SUB | PNG_FILTER_PAETH;
+ }
+
+ else /* gray 8 or 16-bit */
+ filters &= ~PNG_FILTER_NONE;
+ }
+
+ /* Insert standard copyright and licence text. */
+ {
+ static png_const_charp copyright[] =
+ {
+ COPYRIGHT, /* ISO-Latin-1 */
+ NULL
+ };
+ static png_const_charp licensing[] =
+ {
+ IMAGE_LICENSING, /* UTF-8 */
+ NULL
+ };
+
+ chunk_insert *new_insert;
+
+ new_insert = add_tEXt("Copyright", copyright);
+ if (new_insert != NULL)
+ {
+ *insert_ptr = new_insert;
+ insert_ptr = &new_insert->next;
+ }
+
+ new_insert = add_iTXt("Licensing", "en", NULL, licensing);
+ if (new_insert != NULL)
+ {
+ *insert_ptr = new_insert;
+ insert_ptr = &new_insert->next;
+ }
+ }
+
+ {
+ int ret = write_png(&file_name, fp, color_type, bit_depth, gamma,
+ head_insert, filters, colors, small, tRNS);
+
+ if (ret != 0 && file_name != NULL)
+ remove(file_name);
+
+ return ret;
+ }
+}
diff --git a/contrib/libtests/pngimage.c b/contrib/libtests/pngimage.c
new file mode 100644
index 0000000..442b2f4
--- /dev/null
+++ b/contrib/libtests/pngimage.c
@@ -0,0 +1,1687 @@
+/* pngimage.c
+ *
+ * Copyright (c) 2015 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.20 [December 3, 2015]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Test the png_read_png and png_write_png interfaces. Given a PNG file load it
+ * using png_read_png and then write with png_write_png. Test all possible
+ * transforms.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+#ifndef PNG_SETJMP_SUPPORTED
+# include <setjmp.h> /* because png.h did *not* include this */
+#endif
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)\
+ && (defined(PNG_READ_PNG_SUPPORTED) || PNG_LIBPNG_VER < 10700)
+/* If a transform is valid on both read and write this implies that if the
+ * transform is applied to read it must also be applied on write to produce
+ * meaningful data. This is because these transforms when performed on read
+ * produce data with a memory format that does not correspond to a PNG format.
+ *
+ * Most of these transforms are invertible; after applying the transform on
+ * write the result is the original PNG data that would have would have been
+ * read if no transform were applied.
+ *
+ * The exception is _SHIFT, which destroys the low order bits marked as not
+ * significant in a PNG with the sBIT chunk.
+ *
+ * The following table lists, for each transform, the conditions under which it
+ * is expected to do anything. Conditions are defined as follows:
+ *
+ * 1) Color mask bits required - simply a mask to AND with color_type; one of
+ * these must be present for the transform to fire, except that 0 means
+ * 'always'.
+ * 2) Color mask bits which must be absent - another mask - none of these must
+ * be present.
+ * 3) Bit depths - a mask of component bit depths for the transform to fire.
+ * 4) 'read' - the transform works in png_read_png.
+ * 5) 'write' - the transform works in png_write_png.
+ * 6) PNG_INFO_chunk; a mask of the chunks that must be present for the
+ * transform to fire. All must be present - the requirement is that
+ * png_get_valid() & mask == mask, so if mask is 0 there is no requirement.
+ *
+ * The condition refers to the original image state - if multiple transforms are
+ * used together it is possible to cause a transform that wouldn't fire on the
+ * original image to fire.
+ */
+static struct transform_info
+{
+ const char *name;
+ int transform;
+ png_uint_32 valid_chunks;
+# define CHUNK_NONE 0
+# define CHUNK_sBIT PNG_INFO_sBIT
+# define CHUNK_tRNS PNG_INFO_tRNS
+ png_byte color_mask_required;
+ png_byte color_mask_absent;
+# define COLOR_MASK_X 0
+# define COLOR_MASK_P PNG_COLOR_MASK_PALETTE
+# define COLOR_MASK_C PNG_COLOR_MASK_COLOR
+# define COLOR_MASK_A PNG_COLOR_MASK_ALPHA
+# define COLOR_MASK_ALL (PALETTE+COLOR+ALPHA) /* absent = gray, no alpha */
+ png_byte bit_depths;
+# define BD_ALL (1 + 2 + 4 + 8 + 16)
+# define BD_PAL (1 + 2 + 4 + 8)
+# define BD_LOW (1 + 2 + 4)
+# define BD_16 16
+# define BD_TRUE (8+16) /* i.e. true-color depths */
+ png_byte when;
+# define TRANSFORM_R 1
+# define TRANSFORM_W 2
+# define TRANSFORM_RW 3
+ png_byte tested; /* the transform was tested somewhere */
+} transform_info[] =
+{
+ /* List ALL the PNG_TRANSFORM_ macros here. Check for support using the READ
+ * macros; even if the transform is supported on write it cannot be tested
+ * without the read support.
+ */
+# define T(name,chunk,cm_required,cm_absent,bd,when)\
+ { #name, PNG_TRANSFORM_ ## name, CHUNK_ ## chunk,\
+ COLOR_MASK_ ## cm_required, COLOR_MASK_ ## cm_absent, BD_ ## bd,\
+ TRANSFORM_ ## when, 0/*!tested*/ }
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+ T(STRIP_16, NONE, X, X, 16, R),
+ /* drops the bottom 8 bits when bit depth is 16 */
+#endif
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ T(STRIP_ALPHA, NONE, A, X, ALL, R),
+ /* removes the alpha channel if present */
+#endif
+#ifdef PNG_WRITE_PACK_SUPPORTED
+# define TRANSFORM_RW_PACK TRANSFORM_RW
+#else
+# define TRANSFORM_RW_PACK TRANSFORM_R
+#endif
+#ifdef PNG_READ_PACK_SUPPORTED
+ T(PACKING, NONE, X, X, LOW, RW_PACK),
+ /* unpacks low-bit-depth components into 1 byte per component on read,
+ * reverses this on write.
+ */
+#endif
+#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
+# define TRANSFORM_RW_PACKSWAP TRANSFORM_RW
+#else
+# define TRANSFORM_RW_PACKSWAP TRANSFORM_R
+#endif
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ T(PACKSWAP, NONE, X, X, LOW, RW_PACKSWAP),
+ /* reverses the order of low-bit-depth components packed into a byte */
+#endif
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ T(EXPAND, NONE, P, X, ALL, R),
+ /* expands PLTE PNG files to RGB (no tRNS) or RGBA (tRNS) *
+ * Note that the 'EXPAND' transform does lots of different things: */
+ T(EXPAND, NONE, X, C, ALL, R),
+ /* expands grayscale PNG files to RGB, or RGBA */
+ T(EXPAND, tRNS, X, A, ALL, R),
+ /* expands the tRNS chunk in files without alpha */
+#endif
+#ifdef PNG_WRITE_INVERT_SUPPORTED
+# define TRANSFORM_RW_INVERT TRANSFORM_RW
+#else
+# define TRANSFORM_RW_INVERT TRANSFORM_R
+#endif
+#ifdef PNG_READ_INVERT_SUPPORTED
+ T(INVERT_MONO, NONE, X, C, ALL, RW_INVERT),
+ /* converts gray-scale components to 1..0 from 0..1 */
+#endif
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+# define TRANSFORM_RW_SHIFT TRANSFORM_RW
+#else
+# define TRANSFORM_RW_SHIFT TRANSFORM_R
+#endif
+#ifdef PNG_READ_SHIFT_SUPPORTED
+ T(SHIFT, sBIT, X, X, ALL, RW_SHIFT),
+ /* reduces component values to the original range based on the sBIT chunk,
+ * this is only partially reversible - the low bits are lost and cannot be
+ * recovered on write. In fact write code replicates the bits to generate
+ * new low-order bits.
+ */
+#endif
+#ifdef PNG_WRITE_BGR_SUPPORTED
+# define TRANSFORM_RW_BGR TRANSFORM_RW
+#else
+# define TRANSFORM_RW_BGR TRANSFORM_R
+#endif
+#ifdef PNG_READ_BGR_SUPPORTED
+ T(BGR, NONE, C, P, TRUE, RW_BGR),
+ /* reverses the rgb component values of true-color pixels */
+#endif
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+# define TRANSFORM_RW_SWAP_ALPHA TRANSFORM_RW
+#else
+# define TRANSFORM_RW_SWAP_ALPHA TRANSFORM_R
+#endif
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+ T(SWAP_ALPHA, NONE, A, X, TRUE, RW_SWAP_ALPHA),
+ /* swaps the alpha channel of RGBA or GA pixels to the front - ARGB or
+ * AG, on write reverses the process.
+ */
+#endif
+#ifdef PNG_WRITE_SWAP_SUPPORTED
+# define TRANSFORM_RW_SWAP TRANSFORM_RW
+#else
+# define TRANSFORM_RW_SWAP TRANSFORM_R
+#endif
+#ifdef PNG_READ_SWAP_SUPPORTED
+ T(SWAP_ENDIAN, NONE, X, P, 16, RW_SWAP),
+ /* byte-swaps 16-bit component values */
+#endif
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+# define TRANSFORM_RW_INVERT_ALPHA TRANSFORM_RW
+#else
+# define TRANSFORM_RW_INVERT_ALPHA TRANSFORM_R
+#endif
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+ T(INVERT_ALPHA, NONE, A, X, TRUE, RW_INVERT_ALPHA),
+ /* converts an alpha channel from 0..1 to 1..0 */
+#endif
+#ifdef PNG_WRITE_FILLER_SUPPORTED
+ T(STRIP_FILLER_BEFORE, NONE, A, P, TRUE, W), /* 'A' for a filler! */
+ /* on write skips a leading filler channel; testing requires data with a
+ * filler channel so this is produced from RGBA or GA images by removing
+ * the 'alpha' flag from the color type in place.
+ */
+ T(STRIP_FILLER_AFTER, NONE, A, P, TRUE, W),
+ /* on write strips a trailing filler channel */
+#endif
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ T(GRAY_TO_RGB, NONE, X, C, ALL, R),
+ /* expands grayscale images to RGB, also causes the palette part of
+ * 'EXPAND' to happen. Low bit depth grayscale images are expanded to
+ * 8-bits per component and no attempt is made to convert the image to a
+ * palette image. While this transform is partially reversible
+ * png_write_png does not currently support this.
+ */
+ T(GRAY_TO_RGB, NONE, P, X, ALL, R),
+ /* The 'palette' side effect mentioned above; a bit bogus but this is the
+ * way the libpng code works.
+ */
+#endif
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+ T(EXPAND_16, NONE, X, X, PAL, R),
+ /* expands images to 16-bits per component, as a side effect expands
+ * palette images to RGB and expands the tRNS chunk if present, so it can
+ * modify 16-bit per component images as well:
+ */
+ T(EXPAND_16, tRNS, X, A, 16, R),
+ /* side effect of EXPAND_16 - expands the tRNS chunk in an RGB or G 16-bit
+ * image.
+ */
+#endif
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ T(SCALE_16, NONE, X, X, 16, R),
+ /* scales 16-bit components to 8-bits. */
+#endif
+
+ { NULL /*name*/, 0, 0, 0, 0, 0, 0, 0/*!tested*/ }
+
+#undef T
+};
+
+#define ARRAY_SIZE(a) ((sizeof a)/(sizeof a[0]))
+#define TTABLE_SIZE ARRAY_SIZE(transform_info)
+
+/* Some combinations of options that should be reversible are not; these cases
+ * are bugs.
+ */
+static int known_bad_combos[][2] =
+{
+ /* problem, antidote */
+ { PNG_TRANSFORM_SHIFT | PNG_TRANSFORM_INVERT_ALPHA, 0/*antidote*/ }
+};
+
+static int
+is_combo(int transforms)
+{
+ return transforms & (transforms-1); /* non-zero if more than one set bit */
+}
+
+static int
+first_transform(int transforms)
+{
+ return transforms & -transforms; /* lowest set bit */
+}
+
+static int
+is_bad_combo(int transforms)
+{
+ unsigned int i;
+
+ for (i=0; i<ARRAY_SIZE(known_bad_combos); ++i)
+ {
+ int combo = known_bad_combos[i][0];
+
+ if ((combo & transforms) == combo &&
+ (transforms & known_bad_combos[i][1]) == 0)
+ return 1;
+ }
+
+ return 0; /* combo is ok */
+}
+
+static const char *
+transform_name(int t)
+ /* The name, if 't' has multiple bits set the name of the lowest set bit is
+ * returned.
+ */
+{
+ unsigned int i;
+
+ t &= -t; /* first set bit */
+
+ for (i=0; i<TTABLE_SIZE; ++i) if (transform_info[i].name != NULL)
+ {
+ if ((transform_info[i].transform & t) != 0)
+ return transform_info[i].name;
+ }
+
+ return "invalid transform";
+}
+
+/* Variables calculated by validate_T below and used to record all the supported
+ * transforms. Need (unsigned int) here because of the places where these
+ * values are used (unsigned compares in the 'exhaustive' iterator.)
+ */
+static unsigned int read_transforms, write_transforms, rw_transforms;
+
+static void
+validate_T(void)
+ /* Validate the above table - this just builds the above values */
+{
+ unsigned int i;
+
+ for (i=0; i<TTABLE_SIZE; ++i) if (transform_info[i].name != NULL)
+ {
+ if (transform_info[i].when & TRANSFORM_R)
+ read_transforms |= transform_info[i].transform;
+
+ if (transform_info[i].when & TRANSFORM_W)
+ write_transforms |= transform_info[i].transform;
+ }
+
+ /* Reversible transforms are those which are supported on both read and
+ * write.
+ */
+ rw_transforms = read_transforms & write_transforms;
+}
+
+/* FILE DATA HANDLING
+ * The original file is cached in memory. During write the output file is
+ * written to memory.
+ *
+ * In both cases the file data is held in a linked list of buffers - not all
+ * of these are in use at any time.
+ */
+#define NEW(type) ((type *)malloc(sizeof (type)))
+#define DELETE(ptr) (free(ptr))
+
+struct buffer_list
+{
+ struct buffer_list *next; /* next buffer in list */
+ png_byte buffer[1024]; /* the actual buffer */
+};
+
+struct buffer
+{
+ struct buffer_list *last; /* last buffer in use */
+ size_t end_count; /* bytes in the last buffer */
+ struct buffer_list *current; /* current buffer being read */
+ size_t read_count; /* count of bytes read from current */
+ struct buffer_list first; /* the very first buffer */
+};
+
+static void
+buffer_init(struct buffer *buffer)
+ /* Call this only once for a given buffer */
+{
+ buffer->first.next = NULL;
+ buffer->last = NULL;
+ buffer->current = NULL;
+}
+
+static void
+buffer_destroy_list(struct buffer_list *list)
+{
+ if (list != NULL)
+ {
+ struct buffer_list *next = list->next;
+ DELETE(list);
+ buffer_destroy_list(next);
+ }
+}
+
+static void
+buffer_destroy(struct buffer *buffer)
+{
+ struct buffer_list *list = buffer->first.next;
+ buffer_init(buffer);
+ buffer_destroy_list(list);
+}
+
+#ifdef PNG_WRITE_SUPPORTED
+static void
+buffer_start_write(struct buffer *buffer)
+{
+ buffer->last = &buffer->first;
+ buffer->end_count = 0;
+ buffer->current = NULL;
+}
+#endif
+
+static void
+buffer_start_read(struct buffer *buffer)
+{
+ buffer->current = &buffer->first;
+ buffer->read_count = 0;
+}
+
+#ifdef ENOMEM /* required by POSIX 1003.1 */
+# define MEMORY ENOMEM
+#else
+# define MEMORY ERANGE /* required by ANSI-C */
+#endif
+static struct buffer *
+get_buffer(png_structp pp)
+ /* Used from libpng callbacks to get the current buffer */
+{
+ return (struct buffer*)png_get_io_ptr(pp);
+}
+
+static struct buffer_list *
+buffer_extend(struct buffer_list *current)
+{
+ struct buffer_list *add;
+
+ assert(current->next == NULL);
+
+ add = NEW(struct buffer_list);
+ if (add == NULL)
+ return NULL;
+
+ add->next = NULL;
+ current->next = add;
+
+ return add;
+}
+
+/* Load a buffer from a file; does the equivalent of buffer_start_write. On a
+ * read error returns an errno value, else returns 0.
+ */
+static int
+buffer_from_file(struct buffer *buffer, FILE *fp)
+{
+ struct buffer_list *last = &buffer->first;
+ size_t count = 0;
+
+ for (;;)
+ {
+ size_t r = fread(last->buffer+count, 1/*size*/,
+ (sizeof last->buffer)-count, fp);
+
+ if (r > 0)
+ {
+ count += r;
+
+ if (count >= sizeof last->buffer)
+ {
+ assert(count == sizeof last->buffer);
+ count = 0;
+
+ if (last->next == NULL)
+ {
+ last = buffer_extend(last);
+ if (last == NULL)
+ return MEMORY;
+ }
+
+ else
+ last = last->next;
+ }
+ }
+
+ else /* fread failed - probably end of file */
+ {
+ if (feof(fp))
+ {
+ buffer->last = last;
+ buffer->end_count = count;
+ return 0; /* no error */
+ }
+
+ /* Some kind of funky error; errno should be non-zero */
+ return errno == 0 ? ERANGE : errno;
+ }
+ }
+}
+
+/* This structure is used to control the test of a single file. */
+typedef enum
+{
+ VERBOSE, /* switches on all messages */
+ INFORMATION,
+ WARNINGS, /* switches on warnings */
+ LIBPNG_WARNING,
+ APP_WARNING,
+ ERRORS, /* just errors */
+ APP_FAIL, /* continuable error - no need to longjmp */
+ LIBPNG_ERROR, /* this and higher cause a longjmp */
+ LIBPNG_BUG, /* erroneous behavior in libpng */
+ APP_ERROR, /* such as out-of-memory in a callback */
+ QUIET, /* no normal messages */
+ USER_ERROR, /* such as file-not-found */
+ INTERNAL_ERROR
+} error_level;
+#define LEVEL_MASK 0xf /* where the level is in 'options' */
+
+#define EXHAUSTIVE 0x010 /* Test all combinations of active options */
+#define STRICT 0x020 /* Fail on warnings as well as errors */
+#define LOG 0x040 /* Log pass/fail to stdout */
+#define CONTINUE 0x080 /* Continue on APP_FAIL errors */
+#define SKIP_BUGS 0x100 /* Skip over known bugs */
+#define LOG_SKIPPED 0x200 /* Log skipped bugs */
+#define FIND_BAD_COMBOS 0x400 /* Attempt to deduce bad combos */
+#define LIST_COMBOS 0x800 /* List combos by name */
+
+/* Result masks apply to the result bits in the 'results' field below; these
+ * bits are simple 1U<<error_level. A pass requires either nothing worse than
+ * warnings (--relaxes) or nothing worse than information (--strict)
+ */
+#define RESULT_STRICT(r) (((r) & ~((1U<<WARNINGS)-1)) == 0)
+#define RESULT_RELAXED(r) (((r) & ~((1U<<ERRORS)-1)) == 0)
+
+struct display
+{
+ jmp_buf error_return; /* Where to go to on error */
+
+ const char *filename; /* The name of the original file */
+ const char *operation; /* Operation being performed */
+ int transforms; /* Transform used in operation */
+ png_uint_32 options; /* See display_log below */
+ png_uint_32 results; /* A mask of errors seen */
+
+
+ png_structp original_pp; /* used on the original read */
+ png_infop original_ip; /* set by the original read */
+
+ png_size_t original_rowbytes; /* of the original rows: */
+ png_bytepp original_rows; /* from the original read */
+
+ /* Original chunks valid */
+ png_uint_32 chunks;
+
+ /* Original IHDR information */
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ int interlace_method;
+ int compression_method;
+ int filter_method;
+
+ /* Derived information for the original image. */
+ int active_transforms; /* transforms that do something on read */
+ int ignored_transforms; /* transforms that should do nothing */
+
+ /* Used on a read, both the original read and when validating a written
+ * image.
+ */
+ png_structp read_pp;
+ png_infop read_ip;
+
+# ifdef PNG_WRITE_SUPPORTED
+ /* Used to write a new image (the original info_ptr is used) */
+ png_structp write_pp;
+ struct buffer written_file; /* where the file gets written */
+# endif
+
+ struct buffer original_file; /* Data read from the original file */
+};
+
+static void
+display_init(struct display *dp)
+ /* Call this only once right at the start to initialize the control
+ * structure, the (struct buffer) lists are maintained across calls - the
+ * memory is not freed.
+ */
+{
+ memset(dp, 0, sizeof *dp);
+ dp->options = WARNINGS; /* default to !verbose, !quiet */
+ dp->filename = NULL;
+ dp->operation = NULL;
+ dp->original_pp = NULL;
+ dp->original_ip = NULL;
+ dp->original_rows = NULL;
+ dp->read_pp = NULL;
+ dp->read_ip = NULL;
+ buffer_init(&dp->original_file);
+
+# ifdef PNG_WRITE_SUPPORTED
+ dp->write_pp = NULL;
+ buffer_init(&dp->written_file);
+# endif
+}
+
+static void
+display_clean_read(struct display *dp)
+{
+ if (dp->read_pp != NULL)
+ png_destroy_read_struct(&dp->read_pp, &dp->read_ip, NULL);
+}
+
+#ifdef PNG_WRITE_SUPPORTED
+static void
+display_clean_write(struct display *dp)
+{
+ if (dp->write_pp != NULL)
+ png_destroy_write_struct(&dp->write_pp, NULL);
+}
+#endif
+
+static void
+display_clean(struct display *dp)
+{
+# ifdef PNG_WRITE_SUPPORTED
+ display_clean_write(dp);
+# endif
+ display_clean_read(dp);
+
+ dp->original_rowbytes = 0;
+ dp->original_rows = NULL;
+ dp->chunks = 0;
+
+ png_destroy_read_struct(&dp->original_pp, &dp->original_ip, NULL);
+ /* leave the filename for error detection */
+ dp->results = 0; /* reset for next time */
+}
+
+static void
+display_destroy(struct display *dp)
+{
+ /* Release any memory held in the display. */
+# ifdef PNG_WRITE_SUPPORTED
+ buffer_destroy(&dp->written_file);
+# endif
+
+ buffer_destroy(&dp->original_file);
+}
+
+static struct display *
+get_dp(png_structp pp)
+ /* The display pointer is always stored in the png_struct error pointer */
+{
+ struct display *dp = (struct display*)png_get_error_ptr(pp);
+
+ if (dp == NULL)
+ {
+ fprintf(stderr, "pngimage: internal error (no display)\n");
+ exit(99); /* prevents a crash */
+ }
+
+ return dp;
+}
+
+/* error handling */
+#ifdef __GNUC__
+# define VGATTR __attribute__((__format__ (__printf__,3,4)))
+ /* Required to quiet GNUC warnings when the compiler sees a stdarg function
+ * that calls one of the stdio v APIs.
+ */
+#else
+# define VGATTR
+#endif
+static void VGATTR
+display_log(struct display *dp, error_level level, const char *fmt, ...)
+ /* 'level' is as above, fmt is a stdio style format string. This routine
+ * does not return if level is above LIBPNG_WARNING
+ */
+{
+ dp->results |= 1U << level;
+
+ if (level > (error_level)(dp->options & LEVEL_MASK))
+ {
+ const char *lp;
+ va_list ap;
+
+ switch (level)
+ {
+ case INFORMATION: lp = "information"; break;
+ case LIBPNG_WARNING: lp = "warning(libpng)"; break;
+ case APP_WARNING: lp = "warning(pngimage)"; break;
+ case APP_FAIL: lp = "error(continuable)"; break;
+ case LIBPNG_ERROR: lp = "error(libpng)"; break;
+ case LIBPNG_BUG: lp = "bug(libpng)"; break;
+ case APP_ERROR: lp = "error(pngimage)"; break;
+ case USER_ERROR: lp = "error(user)"; break;
+
+ case INTERNAL_ERROR: /* anything unexpected is an internal error: */
+ case VERBOSE: case WARNINGS: case ERRORS: case QUIET:
+ default: lp = "bug(pngimage)"; break;
+ }
+
+ fprintf(stderr, "%s: %s: %s",
+ dp->filename != NULL ? dp->filename : "<stdin>", lp, dp->operation);
+
+ if (dp->transforms != 0)
+ {
+ int tr = dp->transforms;
+
+ if (is_combo(tr))
+ {
+ if (dp->options & LIST_COMBOS)
+ {
+ int trx = tr;
+
+ fprintf(stderr, "(");
+ if (trx)
+ {
+ int start = 0;
+
+ while (trx)
+ {
+ int trz = trx & -trx;
+
+ if (start) fprintf(stderr, "+");
+ fprintf(stderr, "%s", transform_name(trz));
+ start = 1;
+ trx &= ~trz;
+ }
+ }
+
+ else
+ fprintf(stderr, "-");
+ fprintf(stderr, ")");
+ }
+
+ else
+ fprintf(stderr, "(0x%x)", tr);
+ }
+
+ else
+ fprintf(stderr, "(%s)", transform_name(tr));
+ }
+
+ fprintf(stderr, ": ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stderr);
+ }
+ /* else do not output any message */
+
+ /* Errors cause this routine to exit to the fail code */
+ if (level > APP_FAIL || (level > ERRORS && !(dp->options & CONTINUE)))
+ longjmp(dp->error_return, level);
+}
+
+/* error handler callbacks for libpng */
+static void PNGCBAPI
+display_warning(png_structp pp, png_const_charp warning)
+{
+ display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning);
+}
+
+static void PNGCBAPI
+display_error(png_structp pp, png_const_charp error)
+{
+ struct display *dp = get_dp(pp);
+
+ display_log(dp, LIBPNG_ERROR, "%s", error);
+}
+
+static void
+display_cache_file(struct display *dp, const char *filename)
+ /* Does the initial cache of the file. */
+{
+ FILE *fp;
+ int ret;
+
+ dp->filename = filename;
+
+ if (filename != NULL)
+ {
+ fp = fopen(filename, "rb");
+ if (fp == NULL)
+ display_log(dp, USER_ERROR, "open failed: %s", strerror(errno));
+ }
+
+ else
+ fp = stdin;
+
+ ret = buffer_from_file(&dp->original_file, fp);
+
+ fclose(fp);
+
+ if (ret != 0)
+ display_log(dp, APP_ERROR, "read failed: %s", strerror(ret));
+}
+
+static void
+buffer_read(struct display *dp, struct buffer *bp, png_bytep data,
+ png_size_t size)
+{
+ struct buffer_list *last = bp->current;
+ size_t read_count = bp->read_count;
+
+ while (size > 0)
+ {
+ size_t avail;
+
+ if (last == NULL ||
+ (last == bp->last && read_count >= bp->end_count))
+ {
+ display_log(dp, USER_ERROR, "file truncated (%lu bytes)",
+ (unsigned long)size);
+ /*NOTREACHED*/
+ break;
+ }
+
+ else if (read_count >= sizeof last->buffer)
+ {
+ /* Move to the next buffer: */
+ last = last->next;
+ read_count = 0;
+ bp->current = last; /* Avoid update outside the loop */
+
+ /* And do a sanity check (the EOF case is caught above) */
+ if (last == NULL)
+ {
+ display_log(dp, INTERNAL_ERROR, "damaged buffer list");
+ /*NOTREACHED*/
+ break;
+ }
+ }
+
+ avail = (sizeof last->buffer) - read_count;
+ if (avail > size)
+ avail = size;
+
+ memcpy(data, last->buffer + read_count, avail);
+ read_count += avail;
+ size -= avail;
+ data += avail;
+ }
+
+ bp->read_count = read_count;
+}
+
+static void PNGCBAPI
+read_function(png_structp pp, png_bytep data, png_size_t size)
+{
+ buffer_read(get_dp(pp), get_buffer(pp), data, size);
+}
+
+static void
+read_png(struct display *dp, struct buffer *bp, const char *operation,
+ int transforms)
+{
+ png_structp pp;
+ png_infop ip;
+
+ /* This cleans out any previous read and sets operation and transforms to
+ * empty.
+ */
+ display_clean_read(dp);
+
+ if (operation != NULL) /* else this is a verify and do not overwrite info */
+ {
+ dp->operation = operation;
+ dp->transforms = transforms;
+ }
+
+ dp->read_pp = pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, dp,
+ display_error, display_warning);
+ if (pp == NULL)
+ display_log(dp, LIBPNG_ERROR, "failed to create read struct");
+
+ /* The png_read_png API requires us to make the info struct, but it does the
+ * call to png_read_info.
+ */
+ dp->read_ip = ip = png_create_info_struct(pp);
+ if (ip == NULL)
+ display_log(dp, LIBPNG_ERROR, "failed to create info struct");
+
+# ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ /* Remove the user limits, if any */
+ png_set_user_limits(pp, 0x7fffffff, 0x7fffffff);
+# endif
+
+ /* Set the IO handling */
+ buffer_start_read(bp);
+ png_set_read_fn(pp, bp, read_function);
+
+ png_read_png(pp, ip, transforms, NULL/*params*/);
+
+#if 0 /* crazy debugging */
+ {
+ png_bytep pr = png_get_rows(pp, ip)[0];
+ size_t rb = png_get_rowbytes(pp, ip);
+ size_t cb;
+ char c = ' ';
+
+ fprintf(stderr, "%.4x %2d (%3lu bytes):", transforms, png_get_bit_depth(pp,ip), (unsigned long)rb);
+
+ for (cb=0; cb<rb; ++cb)
+ fputc(c, stderr), fprintf(stderr, "%.2x", pr[cb]), c='.';
+
+ fputc('\n', stderr);
+ }
+#endif
+}
+
+static void
+update_display(struct display *dp)
+ /* called once after the first read to update all the info, original_pp and
+ * original_ip must have been filled in.
+ */
+{
+ png_structp pp;
+ png_infop ip;
+
+ /* Now perform the initial read with a 0 tranform. */
+ read_png(dp, &dp->original_file, "original read", 0/*no transform*/);
+
+ /* Move the result to the 'original' fields */
+ dp->original_pp = pp = dp->read_pp, dp->read_pp = NULL;
+ dp->original_ip = ip = dp->read_ip, dp->read_ip = NULL;
+
+ dp->original_rowbytes = png_get_rowbytes(pp, ip);
+ if (dp->original_rowbytes == 0)
+ display_log(dp, LIBPNG_BUG, "png_get_rowbytes returned 0");
+
+ dp->chunks = png_get_valid(pp, ip, 0xffffffff);
+ if ((dp->chunks & PNG_INFO_IDAT) == 0) /* set by png_read_png */
+ display_log(dp, LIBPNG_BUG, "png_read_png did not set IDAT flag");
+
+ dp->original_rows = png_get_rows(pp, ip);
+ if (dp->original_rows == NULL)
+ display_log(dp, LIBPNG_BUG, "png_read_png did not create row buffers");
+
+ if (!png_get_IHDR(pp, ip,
+ &dp->width, &dp->height, &dp->bit_depth, &dp->color_type,
+ &dp->interlace_method, &dp->compression_method, &dp->filter_method))
+ display_log(dp, LIBPNG_BUG, "png_get_IHDR failed");
+
+ /* 'active' transforms are discovered based on the original image format;
+ * running one active transform can activate others. At present the code
+ * does not attempt to determine the closure.
+ */
+ {
+ png_uint_32 chunks = dp->chunks;
+ int active = 0, inactive = 0;
+ int ct = dp->color_type;
+ int bd = dp->bit_depth;
+ unsigned int i;
+
+ for (i=0; i<TTABLE_SIZE; ++i) if (transform_info[i].name != NULL)
+ {
+ int transform = transform_info[i].transform;
+
+ if ((transform_info[i].valid_chunks == 0 ||
+ (transform_info[i].valid_chunks & chunks) != 0) &&
+ (transform_info[i].color_mask_required & ct) ==
+ transform_info[i].color_mask_required &&
+ (transform_info[i].color_mask_absent & ct) == 0 &&
+ (transform_info[i].bit_depths & bd) != 0 &&
+ (transform_info[i].when & TRANSFORM_R) != 0)
+ active |= transform;
+
+ else if ((transform_info[i].when & TRANSFORM_R) != 0)
+ inactive |= transform;
+ }
+
+ /* Some transforms appear multiple times in the table; the 'active' status
+ * is the logical OR of these and the inactive status must be adjusted to
+ * take this into account.
+ */
+ inactive &= ~active;
+
+ dp->active_transforms = active;
+ dp->ignored_transforms = inactive; /* excluding write-only transforms */
+ }
+}
+
+static int
+compare_read(struct display *dp, int applied_transforms)
+{
+ /* Compare the png_info from read_ip with original_info */
+ size_t rowbytes;
+ png_uint_32 width, height;
+ int bit_depth, color_type;
+ int interlace_method, compression_method, filter_method;
+ const char *e = NULL;
+
+ png_get_IHDR(dp->read_pp, dp->read_ip, &width, &height, &bit_depth,
+ &color_type, &interlace_method, &compression_method, &filter_method);
+
+# define C(item) if (item != dp->item) \
+ display_log(dp, APP_WARNING, "IHDR " #item "(%lu) changed to %lu",\
+ (unsigned long)dp->item, (unsigned long)item), e = #item
+
+ /* The IHDR should be identical: */
+ C(width);
+ C(height);
+ C(bit_depth);
+ C(color_type);
+ C(interlace_method);
+ C(compression_method);
+ C(filter_method);
+
+ /* 'e' remains set to the name of the last thing changed: */
+ if (e)
+ display_log(dp, APP_ERROR, "IHDR changed (%s)", e);
+
+ /* All the chunks from the original PNG should be preserved in the output PNG
+ * because the PNG format has not been changed.
+ */
+ {
+ unsigned long chunks =
+ png_get_valid(dp->read_pp, dp->read_ip, 0xffffffff);
+
+ if (chunks != dp->chunks)
+ display_log(dp, APP_FAIL, "PNG chunks changed from 0x%lx to 0x%lx",
+ (unsigned long)dp->chunks, chunks);
+ }
+
+ /* rowbytes should be the same */
+ rowbytes = png_get_rowbytes(dp->read_pp, dp->read_ip);
+
+ /* NOTE: on 64-bit systems this may trash the top bits of rowbytes,
+ * which could lead to weird error messages.
+ */
+ if (rowbytes != dp->original_rowbytes)
+ display_log(dp, APP_ERROR, "PNG rowbytes changed from %lu to %lu",
+ (unsigned long)dp->original_rowbytes, (unsigned long)rowbytes);
+
+ /* The rows should be the same too, unless the applied transforms includes
+ * the shift transform, in which case low bits may have been lost.
+ */
+ {
+ png_bytepp rows = png_get_rows(dp->read_pp, dp->read_ip);
+ unsigned int mask; /* mask (if not zero) for the final byte */
+
+ if (bit_depth < 8)
+ {
+ /* Need the stray bits at the end, this depends only on the low bits
+ * of the image width; overflow does not matter. If the width is an
+ * exact multiple of 8 bits this gives a mask of 0, not 0xff.
+ */
+ mask = 0xff & (0xff00 >> ((bit_depth * width) & 7));
+ }
+
+ else
+ mask = 0;
+
+ if (rows == NULL)
+ display_log(dp, LIBPNG_BUG, "png_get_rows returned NULL");
+
+ if ((applied_transforms & PNG_TRANSFORM_SHIFT) == 0 ||
+ (dp->active_transforms & PNG_TRANSFORM_SHIFT) == 0 ||
+ color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ unsigned long y;
+
+ for (y=0; y<height; ++y)
+ {
+ png_bytep row = rows[y];
+ png_bytep orig = dp->original_rows[y];
+
+ if (memcmp(row, orig, rowbytes-(mask != 0)) != 0 || (mask != 0 &&
+ ((row[rowbytes-1] & mask) != (orig[rowbytes-1] & mask))))
+ {
+ size_t x;
+
+ /* Find the first error */
+ for (x=0; x<rowbytes-1; ++x) if (row[x] != orig[x])
+ break;
+
+ display_log(dp, APP_FAIL,
+ "byte(%lu,%lu) changed 0x%.2x -> 0x%.2x",
+ (unsigned long)x, (unsigned long)y, orig[x], row[x]);
+ return 0; /* don't keep reporting failed rows on 'continue' */
+ }
+ }
+ }
+
+ else
+ {
+ unsigned long y;
+ int bpp; /* bits-per-pixel then bytes-per-pixel */
+ /* components are up to 8 bytes in size */
+ png_byte sig_bits[8];
+ png_color_8p sBIT;
+
+ if (png_get_sBIT(dp->read_pp, dp->read_ip, &sBIT) != PNG_INFO_sBIT)
+ display_log(dp, INTERNAL_ERROR,
+ "active shift transform but no sBIT in file");
+
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ sig_bits[0] = sBIT->gray;
+ bpp = bit_depth;
+ break;
+
+ case PNG_COLOR_TYPE_GA:
+ sig_bits[0] = sBIT->gray;
+ sig_bits[1] = sBIT->alpha;
+ bpp = 2 * bit_depth;
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+ sig_bits[0] = sBIT->red;
+ sig_bits[1] = sBIT->green;
+ sig_bits[2] = sBIT->blue;
+ bpp = 3 * bit_depth;
+ break;
+
+ case PNG_COLOR_TYPE_RGBA:
+ sig_bits[0] = sBIT->red;
+ sig_bits[1] = sBIT->green;
+ sig_bits[2] = sBIT->blue;
+ sig_bits[3] = sBIT->alpha;
+ bpp = 4 * bit_depth;
+ break;
+
+ default:
+ display_log(dp, LIBPNG_ERROR, "invalid colour type %d",
+ color_type);
+ /*NOTREACHED*/
+ bpp = 0;
+ break;
+ }
+
+ {
+ int b;
+
+ for (b=0; 8*b<bpp; ++b)
+ {
+ /* libpng should catch this; if not there is a security issue
+ * because an app (like this one) may overflow an array. In fact
+ * libpng doesn't catch this at present.
+ */
+ if (sig_bits[b] == 0 || sig_bits[b] > bit_depth/*!palette*/)
+ display_log(dp, LIBPNG_BUG,
+ "invalid sBIT[%u] value %d returned for PNG bit depth %d",
+ b, sig_bits[b], bit_depth);
+ }
+ }
+
+ if (bpp < 8 && bpp != bit_depth)
+ {
+ /* sanity check; this is a grayscale PNG; something is wrong in the
+ * code above.
+ */
+ display_log(dp, INTERNAL_ERROR, "invalid bpp %u for bit_depth %u",
+ bpp, bit_depth);
+ }
+
+ switch (bit_depth)
+ {
+ int b;
+
+ case 16: /* Two bytes per component, big-endian */
+ for (b = (bpp >> 4); b > 0; --b)
+ {
+ unsigned int sig = (unsigned int)(0xffff0000 >> sig_bits[b]);
+
+ sig_bits[2*b+1] = (png_byte)sig;
+ sig_bits[2*b+0] = (png_byte)(sig >> 8); /* big-endian */
+ }
+ break;
+
+ case 8: /* One byte per component */
+ for (b=0; b*8 < bpp; ++b)
+ sig_bits[b] = (png_byte)(0xff00 >> sig_bits[b]);
+ break;
+
+ case 1: /* allowed, but dumb */
+ /* Value is 1 */
+ sig_bits[0] = 0xff;
+ break;
+
+ case 2: /* Replicate 4 times */
+ /* Value is 1 or 2 */
+ b = 0x3 & ((0x3<<2) >> sig_bits[0]);
+ b |= b << 2;
+ b |= b << 4;
+ sig_bits[0] = (png_byte)b;
+ break;
+
+ case 4: /* Relicate twice */
+ /* Value is 1, 2, 3 or 4 */
+ b = 0xf & ((0xf << 4) >> sig_bits[0]);
+ b |= b << 4;
+ sig_bits[0] = (png_byte)b;
+ break;
+
+ default:
+ display_log(dp, LIBPNG_BUG, "invalid bit depth %d", bit_depth);
+ break;
+ }
+
+ /* Convert bpp to bytes; this gives '1' for low-bit depth grayscale,
+ * where there are multiple pixels per byte.
+ */
+ bpp = (bpp+7) >> 3;
+
+ /* The mask can be combined with sig_bits[0] */
+ if (mask != 0)
+ {
+ mask &= sig_bits[0];
+
+ if (bpp != 1 || mask == 0)
+ display_log(dp, INTERNAL_ERROR, "mask calculation error %u, %u",
+ bpp, mask);
+ }
+
+ for (y=0; y<height; ++y)
+ {
+ png_bytep row = rows[y];
+ png_bytep orig = dp->original_rows[y];
+ unsigned long x;
+
+ for (x=0; x<(width-(mask!=0)); ++x)
+ {
+ int b;
+
+ for (b=0; b<bpp; ++b)
+ {
+ if ((*row++ & sig_bits[b]) != (*orig++ & sig_bits[b]))
+ {
+ display_log(dp, APP_FAIL,
+ "significant bits at (%lu[%u],%lu) changed %.2x->%.2x",
+ x, b, y, orig[-1], row[-1]);
+ return 0;
+ }
+ }
+ }
+
+ if (mask != 0 && (*row & mask) != (*orig & mask))
+ {
+ display_log(dp, APP_FAIL,
+ "significant bits at (%lu[end],%lu) changed", x, y);
+ return 0;
+ }
+ } /* for y */
+ }
+ }
+
+ return 1; /* compare succeeded */
+}
+
+#ifdef PNG_WRITE_SUPPORTED
+static void
+buffer_write(struct display *dp, struct buffer *buffer, png_bytep data,
+ png_size_t size)
+ /* Generic write function used both from the write callback provided to
+ * libpng and from the generic read code.
+ */
+{
+ /* Write the data into the buffer, adding buffers as required */
+ struct buffer_list *last = buffer->last;
+ size_t end_count = buffer->end_count;
+
+ while (size > 0)
+ {
+ size_t avail;
+
+ if (end_count >= sizeof last->buffer)
+ {
+ if (last->next == NULL)
+ {
+ last = buffer_extend(last);
+
+ if (last == NULL)
+ display_log(dp, APP_ERROR, "out of memory saving file");
+ }
+
+ else
+ last = last->next;
+
+ buffer->last = last; /* avoid the need to rewrite every time */
+ end_count = 0;
+ }
+
+ avail = (sizeof last->buffer) - end_count;
+ if (avail > size)
+ avail = size;
+
+ memcpy(last->buffer + end_count, data, avail);
+ end_count += avail;
+ size -= avail;
+ data += avail;
+ }
+
+ buffer->end_count = end_count;
+}
+
+static void PNGCBAPI
+write_function(png_structp pp, png_bytep data, png_size_t size)
+{
+ buffer_write(get_dp(pp), get_buffer(pp), data, size);
+}
+
+static void
+write_png(struct display *dp, png_infop ip, int transforms)
+{
+ display_clean_write(dp); /* safety */
+
+ buffer_start_write(&dp->written_file);
+ dp->operation = "write";
+ dp->transforms = transforms;
+
+ dp->write_pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, dp,
+ display_error, display_warning);
+
+ if (dp->write_pp == NULL)
+ display_log(dp, APP_ERROR, "failed to create write png_struct");
+
+ png_set_write_fn(dp->write_pp, &dp->written_file, write_function,
+ NULL/*flush*/);
+
+# ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ /* Remove the user limits, if any */
+ png_set_user_limits(dp->write_pp, 0x7fffffff, 0x7fffffff);
+# endif
+
+ /* Certain transforms require the png_info to be zapped to allow the
+ * transform to work correctly.
+ */
+ if (transforms & (PNG_TRANSFORM_PACKING|
+ PNG_TRANSFORM_STRIP_FILLER|
+ PNG_TRANSFORM_STRIP_FILLER_BEFORE))
+ {
+ int ct = dp->color_type;
+
+ if (transforms & (PNG_TRANSFORM_STRIP_FILLER|
+ PNG_TRANSFORM_STRIP_FILLER_BEFORE))
+ ct &= ~PNG_COLOR_MASK_ALPHA;
+
+ png_set_IHDR(dp->write_pp, ip, dp->width, dp->height, dp->bit_depth, ct,
+ dp->interlace_method, dp->compression_method, dp->filter_method);
+ }
+
+ png_write_png(dp->write_pp, ip, transforms, NULL/*params*/);
+
+ /* Clean it on the way out - if control returns to the caller then the
+ * written_file contains the required data.
+ */
+ display_clean_write(dp);
+}
+#endif /* WRITE_SUPPORTED */
+
+static int
+skip_transform(struct display *dp, int tr)
+ /* Helper to test for a bad combo and log it if it is skipped */
+{
+ if ((dp->options & SKIP_BUGS) != 0 && is_bad_combo(tr))
+ {
+ /* Log this to stdout if logging is on, otherwise just do an information
+ * display_log.
+ */
+ if ((dp->options & LOG_SKIPPED) != 0)
+ {
+ printf("SKIP: %s transforms ", dp->filename);
+
+ while (tr != 0)
+ {
+ int next = first_transform(tr);
+ tr &= ~next;
+
+ printf("%s", transform_name(next));
+ if (tr != 0)
+ putchar('+');
+ }
+
+ putchar('\n');
+ }
+
+ else
+ display_log(dp, INFORMATION, "%s: skipped known bad combo 0x%x",
+ dp->filename, tr);
+
+ return 1; /* skip */
+ }
+
+ return 0; /* don't skip */
+}
+
+static void
+test_one_file(struct display *dp, const char *filename)
+{
+ /* First cache the file and update the display original file
+ * information for the new file.
+ */
+ dp->operation = "cache file";
+ dp->transforms = 0;
+ display_cache_file(dp, filename);
+ update_display(dp);
+
+ /* First test: if there are options that should be ignored for this file
+ * verify that they really are ignored.
+ */
+ if (dp->ignored_transforms != 0)
+ {
+ read_png(dp, &dp->original_file, "ignored transforms",
+ dp->ignored_transforms);
+
+ /* The result should be identical to the original_rows */
+ if (!compare_read(dp, 0/*transforms applied*/))
+ return; /* no point testing more */
+ }
+
+#ifdef PNG_WRITE_SUPPORTED
+ /* Second test: write the original PNG data out to a new file (to test the
+ * write side) then read the result back in and make sure that it hasn't
+ * changed.
+ */
+ dp->operation = "write";
+ write_png(dp, dp->original_ip, 0/*transforms*/);
+ read_png(dp, &dp->written_file, NULL, 0/*transforms*/);
+ if (!compare_read(dp, 0/*transforms applied*/))
+ return;
+#endif
+
+ /* Third test: the active options. Test each in turn, or, with the
+ * EXHAUSTIVE option, test all possible combinations.
+ */
+ {
+ /* Use unsigned int here because the code below to increment through all
+ * the possibilities exhaustively has to use a compare and that must be
+ * unsigned, because some transforms are negative on a 16-bit system.
+ */
+ unsigned int active = dp->active_transforms;
+ const int exhaustive = (dp->options & EXHAUSTIVE) != 0;
+ unsigned int current = first_transform(active);
+ unsigned int bad_transforms = 0;
+ unsigned int bad_combo = ~0U; /* bitwise AND of failing transforms */
+ unsigned int bad_combo_list = 0; /* bitwise OR of failures */
+
+ for (;;)
+ {
+ read_png(dp, &dp->original_file, "active transforms", current);
+
+ /* If this involved any irreversible transformations then if we write
+ * it out with just the reversible transformations and read it in again
+ * with the same transforms we should get the same thing. At present
+ * this isn't done - it just seems like a waste of time and it would
+ * require two sets of read png_struct/png_info.
+ *
+ * If there were no irreversible transformations then if we write it
+ * out and read it back in again (without the reversible transforms)
+ * we should get back to the place where we started.
+ */
+#ifdef PNG_WRITE_SUPPORTED
+ if ((current & write_transforms) == current)
+ {
+ /* All transforms reversible: write the PNG with the transformations
+ * reversed, then read it back in with no transformations. The
+ * result should be the same as the original apart from the loss of
+ * low order bits because of the SHIFT/sBIT transform.
+ */
+ dp->operation = "reversible transforms";
+ write_png(dp, dp->read_ip, current);
+
+ /* And if this is read back in, because all the transformations were
+ * reversible, the result should be the same.
+ */
+ read_png(dp, &dp->written_file, NULL, 0);
+ if (!compare_read(dp, current/*for the SHIFT/sBIT transform*/))
+ {
+ /* This set of transforms failed. If a single bit is set - if
+ * there is just one transform - don't include this in further
+ * 'exhaustive' tests. Notice that each transform is tested on
+ * its own before testing combos in the exhaustive case.
+ */
+ if (is_combo(current))
+ {
+ bad_combo &= current;
+ bad_combo_list |= current;
+ }
+
+ else
+ bad_transforms |= current;
+ }
+ }
+#endif
+
+ /* Now move to the next transform */
+ if (exhaustive) /* all combinations */
+ {
+ unsigned int next = current;
+
+ do
+ {
+ if (next == read_transforms) /* Everything tested */
+ goto combo;
+
+ ++next;
+ } /* skip known bad combos if the relevant option is set; skip
+ * combos involving known bad single transforms in all cases.
+ */
+ while ( (next & read_transforms) <= current
+ || (next & active) == 0 /* skip cases that do nothing */
+ || (next & bad_transforms) != 0
+ || skip_transform(dp, next));
+
+ assert((next & read_transforms) == next);
+ current = next;
+ }
+
+ else /* one at a time */
+ {
+ active &= ~current;
+
+ if (active == 0)
+ goto combo;
+
+ current = first_transform(active);
+ }
+ }
+
+combo:
+ if (dp->options & FIND_BAD_COMBOS)
+ {
+ /* bad_combos identifies the combos that occur in all failing cases;
+ * bad_combo_list identifies transforms that do not prevent the
+ * failure.
+ */
+ if (bad_combo != ~0U)
+ printf("%s[0x%x]: PROBLEM: 0x%x[0x%x] ANTIDOTE: 0x%x\n",
+ dp->filename, active, bad_combo, bad_combo_list,
+ rw_transforms & ~bad_combo_list);
+
+ else
+ printf("%s: no %sbad combos found\n", dp->filename,
+ (dp->options & SKIP_BUGS) ? "additional " : "");
+ }
+ }
+}
+
+static int
+do_test(struct display *dp, const char *file)
+ /* Exists solely to isolate the setjmp clobbers */
+{
+ int ret = setjmp(dp->error_return);
+
+ if (ret == 0)
+ {
+ test_one_file(dp, file);
+ return 0;
+ }
+
+ else if (ret < ERRORS) /* shouldn't longjmp on warnings */
+ display_log(dp, INTERNAL_ERROR, "unexpected return code %d", ret);
+
+ return ret;
+}
+
+int
+main(const int argc, const char * const * const argv)
+{
+ /* For each file on the command line test it with a range of transforms */
+ int option_end, ilog = 0;
+ struct display d;
+
+ validate_T();
+ display_init(&d);
+
+ for (option_end=1; option_end<argc; ++option_end)
+ {
+ const char *name = argv[option_end];
+
+ if (strcmp(name, "--verbose") == 0)
+ d.options = (d.options & ~LEVEL_MASK) | VERBOSE;
+
+ else if (strcmp(name, "--warnings") == 0)
+ d.options = (d.options & ~LEVEL_MASK) | WARNINGS;
+
+ else if (strcmp(name, "--errors") == 0)
+ d.options = (d.options & ~LEVEL_MASK) | ERRORS;
+
+ else if (strcmp(name, "--quiet") == 0)
+ d.options = (d.options & ~LEVEL_MASK) | QUIET;
+
+ else if (strcmp(name, "--exhaustive") == 0)
+ d.options |= EXHAUSTIVE;
+
+ else if (strcmp(name, "--fast") == 0)
+ d.options &= ~EXHAUSTIVE;
+
+ else if (strcmp(name, "--strict") == 0)
+ d.options |= STRICT;
+
+ else if (strcmp(name, "--relaxed") == 0)
+ d.options &= ~STRICT;
+
+ else if (strcmp(name, "--log") == 0)
+ {
+ ilog = option_end; /* prevent display */
+ d.options |= LOG;
+ }
+
+ else if (strcmp(name, "--nolog") == 0)
+ d.options &= ~LOG;
+
+ else if (strcmp(name, "--continue") == 0)
+ d.options |= CONTINUE;
+
+ else if (strcmp(name, "--stop") == 0)
+ d.options &= ~CONTINUE;
+
+ else if (strcmp(name, "--skip-bugs") == 0)
+ d.options |= SKIP_BUGS;
+
+ else if (strcmp(name, "--test-all") == 0)
+ d.options &= ~SKIP_BUGS;
+
+ else if (strcmp(name, "--log-skipped") == 0)
+ d.options |= LOG_SKIPPED;
+
+ else if (strcmp(name, "--nolog-skipped") == 0)
+ d.options &= ~LOG_SKIPPED;
+
+ else if (strcmp(name, "--find-bad-combos") == 0)
+ d.options |= FIND_BAD_COMBOS;
+
+ else if (strcmp(name, "--nofind-bad-combos") == 0)
+ d.options &= ~FIND_BAD_COMBOS;
+
+ else if (strcmp(name, "--list-combos") == 0)
+ d.options |= LIST_COMBOS;
+
+ else if (strcmp(name, "--nolist-combos") == 0)
+ d.options &= ~LIST_COMBOS;
+
+ else if (name[0] == '-' && name[1] == '-')
+ {
+ fprintf(stderr, "pngimage: %s: unknown option\n", name);
+ return 99;
+ }
+
+ else
+ break; /* Not an option */
+ }
+
+ {
+ int i;
+ int errors = 0;
+
+ for (i=option_end; i<argc; ++i)
+ {
+ {
+ int ret = do_test(&d, argv[i]);
+
+ if (ret > QUIET) /* abort on user or internal error */
+ return 99;
+ }
+
+ /* Here on any return, including failures, except user/internal issues
+ */
+ {
+ const int pass = (d.options & STRICT) ?
+ RESULT_STRICT(d.results) : RESULT_RELAXED(d.results);
+
+ if (!pass)
+ ++errors;
+
+ if (d.options & LOG)
+ {
+ int j;
+
+ printf("%s: pngimage ", pass ? "PASS" : "FAIL");
+
+ for (j=1; j<option_end; ++j) if (j != ilog)
+ printf("%s ", argv[j]);
+
+ printf("%s\n", d.filename);
+ }
+ }
+
+ display_clean(&d);
+ }
+
+ /* Release allocated memory */
+ display_destroy(&d);
+
+ return errors != 0;
+ }
+}
+#else /* !INFO_IMAGE || !SEQUENTIAL_READ || !READ_PNG*/
+int
+main(void)
+{
+ fprintf(stderr, "pngimage: no support for png_read/write_image\n");
+ return 77;
+}
+#endif
diff --git a/contrib/libtests/pngstest-errors.h b/contrib/libtests/pngstest-errors.h
new file mode 100644
index 0000000..3356f1c
--- /dev/null
+++ b/contrib/libtests/pngstest-errors.h
@@ -0,0 +1,165 @@
+/* contrib/libtests/pngstest-errors.h
+ *
+ * BUILT USING: libpng version 1.6.19beta03 - September 25, 2015
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * THIS IS A MACHINE GENERATED FILE: do not edit it directly!
+ * Instead run:
+ *
+ * pngstest --accumulate
+ *
+ * on as many PNG files as possible; at least PNGSuite and
+ * contrib/libtests/testpngs.
+ */
+static png_uint_16 gpc_error[16/*in*/][16/*out*/][4/*a*/] =
+{
+ { /* input: sRGB-gray */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 372, 0 }, { 0, 0, 372, 0 }, { 0, 0, 372, 0 }, { 0, 0, 372, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: sRGB-gray+alpha */
+ { 0, 19, 0, 0 }, { 0, 0, 0, 0 }, { 0, 20, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 897, 788, 0 }, { 0, 897, 788, 0 }, { 0, 897, 788, 0 }, { 0, 897, 788, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: sRGB-rgb */
+ { 0, 0, 19, 0 }, { 0, 0, 19, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 893, 0 }, { 0, 0, 893, 0 }, { 0, 0, 811, 0 }, { 0, 0, 811, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: sRGB-rgb+alpha */
+ { 0, 16, 17, 0 }, { 0, 17, 17, 0 }, { 0, 19, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 850, 875, 0 }, { 0, 850, 875, 0 }, { 0, 897, 788, 0 }, { 0, 897, 788, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-gray */
+ { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-gray+alpha */
+ { 0, 74, 9, 0 }, { 0, 20, 9, 0 }, { 0, 74, 9, 0 }, { 0, 20, 9, 0 },
+ { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 1, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-rgb */
+ { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+ { 0, 0, 4, 0 }, { 0, 0, 4, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: linear-rgb+alpha */
+ { 0, 126, 143, 0 }, { 0, 11, 7, 0 }, { 0, 74, 9, 0 }, { 0, 17, 9, 0 },
+ { 0, 4, 4, 0 }, { 0, 5, 4, 0 }, { 0, 0, 0, 0 }, { 0, 1, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-gray */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-gray+alpha */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-rgb */
+ { 0, 0, 13, 0 }, { 0, 0, 13, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+ { 0, 0, 673, 0 }, { 0, 0, 673, 0 }, { 0, 0, 674, 0 }, { 0, 0, 674, 0 },
+ { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 460, 0 }, { 0, 0, 460, 0 }, { 0, 0, 263, 0 }, { 0, 0, 263, 0 }
+ }, { /* input: color-mapped-sRGB-rgb+alpha */
+ { 0, 6, 8, 0 }, { 0, 7, 8, 0 }, { 0, 75, 9, 0 }, { 0, 9, 9, 0 },
+ { 0, 585, 427, 0 }, { 0, 585, 427, 0 }, { 0, 717, 514, 0 }, { 0, 717, 514, 0 },
+ { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 13323, 460, 0 }, { 0, 427, 460, 0 }, { 0, 16480, 263, 0 }, { 0, 243, 263, 0 }
+ }, { /* input: color-mapped-linear-gray */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 282, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-gray+alpha */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 253, 282, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 265, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb+alpha */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 243, 265, 0 }
+ }
+};
+static png_uint_16 gpc_error_via_linear[16][4/*out*/][4] =
+{
+ { /* input: sRGB-gray */
+ { 0, 0, 7, 0 }, { 0, 0, 7, 0 }, { 0, 0, 7, 0 }, { 0, 0, 7, 0 }
+ }, { /* input: sRGB-gray+alpha */
+ { 0, 15, 15, 0 }, { 0, 186, 15, 0 }, { 0, 15, 15, 0 }, { 0, 186, 15, 0 }
+ }, { /* input: sRGB-rgb */
+ { 0, 0, 20, 0 }, { 0, 0, 20, 0 }, { 0, 0, 15, 0 }, { 0, 0, 15, 0 }
+ }, { /* input: sRGB-rgb+alpha */
+ { 0, 16, 17, 0 }, { 0, 187, 17, 0 }, { 0, 15, 15, 0 }, { 0, 186, 15, 0 }
+ }, { /* input: linear-gray */
+ { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }
+ }, { /* input: linear-gray+alpha */
+ { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }
+ }, { /* input: linear-rgb */
+ { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }, { 0, 0, 1, 0 }
+ }, { /* input: linear-rgb+alpha */
+ { 0, 1, 1, 0 }, { 0, 9, 1, 0 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }
+ }, { /* input: color-mapped-sRGB-gray */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-gray+alpha */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-sRGB-rgb */
+ { 0, 0, 13, 0 }, { 0, 0, 13, 0 }, { 0, 0, 14, 0 }, { 0, 0, 14, 0 }
+ }, { /* input: color-mapped-sRGB-rgb+alpha */
+ { 0, 4, 8, 0 }, { 0, 9, 8, 0 }, { 0, 9, 5, 0 }, { 0, 32, 5, 0 }
+ }, { /* input: color-mapped-linear-gray */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-gray+alpha */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }, { /* input: color-mapped-linear-rgb+alpha */
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }
+ }
+};
+static png_uint_16 gpc_error_to_colormap[8/*i*/][8/*o*/][4] =
+{
+ { /* input: sRGB-gray */
+ { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 }, { 0, 0, 9, 0 },
+ { 0, 0, 560, 0 }, { 0, 0, 560, 0 }, { 0, 0, 560, 0 }, { 0, 0, 560, 0 }
+ }, { /* input: sRGB-gray+alpha */
+ { 0, 19, 9, 0 }, { 0, 255, 9, 25 }, { 0, 88, 9, 0 }, { 0, 255, 9, 25 },
+ { 0, 1012, 928, 0 }, { 0, 16026, 928, 6425 }, { 0, 1012, 928, 0 }, { 0, 16026, 928, 6425 }
+ }, { /* input: sRGB-rgb */
+ { 0, 0, 19, 0 }, { 0, 0, 19, 0 }, { 0, 0, 25, 0 }, { 0, 0, 25, 0 },
+ { 0, 0, 962, 0 }, { 0, 0, 962, 0 }, { 0, 0, 13677, 0 }, { 0, 0, 13677, 0 }
+ }, { /* input: sRGB-rgb+alpha */
+ { 0, 63, 77, 0 }, { 0, 255, 19, 25 }, { 0, 225, 25, 0 }, { 0, 255, 25, 67 },
+ { 0, 17534, 18491, 0 }, { 0, 15736, 2824, 6425 }, { 0, 14019, 13677, 0 }, { 0, 50115, 13677, 17219 }
+ }, { /* input: linear-gray */
+ { 0, 0, 73, 0 }, { 0, 0, 73, 0 }, { 0, 0, 73, 0 }, { 0, 0, 73, 0 },
+ { 0, 0, 18817, 0 }, { 0, 0, 18817, 0 }, { 0, 0, 18817, 0 }, { 0, 0, 18817, 0 }
+ }, { /* input: linear-gray+alpha */
+ { 0, 74, 74, 0 }, { 0, 255, 74, 25 }, { 0, 99, 74, 0 }, { 0, 255, 74, 25 },
+ { 0, 18919, 18907, 0 }, { 0, 24549, 18907, 6553 }, { 0, 18919, 18907, 0 }, { 0, 24549, 18907, 6553 }
+ }, { /* input: linear-rgb */
+ { 0, 0, 73, 0 }, { 0, 0, 73, 0 }, { 0, 0, 98, 0 }, { 0, 0, 98, 0 },
+ { 0, 0, 18664, 0 }, { 0, 0, 18664, 0 }, { 0, 0, 24998, 0 }, { 0, 0, 24998, 0 }
+ }, { /* input: linear-rgb+alpha */
+ { 0, 181, 196, 0 }, { 0, 255, 61, 25 }, { 206, 187, 98, 0 }, { 0, 255, 98, 67 },
+ { 0, 18141, 18137, 0 }, { 0, 17494, 17504, 6553 }, { 0, 24979, 24992, 0 }, { 0, 49172, 24992, 17347 }
+ }
+};
+/* END MACHINE GENERATED */
diff --git a/contrib/libtests/pngstest.c b/contrib/libtests/pngstest.c
new file mode 100644
index 0000000..3374c72
--- /dev/null
+++ b/contrib/libtests/pngstest.c
@@ -0,0 +1,3722 @@
+/*-
+ * pngstest.c
+ *
+ * Copyright (c) 2013-2015 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.19 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Test for the PNG 'simplified' APIs.
+ */
+#define _ISOC90_SOURCE 1
+#define MALLOC_CHECK_ 2/*glibc facility: turn on debugging*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* Else nothing can be done */
+#include "../tools/sRGB.h"
+
+/* KNOWN ISSUES
+ *
+ * These defines switch on alternate algorithms for format conversions to match
+ * the current libpng implementation; they are set to allow pngstest to pass
+ * even though libpng is producing answers that are not as correct as they
+ * should be.
+ */
+#define ALLOW_UNUSED_GPC 0
+ /* If true include unused static GPC functions and declare an external array
+ * of them to hide the fact that they are unused. This is for development
+ * use while testing the correct function to use to take into account libpng
+ * misbehavior, such as using a simple power law to correct sRGB to linear.
+ */
+
+/* The following is to support direct compilation of this file as C++ */
+#ifdef __cplusplus
+# define voidcast(type, value) static_cast<type>(value)
+# define aligncastconst(type, value) \
+ static_cast<type>(static_cast<const void*>(value))
+#else
+# define voidcast(type, value) (value)
+# define aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+/* During parallel runs of pngstest each temporary file needs a unique name,
+ * this is used to permit uniqueness using a command line argument which can be
+ * up to 22 characters long.
+ */
+static char tmpf[23] = "TMP";
+
+/* Generate random bytes. This uses a boring repeatable algorithm and it
+ * is implemented here so that it gives the same set of numbers on every
+ * architecture. It's a linear congruential generator (Knuth or Sedgewick
+ * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
+ * Hill, "The Art of Electronics".
+ */
+static void
+make_random_bytes(png_uint_32* seed, void* pv, size_t size)
+{
+ png_uint_32 u0 = seed[0], u1 = seed[1];
+ png_bytep bytes = voidcast(png_bytep, pv);
+
+ /* There are thirty three bits, the next bit in the sequence is bit-33 XOR
+ * bit-20. The top 1 bit is in u1, the bottom 32 are in u0.
+ */
+ size_t i;
+ for (i=0; i<size; ++i)
+ {
+ /* First generate 8 new bits then shift them in at the end. */
+ png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
+ u1 <<= 8;
+ u1 |= u0 >> 24;
+ u0 <<= 8;
+ u0 |= u;
+ *bytes++ = (png_byte)u;
+ }
+
+ seed[0] = u0;
+ seed[1] = u1;
+}
+
+static void
+random_color(png_colorp color)
+{
+ static png_uint_32 color_seed[2] = { 0x12345678, 0x9abcdef };
+ make_random_bytes(color_seed, color, sizeof *color);
+}
+
+/* Math support - neither Cygwin nor Visual Studio have C99 support and we need
+ * a predictable rounding function, so make one here:
+ */
+static double
+closestinteger(double x)
+{
+ return floor(x + .5);
+}
+
+/* Cast support: remove GCC whines. */
+static png_byte
+u8d(double d)
+{
+ d = closestinteger(d);
+ return (png_byte)d;
+}
+
+static png_uint_16
+u16d(double d)
+{
+ d = closestinteger(d);
+ return (png_uint_16)d;
+}
+
+/* sRGB support: use exact calculations rounded to the nearest int, see the
+ * fesetround() call in main(). sRGB_to_d optimizes the 8 to 16-bit conversion.
+ */
+static double sRGB_to_d[256];
+static double g22_to_d[256];
+
+static void
+init_sRGB_to_d(void)
+{
+ int i;
+
+ sRGB_to_d[0] = 0;
+ for (i=1; i<255; ++i)
+ sRGB_to_d[i] = linear_from_sRGB(i/255.);
+ sRGB_to_d[255] = 1;
+
+ g22_to_d[0] = 0;
+ for (i=1; i<255; ++i)
+ g22_to_d[i] = pow(i/255., 1/.45455);
+ g22_to_d[255] = 1;
+}
+
+static png_byte
+sRGB(double linear /*range 0.0 .. 1.0*/)
+{
+ return u8d(255 * sRGB_from_linear(linear));
+}
+
+static png_byte
+isRGB(int fixed_linear)
+{
+ return sRGB(fixed_linear / 65535.);
+}
+
+#if 0 /* not used */
+static png_byte
+unpremultiply(int component, int alpha)
+{
+ if (alpha <= component)
+ return 255; /* Arbitrary, but consistent with the libpng code */
+
+ else if (alpha >= 65535)
+ return isRGB(component);
+
+ else
+ return sRGB((double)component / alpha);
+}
+#endif
+
+static png_uint_16
+ilinear(int fixed_srgb)
+{
+ return u16d(65535 * sRGB_to_d[fixed_srgb]);
+}
+
+static png_uint_16
+ilineara(int fixed_srgb, int alpha)
+{
+ return u16d((257 * alpha) * sRGB_to_d[fixed_srgb]);
+}
+
+static png_uint_16
+ilinear_g22(int fixed_srgb)
+{
+ return u16d(65535 * g22_to_d[fixed_srgb]);
+}
+
+#if ALLOW_UNUSED_GPC
+static png_uint_16
+ilineara_g22(int fixed_srgb, int alpha)
+{
+ return u16d((257 * alpha) * g22_to_d[fixed_srgb]);
+}
+#endif
+
+static double
+YfromRGBint(int ir, int ig, int ib)
+{
+ double r = ir;
+ double g = ig;
+ double b = ib;
+ return YfromRGB(r, g, b);
+}
+
+#if 0 /* unused */
+/* The error that results from using a 2.2 power law in place of the correct
+ * sRGB transform, given an 8-bit value which might be either sRGB or power-law.
+ */
+static int
+power_law_error8(int value)
+{
+ if (value > 0 && value < 255)
+ {
+ double vd = value / 255.;
+ double e = fabs(
+ pow(sRGB_to_d[value], 1/2.2) - sRGB_from_linear(pow(vd, 2.2)));
+
+ /* Always allow an extra 1 here for rounding errors */
+ e = 1+floor(255 * e);
+ return (int)e;
+ }
+
+ return 0;
+}
+
+static int error_in_sRGB_roundtrip = 56; /* by experiment */
+static int
+power_law_error16(int value)
+{
+ if (value > 0 && value < 65535)
+ {
+ /* Round trip the value through an 8-bit representation but using
+ * non-matching to/from conversions.
+ */
+ double vd = value / 65535.;
+ double e = fabs(
+ pow(sRGB_from_linear(vd), 2.2) - linear_from_sRGB(pow(vd, 1/2.2)));
+
+ /* Always allow an extra 1 here for rounding errors */
+ e = error_in_sRGB_roundtrip+floor(65535 * e);
+ return (int)e;
+ }
+
+ return 0;
+}
+
+static int
+compare_8bit(int v1, int v2, int error_limit, int multiple_algorithms)
+{
+ int e = abs(v1-v2);
+ int ev1, ev2;
+
+ if (e <= error_limit)
+ return 1;
+
+ if (!multiple_algorithms)
+ return 0;
+
+ ev1 = power_law_error8(v1);
+ if (e <= ev1)
+ return 1;
+
+ ev2 = power_law_error8(v2);
+ if (e <= ev2)
+ return 1;
+
+ return 0;
+}
+
+static int
+compare_16bit(int v1, int v2, int error_limit, int multiple_algorithms)
+{
+ int e = abs(v1-v2);
+ int ev1, ev2;
+
+ if (e <= error_limit)
+ return 1;
+
+ /* "multiple_algorithms" in this case means that a color-map has been
+ * involved somewhere, so we can deduce that the values were forced to 8-bit
+ * (like the via_linear case for 8-bit.)
+ */
+ if (!multiple_algorithms)
+ return 0;
+
+ ev1 = power_law_error16(v1);
+ if (e <= ev1)
+ return 1;
+
+ ev2 = power_law_error16(v2);
+ if (e <= ev2)
+ return 1;
+
+ return 0;
+}
+#endif /* unused */
+
+#define READ_FILE 1 /* else memory */
+#define USE_STDIO 2 /* else use file name */
+#define STRICT 4 /* fail on warnings too */
+#define VERBOSE 8
+#define KEEP_TMPFILES 16 /* else delete temporary files */
+#define KEEP_GOING 32
+#define ACCUMULATE 64
+#define FAST_WRITE 128
+#define sRGB_16BIT 256
+
+static void
+print_opts(png_uint_32 opts)
+{
+ if (opts & READ_FILE)
+ printf(" --file");
+ if (opts & USE_STDIO)
+ printf(" --stdio");
+ if (opts & STRICT)
+ printf(" --strict");
+ if (opts & VERBOSE)
+ printf(" --verbose");
+ if (opts & KEEP_TMPFILES)
+ printf(" --preserve");
+ if (opts & KEEP_GOING)
+ printf(" --keep-going");
+ if (opts & ACCUMULATE)
+ printf(" --accumulate");
+ if (!(opts & FAST_WRITE)) /* --fast is currently the default */
+ printf(" --slow");
+ if (opts & sRGB_16BIT)
+ printf(" --sRGB-16bit");
+}
+
+#define FORMAT_NO_CHANGE 0x80000000 /* additional flag */
+
+/* A name table for all the formats - defines the format of the '+' arguments to
+ * pngstest.
+ */
+#define FORMAT_COUNT 64
+#define FORMAT_MASK 0x3f
+static PNG_CONST char * PNG_CONST format_names[FORMAT_COUNT] =
+{
+ "sRGB-gray",
+ "sRGB-gray+alpha",
+ "sRGB-rgb",
+ "sRGB-rgb+alpha",
+ "linear-gray",
+ "linear-gray+alpha",
+ "linear-rgb",
+ "linear-rgb+alpha",
+
+ "color-mapped-sRGB-gray",
+ "color-mapped-sRGB-gray+alpha",
+ "color-mapped-sRGB-rgb",
+ "color-mapped-sRGB-rgb+alpha",
+ "color-mapped-linear-gray",
+ "color-mapped-linear-gray+alpha",
+ "color-mapped-linear-rgb",
+ "color-mapped-linear-rgb+alpha",
+
+ "sRGB-gray",
+ "sRGB-gray+alpha",
+ "sRGB-bgr",
+ "sRGB-bgr+alpha",
+ "linear-gray",
+ "linear-gray+alpha",
+ "linear-bgr",
+ "linear-bgr+alpha",
+
+ "color-mapped-sRGB-gray",
+ "color-mapped-sRGB-gray+alpha",
+ "color-mapped-sRGB-bgr",
+ "color-mapped-sRGB-bgr+alpha",
+ "color-mapped-linear-gray",
+ "color-mapped-linear-gray+alpha",
+ "color-mapped-linear-bgr",
+ "color-mapped-linear-bgr+alpha",
+
+ "sRGB-gray",
+ "alpha+sRGB-gray",
+ "sRGB-rgb",
+ "alpha+sRGB-rgb",
+ "linear-gray",
+ "alpha+linear-gray",
+ "linear-rgb",
+ "alpha+linear-rgb",
+
+ "color-mapped-sRGB-gray",
+ "color-mapped-alpha+sRGB-gray",
+ "color-mapped-sRGB-rgb",
+ "color-mapped-alpha+sRGB-rgb",
+ "color-mapped-linear-gray",
+ "color-mapped-alpha+linear-gray",
+ "color-mapped-linear-rgb",
+ "color-mapped-alpha+linear-rgb",
+
+ "sRGB-gray",
+ "alpha+sRGB-gray",
+ "sRGB-bgr",
+ "alpha+sRGB-bgr",
+ "linear-gray",
+ "alpha+linear-gray",
+ "linear-bgr",
+ "alpha+linear-bgr",
+
+ "color-mapped-sRGB-gray",
+ "color-mapped-alpha+sRGB-gray",
+ "color-mapped-sRGB-bgr",
+ "color-mapped-alpha+sRGB-bgr",
+ "color-mapped-linear-gray",
+ "color-mapped-alpha+linear-gray",
+ "color-mapped-linear-bgr",
+ "color-mapped-alpha+linear-bgr",
+};
+
+/* Decode an argument to a format number. */
+static png_uint_32
+formatof(const char *arg)
+{
+ char *ep;
+ unsigned long format = strtoul(arg, &ep, 0);
+
+ if (ep > arg && *ep == 0 && format < FORMAT_COUNT)
+ return (png_uint_32)format;
+
+ else for (format=0; format < FORMAT_COUNT; ++format)
+ {
+ if (strcmp(format_names[format], arg) == 0)
+ return (png_uint_32)format;
+ }
+
+ fprintf(stderr, "pngstest: format name '%s' invalid\n", arg);
+ return FORMAT_COUNT;
+}
+
+/* Bitset/test functions for formats */
+#define FORMAT_SET_COUNT (FORMAT_COUNT / 32)
+typedef struct
+{
+ png_uint_32 bits[FORMAT_SET_COUNT];
+}
+format_list;
+
+static void format_init(format_list *pf)
+{
+ int i;
+ for (i=0; i<FORMAT_SET_COUNT; ++i)
+ pf->bits[i] = 0; /* All off */
+}
+
+#if 0 /* currently unused */
+static void format_clear(format_list *pf)
+{
+ int i;
+ for (i=0; i<FORMAT_SET_COUNT; ++i)
+ pf->bits[i] = 0;
+}
+#endif
+
+static int format_is_initial(format_list *pf)
+{
+ int i;
+ for (i=0; i<FORMAT_SET_COUNT; ++i)
+ if (pf->bits[i] != 0)
+ return 0;
+
+ return 1;
+}
+
+static int format_set(format_list *pf, png_uint_32 format)
+{
+ if (format < FORMAT_COUNT)
+ return pf->bits[format >> 5] |= ((png_uint_32)1) << (format & 31);
+
+ return 0;
+}
+
+#if 0 /* currently unused */
+static int format_unset(format_list *pf, png_uint_32 format)
+{
+ if (format < FORMAT_COUNT)
+ return pf->bits[format >> 5] &= ~((png_uint_32)1) << (format & 31);
+
+ return 0;
+}
+#endif
+
+static int format_isset(format_list *pf, png_uint_32 format)
+{
+ return format < FORMAT_COUNT &&
+ (pf->bits[format >> 5] & (((png_uint_32)1) << (format & 31))) != 0;
+}
+
+static void format_default(format_list *pf, int redundant)
+{
+ if (redundant)
+ {
+ int i;
+
+ /* set everything, including flags that are pointless */
+ for (i=0; i<FORMAT_SET_COUNT; ++i)
+ pf->bits[i] = ~(png_uint_32)0;
+ }
+
+ else
+ {
+ png_uint_32 f;
+
+ for (f=0; f<FORMAT_COUNT; ++f)
+ {
+ /* Eliminate redundant and unsupported settings. */
+# ifdef PNG_FORMAT_BGR_SUPPORTED
+ /* BGR is meaningless if no color: */
+ if ((f & PNG_FORMAT_FLAG_COLOR) == 0 &&
+ (f & PNG_FORMAT_FLAG_BGR) != 0)
+# else
+ if ((f & 0x10U/*HACK: fixed value*/) != 0)
+# endif
+ continue;
+
+ /* AFIRST is meaningless if no alpha: */
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if ((f & PNG_FORMAT_FLAG_ALPHA) == 0 &&
+ (f & PNG_FORMAT_FLAG_AFIRST) != 0)
+# else
+ if ((f & 0x20U/*HACK: fixed value*/) != 0)
+# endif
+ continue;
+
+ format_set(pf, f);
+ }
+ }
+}
+
+/* THE Image STRUCTURE */
+/* The super-class of a png_image, contains the decoded image plus the input
+ * data necessary to re-read the file with a different format.
+ */
+typedef struct
+{
+ png_image image;
+ png_uint_32 opts;
+ const char *file_name;
+ int stride_extra;
+ FILE *input_file;
+ png_voidp input_memory;
+ png_size_t input_memory_size;
+ png_bytep buffer;
+ ptrdiff_t stride;
+ png_size_t bufsize;
+ png_size_t allocsize;
+ char tmpfile_name[32];
+ png_uint_16 colormap[256*4];
+}
+Image;
+
+/* Initializer: also sets the permitted error limit for 16-bit operations. */
+static void
+newimage(Image *image)
+{
+ memset(image, 0, sizeof *image);
+}
+
+/* Reset the image to be read again - only needs to rewind the FILE* at present.
+ */
+static void
+resetimage(Image *image)
+{
+ if (image->input_file != NULL)
+ rewind(image->input_file);
+}
+
+/* Free the image buffer; the buffer is re-used on a re-read, this is just for
+ * cleanup.
+ */
+static void
+freebuffer(Image *image)
+{
+ if (image->buffer) free(image->buffer);
+ image->buffer = NULL;
+ image->bufsize = 0;
+ image->allocsize = 0;
+}
+
+/* Delete function; cleans out all the allocated data and the temporary file in
+ * the image.
+ */
+static void
+freeimage(Image *image)
+{
+ freebuffer(image);
+ png_image_free(&image->image);
+
+ if (image->input_file != NULL)
+ {
+ fclose(image->input_file);
+ image->input_file = NULL;
+ }
+
+ if (image->input_memory != NULL)
+ {
+ free(image->input_memory);
+ image->input_memory = NULL;
+ image->input_memory_size = 0;
+ }
+
+ if (image->tmpfile_name[0] != 0 && (image->opts & KEEP_TMPFILES) == 0)
+ {
+ (void)remove(image->tmpfile_name);
+ image->tmpfile_name[0] = 0;
+ }
+}
+
+/* This is actually a re-initializer; allows an image structure to be re-used by
+ * freeing everything that relates to an old image.
+ */
+static void initimage(Image *image, png_uint_32 opts, const char *file_name,
+ int stride_extra)
+{
+ freeimage(image);
+ memset(&image->image, 0, sizeof image->image);
+ image->opts = opts;
+ image->file_name = file_name;
+ image->stride_extra = stride_extra;
+}
+
+/* Make sure the image buffer is big enough; allows re-use of the buffer if the
+ * image is re-read.
+ */
+#define BUFFER_INIT8 73
+static void
+allocbuffer(Image *image)
+{
+ png_size_t size = PNG_IMAGE_BUFFER_SIZE(image->image, image->stride);
+
+ if (size+32 > image->bufsize)
+ {
+ freebuffer(image);
+ image->buffer = voidcast(png_bytep, malloc(size+32));
+ if (image->buffer == NULL)
+ {
+ fflush(stdout);
+ fprintf(stderr,
+ "simpletest: out of memory allocating %lu(+32) byte buffer\n",
+ (unsigned long)size);
+ exit(1);
+ }
+ image->bufsize = size+32;
+ }
+
+ memset(image->buffer, 95, image->bufsize);
+ memset(image->buffer+16, BUFFER_INIT8, size);
+ image->allocsize = size;
+}
+
+/* Make sure 16 bytes match the given byte. */
+static int
+check16(png_const_bytep bp, int b)
+{
+ int i = 16;
+
+ do
+ if (*bp != b) return 1;
+ while (--i);
+
+ return 0;
+}
+
+/* Check for overwrite in the image buffer. */
+static void
+checkbuffer(Image *image, const char *arg)
+{
+ if (check16(image->buffer, 95))
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: overwrite at start of image buffer\n", arg);
+ exit(1);
+ }
+
+ if (check16(image->buffer+16+image->allocsize, 95))
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: overwrite at end of image buffer\n", arg);
+ exit(1);
+ }
+}
+
+/* ERROR HANDLING */
+/* Log a terminal error, also frees the libpng part of the image if necessary.
+ */
+static int
+logerror(Image *image, const char *a1, const char *a2, const char *a3)
+{
+ fflush(stdout);
+ if (image->image.warning_or_error)
+ fprintf(stderr, "%s%s%s: %s\n", a1, a2, a3, image->image.message);
+
+ else
+ fprintf(stderr, "%s%s%s\n", a1, a2, a3);
+
+ if (image->image.opaque != NULL)
+ {
+ fprintf(stderr, "%s: image opaque pointer non-NULL on error\n",
+ image->file_name);
+ png_image_free(&image->image);
+ }
+
+ return 0;
+}
+
+/* Log an error and close a file (just a utility to do both things in one
+ * function call.)
+ */
+static int
+logclose(Image *image, FILE *f, const char *name, const char *operation)
+{
+ int e = errno;
+
+ fclose(f);
+ return logerror(image, name, operation, strerror(e));
+}
+
+/* Make sure the png_image has been freed - validates that libpng is doing what
+ * the spec says and freeing the image.
+ */
+static int
+checkopaque(Image *image)
+{
+ if (image->image.opaque != NULL)
+ {
+ png_image_free(&image->image);
+ return logerror(image, image->file_name, ": opaque not NULL", "");
+ }
+
+ else if (image->image.warning_or_error != 0 && (image->opts & STRICT) != 0)
+ return logerror(image, image->file_name, " --strict", "");
+
+ else
+ return 1;
+}
+
+/* IMAGE COMPARISON/CHECKING */
+/* Compare the pixels of two images, which should be the same but aren't. The
+ * images must have been checked for a size match.
+ */
+typedef struct
+{
+ /* The components, for grayscale images the gray value is in 'g' and if alpha
+ * is not present 'a' is set to 255 or 65535 according to format.
+ */
+ int r, g, b, a;
+} Pixel;
+
+typedef struct
+{
+ /* The background as the original sRGB 8-bit value converted to the final
+ * integer format and as a double precision linear value in the range 0..1
+ * for with partially transparent pixels.
+ */
+ int ir, ig, ib;
+ double dr, dg, db; /* linear r,g,b scaled to 0..1 */
+} Background;
+
+/* Basic image formats; control the data but not the layout thereof. */
+#define BASE_FORMATS\
+ (PNG_FORMAT_FLAG_ALPHA|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_LINEAR)
+
+/* Read a Pixel from a buffer. The code below stores the correct routine for
+ * the format in a function pointer, these are the routines:
+ */
+static void
+gp_g8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = p->g = p->b = pp[0];
+ p->a = 255;
+}
+
+static void
+gp_ga8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = p->g = p->b = pp[0];
+ p->a = pp[1];
+}
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_ag8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = p->g = p->b = pp[1];
+ p->a = pp[0];
+}
+#endif
+
+static void
+gp_rgb8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = pp[0];
+ p->g = pp[1];
+ p->b = pp[2];
+ p->a = 255;
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgr8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = pp[2];
+ p->g = pp[1];
+ p->b = pp[0];
+ p->a = 255;
+}
+#endif
+
+static void
+gp_rgba8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = pp[0];
+ p->g = pp[1];
+ p->b = pp[2];
+ p->a = pp[3];
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgra8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = pp[2];
+ p->g = pp[1];
+ p->b = pp[0];
+ p->a = pp[3];
+}
+#endif
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_argb8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = pp[1];
+ p->g = pp[2];
+ p->b = pp[3];
+ p->a = pp[0];
+}
+#endif
+
+#if defined(PNG_FORMAT_AFIRST_SUPPORTED) && defined(PNG_FORMAT_BGR_SUPPORTED)
+static void
+gp_abgr8(Pixel *p, png_const_voidp pb)
+{
+ png_const_bytep pp = voidcast(png_const_bytep, pb);
+
+ p->r = pp[3];
+ p->g = pp[2];
+ p->b = pp[1];
+ p->a = pp[0];
+}
+#endif
+
+static void
+gp_g16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = p->g = p->b = pp[0];
+ p->a = 65535;
+}
+
+static void
+gp_ga16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = p->g = p->b = pp[0];
+ p->a = pp[1];
+}
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_ag16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = p->g = p->b = pp[1];
+ p->a = pp[0];
+}
+#endif
+
+static void
+gp_rgb16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = pp[0];
+ p->g = pp[1];
+ p->b = pp[2];
+ p->a = 65535;
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgr16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = pp[2];
+ p->g = pp[1];
+ p->b = pp[0];
+ p->a = 65535;
+}
+#endif
+
+static void
+gp_rgba16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = pp[0];
+ p->g = pp[1];
+ p->b = pp[2];
+ p->a = pp[3];
+}
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+static void
+gp_bgra16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = pp[2];
+ p->g = pp[1];
+ p->b = pp[0];
+ p->a = pp[3];
+}
+#endif
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+static void
+gp_argb16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = pp[1];
+ p->g = pp[2];
+ p->b = pp[3];
+ p->a = pp[0];
+}
+#endif
+
+#if defined(PNG_FORMAT_AFIRST_SUPPORTED) && defined(PNG_FORMAT_BGR_SUPPORTED)
+static void
+gp_abgr16(Pixel *p, png_const_voidp pb)
+{
+ png_const_uint_16p pp = voidcast(png_const_uint_16p, pb);
+
+ p->r = pp[3];
+ p->g = pp[2];
+ p->b = pp[1];
+ p->a = pp[0];
+}
+#endif
+
+/* Given a format, return the correct one of the above functions. */
+static void (*
+get_pixel(png_uint_32 format))(Pixel *p, png_const_voidp pb)
+{
+ /* The color-map flag is irrelevant here - the caller of the function
+ * returned must either pass the buffer or, for a color-mapped image, the
+ * correct entry in the color-map.
+ */
+ if (format & PNG_FORMAT_FLAG_LINEAR)
+ {
+ if (format & PNG_FORMAT_FLAG_COLOR)
+ {
+# ifdef PNG_FORMAT_BGR_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_BGR)
+ {
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ return gp_abgr16;
+
+ else
+# endif
+ return gp_bgra16;
+ }
+
+ else
+ return gp_bgr16;
+ }
+
+ else
+# endif
+ {
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ return gp_argb16;
+
+ else
+# endif
+ return gp_rgba16;
+ }
+
+ else
+ return gp_rgb16;
+ }
+ }
+
+ else
+ {
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ return gp_ag16;
+
+ else
+# endif
+ return gp_ga16;
+ }
+
+ else
+ return gp_g16;
+ }
+ }
+
+ else
+ {
+ if (format & PNG_FORMAT_FLAG_COLOR)
+ {
+# ifdef PNG_FORMAT_BGR_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_BGR)
+ {
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ return gp_abgr8;
+
+ else
+# endif
+ return gp_bgra8;
+ }
+
+ else
+ return gp_bgr8;
+ }
+
+ else
+# endif
+ {
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ return gp_argb8;
+
+ else
+# endif
+ return gp_rgba8;
+ }
+
+ else
+ return gp_rgb8;
+ }
+ }
+
+ else
+ {
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ return gp_ag8;
+
+ else
+# endif
+ return gp_ga8;
+ }
+
+ else
+ return gp_g8;
+ }
+ }
+}
+
+/* Convertion between pixel formats. The code above effectively eliminates the
+ * component ordering changes leaving three basic changes:
+ *
+ * 1) Remove an alpha channel by pre-multiplication or compositing on a
+ * background color. (Adding an alpha channel is a no-op.)
+ *
+ * 2) Remove color by mapping to grayscale. (Grayscale to color is a no-op.)
+ *
+ * 3) Convert between 8-bit and 16-bit components. (Both directtions are
+ * relevant.)
+ *
+ * This gives the following base format conversion matrix:
+ *
+ * OUT: ----- 8-bit ----- ----- 16-bit -----
+ * IN G GA RGB RGBA G GA RGB RGBA
+ * 8 G . . . . lin lin lin lin
+ * 8 GA bckg . bckc . pre' pre pre' pre
+ * 8 RGB g8 g8 . . glin glin lin lin
+ * 8 RGBA g8b g8 bckc . gpr' gpre pre' pre
+ * 16 G sRGB sRGB sRGB sRGB . . . .
+ * 16 GA b16g unpg b16c unpc A . A .
+ * 16 RGB sG sG sRGB sRGB g16 g16 . .
+ * 16 RGBA gb16 sGp cb16 sCp g16 g16' A .
+ *
+ * 8-bit to 8-bit:
+ * bckg: composite on gray background
+ * bckc: composite on color background
+ * g8: convert sRGB components to sRGB grayscale
+ * g8b: convert sRGB components to grayscale and composite on gray background
+ *
+ * 8-bit to 16-bit:
+ * lin: make sRGB components linear, alpha := 65535
+ * pre: make sRGB components linear and premultiply by alpha (scale alpha)
+ * pre': as 'pre' but alpha := 65535
+ * glin: make sRGB components linear, convert to grayscale, alpha := 65535
+ * gpre: make sRGB components grayscale and linear and premultiply by alpha
+ * gpr': as 'gpre' but alpha := 65535
+ *
+ * 16-bit to 8-bit:
+ * sRGB: convert linear components to sRGB, alpha := 255
+ * unpg: unpremultiply gray component and convert to sRGB (scale alpha)
+ * unpc: unpremultiply color components and convert to sRGB (scale alpha)
+ * b16g: composite linear onto gray background and convert the result to sRGB
+ * b16c: composite linear onto color background and convert the result to sRGB
+ * sG: convert linear RGB to sRGB grayscale
+ * sGp: unpremultiply RGB then convert to sRGB grayscale
+ * sCp: unpremultiply RGB then convert to sRGB
+ * gb16: composite linear onto background and convert to sRGB grayscale
+ * (order doesn't matter, the composite and grayscale operations permute)
+ * cb16: composite linear onto background and convert to sRGB
+ *
+ * 16-bit to 16-bit:
+ * A: set alpha to 65535
+ * g16: convert linear RGB to linear grayscale (alpha := 65535)
+ * g16': as 'g16' but alpha is unchanged
+ */
+/* Simple copy: */
+static void
+gpc_noop(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+ out->r = in->r;
+ out->g = in->g;
+ out->b = in->b;
+ out->a = in->a;
+}
+
+#if ALLOW_UNUSED_GPC
+static void
+gpc_nop8(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+ if (in->a == 0)
+ out->r = out->g = out->b = 255;
+
+ else
+ {
+ out->r = in->r;
+ out->g = in->g;
+ out->b = in->b;
+ }
+
+ out->a = in->a;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+static void
+gpc_nop6(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+ if (in->a == 0)
+ out->r = out->g = out->b = 65535;
+
+ else
+ {
+ out->r = in->r;
+ out->g = in->g;
+ out->b = in->b;
+ }
+
+ out->a = in->a;
+}
+#endif
+
+/* 8-bit to 8-bit conversions */
+/* bckg: composite on gray background */
+static void
+gpc_bckg(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ out->r = out->g = out->b = back->ig;
+
+ else if (in->a >= 255)
+ out->r = out->g = out->b = in->g;
+
+ else
+ {
+ double a = in->a / 255.;
+
+ out->r = out->g = out->b = sRGB(sRGB_to_d[in->g] * a + back->dg * (1-a));
+ }
+
+ out->a = 255;
+}
+
+/* bckc: composite on color background */
+static void
+gpc_bckc(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ {
+ out->r = back->ir;
+ out->g = back->ig;
+ out->b = back->ib;
+ }
+
+ else if (in->a >= 255)
+ {
+ out->r = in->r;
+ out->g = in->g;
+ out->b = in->b;
+ }
+
+ else
+ {
+ double a = in->a / 255.;
+
+ out->r = sRGB(sRGB_to_d[in->r] * a + back->dr * (1-a));
+ out->g = sRGB(sRGB_to_d[in->g] * a + back->dg * (1-a));
+ out->b = sRGB(sRGB_to_d[in->b] * a + back->db * (1-a));
+ }
+
+ out->a = 255;
+}
+
+/* g8: convert sRGB components to sRGB grayscale */
+static void
+gpc_g8(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = in->g;
+
+ else
+ out->r = out->g = out->b =
+ sRGB(YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+ out->a = in->a;
+}
+
+/* g8b: convert sRGB components to grayscale and composite on gray background */
+static void
+gpc_g8b(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ out->r = out->g = out->b = back->ig;
+
+ else if (in->a >= 255)
+ {
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = in->g;
+
+ else
+ out->r = out->g = out->b = sRGB(YfromRGB(
+ sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+ }
+
+ else
+ {
+ double a = in->a/255.;
+
+ out->r = out->g = out->b = sRGB(a * YfromRGB(sRGB_to_d[in->r],
+ sRGB_to_d[in->g], sRGB_to_d[in->b]) + back->dg * (1-a));
+ }
+
+ out->a = 255;
+}
+
+/* 8-bit to 16-bit conversions */
+/* lin: make sRGB components linear, alpha := 65535 */
+static void
+gpc_lin(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = ilinear(in->r);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = ilinear(in->b);
+ }
+
+ else
+ {
+ out->g = ilinear(in->g);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = ilinear(in->b);
+ }
+
+ out->a = 65535;
+}
+
+/* pre: make sRGB components linear and premultiply by alpha (scale alpha) */
+static void
+gpc_pre(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = ilineara(in->r, in->a);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = ilineara(in->b, in->a);
+ }
+
+ else
+ {
+ out->g = ilineara(in->g, in->a);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = ilineara(in->b, in->a);
+ }
+
+ out->a = in->a * 257;
+}
+
+/* pre': as 'pre' but alpha := 65535 */
+static void
+gpc_preq(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = ilineara(in->r, in->a);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = ilineara(in->b, in->a);
+ }
+
+ else
+ {
+ out->g = ilineara(in->g, in->a);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = ilineara(in->b, in->a);
+ }
+
+ out->a = 65535;
+}
+
+/* glin: make sRGB components linear, convert to grayscale, alpha := 65535 */
+static void
+gpc_glin(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = ilinear(in->g);
+
+ else
+ out->r = out->g = out->b = u16d(65535 *
+ YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+ out->a = 65535;
+}
+
+/* gpre: make sRGB components grayscale and linear and premultiply by alpha */
+static void
+gpc_gpre(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = ilineara(in->g, in->a);
+
+ else
+ out->r = out->g = out->b = u16d(in->a * 257 *
+ YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+ out->a = 257 * in->a;
+}
+
+/* gpr': as 'gpre' but alpha := 65535 */
+static void
+gpc_gprq(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = ilineara(in->g, in->a);
+
+ else
+ out->r = out->g = out->b = u16d(in->a * 257 *
+ YfromRGB(sRGB_to_d[in->r], sRGB_to_d[in->g], sRGB_to_d[in->b]));
+
+ out->a = 65535;
+}
+
+/* 8-bit to 16-bit conversions for gAMA 45455 encoded values */
+/* Lin: make gAMA 45455 components linear, alpha := 65535 */
+static void
+gpc_Lin(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = ilinear_g22(in->r);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = ilinear_g22(in->b);
+ }
+
+ else
+ {
+ out->g = ilinear_g22(in->g);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = ilinear_g22(in->b);
+ }
+
+ out->a = 65535;
+}
+
+#if ALLOW_UNUSED_GPC
+/* Pre: make gAMA 45455 components linear and premultiply by alpha (scale alpha)
+ */
+static void
+gpc_Pre(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = ilineara_g22(in->r, in->a);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = ilineara_g22(in->b, in->a);
+ }
+
+ else
+ {
+ out->g = ilineara_g22(in->g, in->a);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = ilineara_g22(in->b, in->a);
+ }
+
+ out->a = in->a * 257;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Pre': as 'Pre' but alpha := 65535 */
+static void
+gpc_Preq(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = ilineara_g22(in->r, in->a);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = ilineara_g22(in->b, in->a);
+ }
+
+ else
+ {
+ out->g = ilineara_g22(in->g, in->a);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = ilineara_g22(in->b, in->a);
+ }
+
+ out->a = 65535;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Glin: make gAMA 45455 components linear, convert to grayscale, alpha := 65535
+ */
+static void
+gpc_Glin(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = ilinear_g22(in->g);
+
+ else
+ out->r = out->g = out->b = u16d(65535 *
+ YfromRGB(g22_to_d[in->r], g22_to_d[in->g], g22_to_d[in->b]));
+
+ out->a = 65535;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Gpre: make gAMA 45455 components grayscale and linear and premultiply by
+ * alpha.
+ */
+static void
+gpc_Gpre(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = ilineara_g22(in->g, in->a);
+
+ else
+ out->r = out->g = out->b = u16d(in->a * 257 *
+ YfromRGB(g22_to_d[in->r], g22_to_d[in->g], g22_to_d[in->b]));
+
+ out->a = 257 * in->a;
+}
+#endif
+
+#if ALLOW_UNUSED_GPC
+/* Gpr': as 'Gpre' but alpha := 65535 */
+static void
+gpc_Gprq(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->r == in->g && in->g == in->b)
+ out->r = out->g = out->b = ilineara_g22(in->g, in->a);
+
+ else
+ out->r = out->g = out->b = u16d(in->a * 257 *
+ YfromRGB(g22_to_d[in->r], g22_to_d[in->g], g22_to_d[in->b]));
+
+ out->a = 65535;
+}
+#endif
+
+/* 16-bit to 8-bit conversions */
+/* sRGB: convert linear components to sRGB, alpha := 255 */
+static void
+gpc_sRGB(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = isRGB(in->r);
+
+ if (in->g == in->r)
+ {
+ out->g = out->r;
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else
+ out->b = isRGB(in->b);
+ }
+
+ else
+ {
+ out->g = isRGB(in->g);
+
+ if (in->b == in->r)
+ out->b = out->r;
+
+ else if (in->b == in->g)
+ out->b = out->g;
+
+ else
+ out->b = isRGB(in->b);
+ }
+
+ out->a = 255;
+}
+
+/* unpg: unpremultiply gray component and convert to sRGB (scale alpha) */
+static void
+gpc_unpg(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->a <= 128)
+ {
+ out->r = out->g = out->b = 255;
+ out->a = 0;
+ }
+
+ else
+ {
+ out->r = out->g = out->b = sRGB((double)in->g / in->a);
+ out->a = u8d(in->a / 257.);
+ }
+}
+
+/* unpc: unpremultiply color components and convert to sRGB (scale alpha) */
+static void
+gpc_unpc(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->a <= 128)
+ {
+ out->r = out->g = out->b = 255;
+ out->a = 0;
+ }
+
+ else
+ {
+ out->r = sRGB((double)in->r / in->a);
+ out->g = sRGB((double)in->g / in->a);
+ out->b = sRGB((double)in->b / in->a);
+ out->a = u8d(in->a / 257.);
+ }
+}
+
+/* b16g: composite linear onto gray background and convert the result to sRGB */
+static void
+gpc_b16g(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ out->r = out->g = out->b = back->ig;
+
+ else
+ {
+ double a = in->a/65535.;
+ double a1 = 1-a;
+
+ a /= 65535;
+ out->r = out->g = out->b = sRGB(in->g * a + back->dg * a1);
+ }
+
+ out->a = 255;
+}
+
+/* b16c: composite linear onto color background and convert the result to sRGB*/
+static void
+gpc_b16c(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ {
+ out->r = back->ir;
+ out->g = back->ig;
+ out->b = back->ib;
+ }
+
+ else
+ {
+ double a = in->a/65535.;
+ double a1 = 1-a;
+
+ a /= 65535;
+ out->r = sRGB(in->r * a + back->dr * a1);
+ out->g = sRGB(in->g * a + back->dg * a1);
+ out->b = sRGB(in->b * a + back->db * a1);
+ }
+
+ out->a = 255;
+}
+
+/* sG: convert linear RGB to sRGB grayscale */
+static void
+gpc_sG(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ out->r = out->g = out->b = sRGB(YfromRGBint(in->r, in->g, in->b)/65535);
+ out->a = 255;
+}
+
+/* sGp: unpremultiply RGB then convert to sRGB grayscale */
+static void
+gpc_sGp(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->a <= 128)
+ {
+ out->r = out->g = out->b = 255;
+ out->a = 0;
+ }
+
+ else
+ {
+ out->r = out->g = out->b = sRGB(YfromRGBint(in->r, in->g, in->b)/in->a);
+ out->a = u8d(in->a / 257.);
+ }
+}
+
+/* sCp: unpremultiply RGB then convert to sRGB */
+static void
+gpc_sCp(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+
+ if (in->a <= 128)
+ {
+ out->r = out->g = out->b = 255;
+ out->a = 0;
+ }
+
+ else
+ {
+ out->r = sRGB((double)in->r / in->a);
+ out->g = sRGB((double)in->g / in->a);
+ out->b = sRGB((double)in->b / in->a);
+ out->a = u8d(in->a / 257.);
+ }
+}
+
+/* gb16: composite linear onto background and convert to sRGB grayscale */
+/* (order doesn't matter, the composite and grayscale operations permute) */
+static void
+gpc_gb16(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ out->r = out->g = out->b = back->ig;
+
+ else if (in->a >= 65535)
+ out->r = out->g = out->b = isRGB(in->g);
+
+ else
+ {
+ double a = in->a / 65535.;
+ double a1 = 1-a;
+
+ a /= 65535;
+ out->r = out->g = out->b = sRGB(in->g * a + back->dg * a1);
+ }
+
+ out->a = 255;
+}
+
+/* cb16: composite linear onto background and convert to sRGB */
+static void
+gpc_cb16(Pixel *out, const Pixel *in, const Background *back)
+{
+ if (in->a <= 0)
+ {
+ out->r = back->ir;
+ out->g = back->ig;
+ out->b = back->ib;
+ }
+
+ else if (in->a >= 65535)
+ {
+ out->r = isRGB(in->r);
+ out->g = isRGB(in->g);
+ out->b = isRGB(in->b);
+ }
+
+ else
+ {
+ double a = in->a / 65535.;
+ double a1 = 1-a;
+
+ a /= 65535;
+ out->r = sRGB(in->r * a + back->dr * a1);
+ out->g = sRGB(in->g * a + back->dg * a1);
+ out->b = sRGB(in->b * a + back->db * a1);
+ }
+
+ out->a = 255;
+}
+
+/* 16-bit to 16-bit conversions */
+/* A: set alpha to 65535 */
+static void
+gpc_A(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+ out->r = in->r;
+ out->g = in->g;
+ out->b = in->b;
+ out->a = 65535;
+}
+
+/* g16: convert linear RGB to linear grayscale (alpha := 65535) */
+static void
+gpc_g16(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+ out->r = out->g = out->b = u16d(YfromRGBint(in->r, in->g, in->b));
+ out->a = 65535;
+}
+
+/* g16': as 'g16' but alpha is unchanged */
+static void
+gpc_g16q(Pixel *out, const Pixel *in, const Background *back)
+{
+ (void)back;
+ out->r = out->g = out->b = u16d(YfromRGBint(in->r, in->g, in->b));
+ out->a = in->a;
+}
+
+#if ALLOW_UNUSED_GPC
+/* Unused functions (to hide them from GCC unused function warnings) */
+void (* const gpc_unused[])
+ (Pixel *out, const Pixel *in, const Background *back) =
+{
+ gpc_Pre, gpc_Preq, gpc_Glin, gpc_Gpre, gpc_Gprq, gpc_nop8, gpc_nop6
+};
+#endif
+
+/* OUT: ----- 8-bit ----- ----- 16-bit -----
+ * IN G GA RGB RGBA G GA RGB RGBA
+ * 8 G . . . . lin lin lin lin
+ * 8 GA bckg . bckc . pre' pre pre' pre
+ * 8 RGB g8 g8 . . glin glin lin lin
+ * 8 RGBA g8b g8 bckc . gpr' gpre pre' pre
+ * 16 G sRGB sRGB sRGB sRGB . . . .
+ * 16 GA b16g unpg b16c unpc A . A .
+ * 16 RGB sG sG sRGB sRGB g16 g16 . .
+ * 16 RGBA gb16 sGp cb16 sCp g16 g16' A .
+ *
+ * The matrix is held in an array indexed thus:
+ *
+ * gpc_fn[out_format & BASE_FORMATS][in_format & BASE_FORMATS];
+ */
+/* This will produce a compile time error if the FORMAT_FLAG values don't
+ * match the above matrix!
+ */
+#if PNG_FORMAT_FLAG_ALPHA == 1 && PNG_FORMAT_FLAG_COLOR == 2 &&\
+ PNG_FORMAT_FLAG_LINEAR == 4
+static void (* const gpc_fn[8/*in*/][8/*out*/])
+ (Pixel *out, const Pixel *in, const Background *back) =
+{
+/*out: G-8 GA-8 RGB-8 RGBA-8 G-16 GA-16 RGB-16 RGBA-16 */
+ {gpc_noop,gpc_noop,gpc_noop,gpc_noop, gpc_Lin, gpc_Lin, gpc_Lin, gpc_Lin },
+ {gpc_bckg,gpc_noop,gpc_bckc,gpc_noop, gpc_preq,gpc_pre, gpc_preq,gpc_pre },
+ {gpc_g8, gpc_g8, gpc_noop,gpc_noop, gpc_glin,gpc_glin,gpc_lin, gpc_lin },
+ {gpc_g8b, gpc_g8, gpc_bckc,gpc_noop, gpc_gprq,gpc_gpre,gpc_preq,gpc_pre },
+ {gpc_sRGB,gpc_sRGB,gpc_sRGB,gpc_sRGB, gpc_noop,gpc_noop,gpc_noop,gpc_noop},
+ {gpc_b16g,gpc_unpg,gpc_b16c,gpc_unpc, gpc_A, gpc_noop,gpc_A, gpc_noop},
+ {gpc_sG, gpc_sG, gpc_sRGB,gpc_sRGB, gpc_g16, gpc_g16, gpc_noop,gpc_noop},
+ {gpc_gb16,gpc_sGp, gpc_cb16,gpc_sCp, gpc_g16, gpc_g16q,gpc_A, gpc_noop}
+};
+
+/* The array is repeated for the cases where both the input and output are color
+ * mapped because then different algorithms are used.
+ */
+static void (* const gpc_fn_colormapped[8/*in*/][8/*out*/])
+ (Pixel *out, const Pixel *in, const Background *back) =
+{
+/*out: G-8 GA-8 RGB-8 RGBA-8 G-16 GA-16 RGB-16 RGBA-16 */
+ {gpc_noop,gpc_noop,gpc_noop,gpc_noop, gpc_lin, gpc_lin, gpc_lin, gpc_lin },
+ {gpc_bckg,gpc_noop,gpc_bckc,gpc_noop, gpc_preq,gpc_pre, gpc_preq,gpc_pre },
+ {gpc_g8, gpc_g8, gpc_noop,gpc_noop, gpc_glin,gpc_glin,gpc_lin, gpc_lin },
+ {gpc_g8b, gpc_g8, gpc_bckc,gpc_noop, gpc_gprq,gpc_gpre,gpc_preq,gpc_pre },
+ {gpc_sRGB,gpc_sRGB,gpc_sRGB,gpc_sRGB, gpc_noop,gpc_noop,gpc_noop,gpc_noop},
+ {gpc_b16g,gpc_unpg,gpc_b16c,gpc_unpc, gpc_A, gpc_noop,gpc_A, gpc_noop},
+ {gpc_sG, gpc_sG, gpc_sRGB,gpc_sRGB, gpc_g16, gpc_g16, gpc_noop,gpc_noop},
+ {gpc_gb16,gpc_sGp, gpc_cb16,gpc_sCp, gpc_g16, gpc_g16q,gpc_A, gpc_noop}
+};
+
+/* The error arrays record the error in the same matrix; 64 entries, however
+ * the different algorithms used in libpng for colormap and direct conversions
+ * mean that four separate matrices are used (for each combination of
+ * colormapped and direct.)
+ *
+ * In some cases the conversion between sRGB formats goes via a linear
+ * intermediate; an sRGB to linear conversion (as above) is followed by a simple
+ * linear to sRGB step with no other conversions. This is done by a separate
+ * error array from an arbitrary 'in' format to one of the four basic outputs
+ * (since final output is always sRGB not colormapped).
+ *
+ * These arrays may be modified if the --accumulate flag is set during the run;
+ * then instead of logging errors they are simply added in.
+ *
+ * The three entries are currently for transparent, partially transparent and
+ * opaque input pixel values. Notice that alpha should be exact in each case.
+ *
+ * Errors in alpha should only occur when converting from a direct format
+ * to a colormapped format, when alpha is effectively smashed (so large
+ * errors can occur.) There should be no error in the '0' and 'opaque'
+ * values. The fourth entry in the array is used for the alpha error (and it
+ * should always be zero for the 'via linear' case since this is never color
+ * mapped.)
+ *
+ * Mapping to a colormap smashes the colors, it is necessary to have separate
+ * values for these cases because they are much larger; it is very much
+ * impossible to obtain a reasonable result, these are held in
+ * gpc_error_to_colormap.
+ */
+#if PNG_FORMAT_FLAG_COLORMAP == 8 /* extra check also required */
+# include "pngstest-errors.h" /* machine generated */
+#endif /* COLORMAP flag check */
+#endif /* flag checks */
+
+typedef struct
+{
+ /* Basic pixel information: */
+ Image* in_image; /* Input image */
+ const Image* out_image; /* Output image */
+
+ /* 'background' is the value passed to the gpc_ routines, it may be NULL if
+ * it should not be used (*this* program has an error if it crashes as a
+ * result!)
+ */
+ Background background_color;
+ const Background* background;
+
+ /* Precalculated values: */
+ int in_opaque; /* Value of input alpha that is opaque */
+ int is_palette; /* Sample values come from the palette */
+ int accumulate; /* Accumlate component errors (don't log) */
+ int output_8bit; /* Output is 8-bit (else 16-bit) */
+
+ void (*in_gp)(Pixel*, png_const_voidp);
+ void (*out_gp)(Pixel*, png_const_voidp);
+
+ void (*transform)(Pixel *out, const Pixel *in, const Background *back);
+ /* A function to perform the required transform */
+
+ void (*from_linear)(Pixel *out, const Pixel *in, const Background *back);
+ /* For 'via_linear' transforms the final, from linear, step, else NULL */
+
+ png_uint_16 error[4];
+ /* Three error values for transparent, partially transparent and opaque
+ * input pixels (in turn).
+ */
+
+ png_uint_16 *error_ptr;
+ /* Where these are stored in the static array (for 'accumulate') */
+}
+Transform;
+
+/* Return a 'transform' as above for the given format conversion. */
+static void
+transform_from_formats(Transform *result, Image *in_image,
+ const Image *out_image, png_const_colorp background, int via_linear)
+{
+ png_uint_32 in_format, out_format;
+ png_uint_32 in_base, out_base;
+
+ memset(result, 0, sizeof *result);
+
+ /* Store the original images for error messages */
+ result->in_image = in_image;
+ result->out_image = out_image;
+
+ in_format = in_image->image.format;
+ out_format = out_image->image.format;
+
+ if (in_format & PNG_FORMAT_FLAG_LINEAR)
+ result->in_opaque = 65535;
+ else
+ result->in_opaque = 255;
+
+ result->output_8bit = (out_format & PNG_FORMAT_FLAG_LINEAR) == 0;
+
+ result->is_palette = 0; /* set by caller if required */
+ result->accumulate = (in_image->opts & ACCUMULATE) != 0;
+
+ /* The loaders (which need the ordering information) */
+ result->in_gp = get_pixel(in_format);
+ result->out_gp = get_pixel(out_format);
+
+ /* Remove the ordering information: */
+ in_format &= BASE_FORMATS | PNG_FORMAT_FLAG_COLORMAP;
+ in_base = in_format & BASE_FORMATS;
+ out_format &= BASE_FORMATS | PNG_FORMAT_FLAG_COLORMAP;
+ out_base = out_format & BASE_FORMATS;
+
+ if (via_linear)
+ {
+ /* Check for an error in this program: */
+ if (out_format & (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLORMAP))
+ {
+ fprintf(stderr, "internal transform via linear error 0x%x->0x%x\n",
+ in_format, out_format);
+ exit(1);
+ }
+
+ result->transform = gpc_fn[in_base][out_base | PNG_FORMAT_FLAG_LINEAR];
+ result->from_linear = gpc_fn[out_base | PNG_FORMAT_FLAG_LINEAR][out_base];
+ result->error_ptr = gpc_error_via_linear[in_format][out_format];
+ }
+
+ else if (~in_format & out_format & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ /* The input is not colormapped but the output is, the errors will
+ * typically be large (only the grayscale-no-alpha case permits preserving
+ * even 8-bit values.)
+ */
+ result->transform = gpc_fn[in_base][out_base];
+ result->from_linear = NULL;
+ result->error_ptr = gpc_error_to_colormap[in_base][out_base];
+ }
+
+ else
+ {
+ /* The caller handles the colormap->pixel value conversion, so the
+ * transform function just gets a pixel value, however because libpng
+ * currently contains a different implementation for mapping a colormap if
+ * both input and output are colormapped we need different conversion
+ * functions to deal with errors in the libpng implementation.
+ */
+ if (in_format & out_format & PNG_FORMAT_FLAG_COLORMAP)
+ result->transform = gpc_fn_colormapped[in_base][out_base];
+ else
+ result->transform = gpc_fn[in_base][out_base];
+ result->from_linear = NULL;
+ result->error_ptr = gpc_error[in_format][out_format];
+ }
+
+ /* Follow the libpng simplified API rules to work out what to pass to the gpc
+ * routines as a background value, if one is not required pass NULL so that
+ * this program crashes in the even of a programming error.
+ */
+ result->background = NULL; /* default: not required */
+
+ /* Rule 1: background only need be supplied if alpha is to be removed */
+ if (in_format & ~out_format & PNG_FORMAT_FLAG_ALPHA)
+ {
+ /* The input value is 'NULL' to use the background and (otherwise) an sRGB
+ * background color (to use a solid color). The code above uses a fixed
+ * byte value, BUFFER_INIT8, for buffer even for 16-bit output. For
+ * linear (16-bit) output the sRGB background color is ignored; the
+ * composition is always on the background (so BUFFER_INIT8 * 257), except
+ * that for the colormap (i.e. linear colormapped output) black is used.
+ */
+ result->background = &result->background_color;
+
+ if (out_format & PNG_FORMAT_FLAG_LINEAR || via_linear)
+ {
+ if (out_format & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ result->background_color.ir =
+ result->background_color.ig =
+ result->background_color.ib = 0;
+ result->background_color.dr =
+ result->background_color.dg =
+ result->background_color.db = 0;
+ }
+
+ else
+ {
+ result->background_color.ir =
+ result->background_color.ig =
+ result->background_color.ib = BUFFER_INIT8 * 257;
+ result->background_color.dr =
+ result->background_color.dg =
+ result->background_color.db = 0;
+ }
+ }
+
+ else /* sRGB output */
+ {
+ if (background != NULL)
+ {
+ if (out_format & PNG_FORMAT_FLAG_COLOR)
+ {
+ result->background_color.ir = background->red;
+ result->background_color.ig = background->green;
+ result->background_color.ib = background->blue;
+ /* TODO: sometimes libpng uses the power law conversion here, how
+ * to handle this?
+ */
+ result->background_color.dr = sRGB_to_d[background->red];
+ result->background_color.dg = sRGB_to_d[background->green];
+ result->background_color.db = sRGB_to_d[background->blue];
+ }
+
+ else /* grayscale: libpng only looks at 'g' */
+ {
+ result->background_color.ir =
+ result->background_color.ig =
+ result->background_color.ib = background->green;
+ /* TODO: sometimes libpng uses the power law conversion here, how
+ * to handle this?
+ */
+ result->background_color.dr =
+ result->background_color.dg =
+ result->background_color.db = sRGB_to_d[background->green];
+ }
+ }
+
+ else if ((out_format & PNG_FORMAT_FLAG_COLORMAP) == 0)
+ {
+ result->background_color.ir =
+ result->background_color.ig =
+ result->background_color.ib = BUFFER_INIT8;
+ /* TODO: sometimes libpng uses the power law conversion here, how
+ * to handle this?
+ */
+ result->background_color.dr =
+ result->background_color.dg =
+ result->background_color.db = sRGB_to_d[BUFFER_INIT8];
+ }
+
+ /* Else the output is colormapped and a background color must be
+ * provided; if pngstest crashes then that is a bug in this program
+ * (though libpng should png_error as well.)
+ */
+ else
+ result->background = NULL;
+ }
+ }
+
+ if (result->background == NULL)
+ {
+ result->background_color.ir =
+ result->background_color.ig =
+ result->background_color.ib = -1; /* not used */
+ result->background_color.dr =
+ result->background_color.dg =
+ result->background_color.db = 1E30; /* not used */
+ }
+
+
+ /* Copy the error values into the Transform: */
+ result->error[0] = result->error_ptr[0];
+ result->error[1] = result->error_ptr[1];
+ result->error[2] = result->error_ptr[2];
+ result->error[3] = result->error_ptr[3];
+}
+
+
+/* Compare two pixels.
+ *
+ * OLD error values:
+static int error_to_linear = 811; * by experiment *
+static int error_to_linear_grayscale = 424; * by experiment *
+static int error_to_sRGB = 6; * by experiment *
+static int error_to_sRGB_grayscale = 17; * libpng error by calculation +
+ 2 by experiment *
+static int error_in_compose = 2; * by experiment *
+static int error_in_premultiply = 1;
+ *
+ * The following is *just* the result of a round trip from 8-bit sRGB to linear
+ * then back to 8-bit sRGB when it is done by libpng. There are two problems:
+ *
+ * 1) libpng currently uses a 2.2 power law with no linear segment, this results
+ * in instability in the low values and even with 16-bit precision sRGB(1) ends
+ * up mapping to sRGB(0) as a result of rounding in the 16-bit representation.
+ * This gives an error of 1 in the handling of value 1 only.
+ *
+ * 2) libpng currently uses an intermediate 8-bit linear value in gamma
+ * correction of 8-bit values. This results in many more errors, the worse of
+ * which is mapping sRGB(14) to sRGB(0).
+ *
+ * The general 'error_via_linear' is more complex because of pre-multiplication,
+ * this compounds the 8-bit errors according to the alpha value of the pixel.
+ * As a result 256 values are pre-calculated for error_via_linear.
+ */
+#if 0
+static int error_in_libpng_gamma;
+static int error_via_linear[256]; /* Indexed by 8-bit alpha */
+
+static void
+init_error_via_linear(void)
+{
+ int alpha;
+
+ error_via_linear[0] = 255; /* transparent pixel */
+
+ for (alpha=1; alpha<=255; ++alpha)
+ {
+ /* 16-bit values less than 128.5 get rounded to 8-bit 0 and so the worst
+ * case error arises with 16-bit 128.5, work out what sRGB
+ * (non-associated) value generates 128.5; any value less than this is
+ * going to map to 0, so the worst error is floor(value).
+ *
+ * Note that errors are considerably higher (more than a factor of 2)
+ * because libpng uses a simple power law for sRGB data at present.
+ *
+ * Add .1 for arithmetic errors inside libpng.
+ */
+ double v = floor(255*pow(.5/*(128.5 * 255 / 65535)*/ / alpha, 1/2.2)+.1);
+
+ error_via_linear[alpha] = (int)v;
+ }
+
+ /* This is actually 14.99, but, despite the closeness to 15, 14 seems to work
+ * ok in this case.
+ */
+ error_in_libpng_gamma = 14;
+}
+#endif
+
+static void
+print_pixel(char string[64], const Pixel *pixel, png_uint_32 format)
+{
+ switch (format & (PNG_FORMAT_FLAG_ALPHA|PNG_FORMAT_FLAG_COLOR))
+ {
+ case 0:
+ sprintf(string, "%s(%d)", format_names[format], pixel->g);
+ break;
+
+ case PNG_FORMAT_FLAG_ALPHA:
+ sprintf(string, "%s(%d,%d)", format_names[format], pixel->g,
+ pixel->a);
+ break;
+
+ case PNG_FORMAT_FLAG_COLOR:
+ sprintf(string, "%s(%d,%d,%d)", format_names[format],
+ pixel->r, pixel->g, pixel->b);
+ break;
+
+ case PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA:
+ sprintf(string, "%s(%d,%d,%d,%d)", format_names[format],
+ pixel->r, pixel->g, pixel->b, pixel->a);
+ break;
+
+ default:
+ sprintf(string, "invalid-format");
+ break;
+ }
+}
+
+static int
+logpixel(const Transform *transform, png_uint_32 x, png_uint_32 y,
+ const Pixel *in, const Pixel *calc, const Pixel *out, const char *reason)
+{
+ const png_uint_32 in_format = transform->in_image->image.format;
+ const png_uint_32 out_format = transform->out_image->image.format;
+
+ png_uint_32 back_format = out_format & ~PNG_FORMAT_FLAG_ALPHA;
+ const char *via_linear = "";
+
+ char pixel_in[64], pixel_calc[64], pixel_out[64], pixel_loc[64];
+ char background_info[100];
+
+ print_pixel(pixel_in, in, in_format);
+ print_pixel(pixel_calc, calc, out_format);
+ print_pixel(pixel_out, out, out_format);
+
+ if (transform->is_palette)
+ sprintf(pixel_loc, "palette: %lu", (unsigned long)y);
+ else
+ sprintf(pixel_loc, "%lu,%lu", (unsigned long)x, (unsigned long)y);
+
+ if (transform->from_linear != NULL)
+ {
+ via_linear = " (via linear)";
+ /* And as a result the *read* format which did any background processing
+ * was itself linear, so the background color information is also
+ * linear.
+ */
+ back_format |= PNG_FORMAT_FLAG_LINEAR;
+ }
+
+ if (transform->background != NULL)
+ {
+ Pixel back;
+ char pixel_back[64];
+
+ back.r = transform->background->ir;
+ back.g = transform->background->ig;
+ back.b = transform->background->ib;
+ back.a = -1; /* not used */
+
+ print_pixel(pixel_back, &back, back_format);
+ sprintf(background_info, " on background %s", pixel_back);
+ }
+
+ else
+ background_info[0] = 0;
+
+ if (transform->in_image->file_name != transform->out_image->file_name)
+ {
+ char error_buffer[512];
+ sprintf(error_buffer,
+ "(%s) %s error%s:\n %s%s ->\n %s\n not: %s.\n"
+ "Use --preserve and examine: ", pixel_loc, reason, via_linear,
+ pixel_in, background_info, pixel_out, pixel_calc);
+ return logerror(transform->in_image, transform->in_image->file_name,
+ error_buffer, transform->out_image->file_name);
+ }
+
+ else
+ {
+ char error_buffer[512];
+ sprintf(error_buffer,
+ "(%s) %s error%s:\n %s%s ->\n %s\n not: %s.\n"
+ " The error happened when reading the original file with this format.",
+ pixel_loc, reason, via_linear, pixel_in, background_info, pixel_out,
+ pixel_calc);
+ return logerror(transform->in_image, transform->in_image->file_name,
+ error_buffer, "");
+ }
+}
+
+static int
+cmppixel(Transform *transform, png_const_voidp in, png_const_voidp out,
+ png_uint_32 x, png_uint_32 y/*or palette index*/)
+{
+ int maxerr;
+ png_const_charp errmsg;
+ Pixel pixel_in, pixel_calc, pixel_out;
+
+ transform->in_gp(&pixel_in, in);
+
+ if (transform->from_linear == NULL)
+ transform->transform(&pixel_calc, &pixel_in, transform->background);
+
+ else
+ {
+ transform->transform(&pixel_out, &pixel_in, transform->background);
+ transform->from_linear(&pixel_calc, &pixel_out, NULL);
+ }
+
+ transform->out_gp(&pixel_out, out);
+
+ /* Eliminate the case where the input and output values match exactly. */
+ if (pixel_calc.a == pixel_out.a && pixel_calc.r == pixel_out.r &&
+ pixel_calc.g == pixel_out.g && pixel_calc.b == pixel_out.b)
+ return 1;
+
+ /* Eliminate the case where the output pixel is transparent and the output
+ * is 8-bit - any component values are valid. Don't check the input alpha
+ * here to also skip the 16-bit small alpha cases.
+ */
+ if (transform->output_8bit && pixel_calc.a == 0 && pixel_out.a == 0)
+ return 1;
+
+ /* Check for alpha errors first; an alpha error can damage the components too
+ * so avoid spurious checks on components if one is found.
+ */
+ errmsg = NULL;
+ {
+ int err_a = abs(pixel_calc.a-pixel_out.a);
+
+ if (err_a > transform->error[3])
+ {
+ /* If accumulating check the components too */
+ if (transform->accumulate)
+ transform->error[3] = (png_uint_16)err_a;
+
+ else
+ errmsg = "alpha";
+ }
+ }
+
+ /* Now if *either* of the output alphas are 0 but alpha is within tolerance
+ * eliminate the 8-bit component comparison.
+ */
+ if (errmsg == NULL && transform->output_8bit &&
+ (pixel_calc.a == 0 || pixel_out.a == 0))
+ return 1;
+
+ if (errmsg == NULL) /* else just signal an alpha error */
+ {
+ int err_r = abs(pixel_calc.r - pixel_out.r);
+ int err_g = abs(pixel_calc.g - pixel_out.g);
+ int err_b = abs(pixel_calc.b - pixel_out.b);
+ int limit;
+
+ if ((err_r | err_g | err_b) == 0)
+ return 1; /* exact match */
+
+ /* Mismatch on a component, check the input alpha */
+ if (pixel_in.a >= transform->in_opaque)
+ {
+ errmsg = "opaque component";
+ limit = 2; /* opaque */
+ }
+
+ else if (pixel_in.a > 0)
+ {
+ errmsg = "alpha component";
+ limit = 1; /* partially transparent */
+ }
+
+ else
+ {
+ errmsg = "transparent component (background)";
+ limit = 0; /* transparent */
+ }
+
+ maxerr = err_r;
+ if (maxerr < err_g) maxerr = err_g;
+ if (maxerr < err_b) maxerr = err_b;
+
+ if (maxerr <= transform->error[limit])
+ return 1; /* within the error limits */
+
+ /* Handle a component mis-match; log it, just return an error code, or
+ * accumulate it.
+ */
+ if (transform->accumulate)
+ {
+ transform->error[limit] = (png_uint_16)maxerr;
+ return 1; /* to cause the caller to keep going */
+ }
+ }
+
+ /* Failure to match and not accumulating, so the error must be logged. */
+ return logpixel(transform, x, y, &pixel_in, &pixel_calc, &pixel_out, errmsg);
+}
+
+static png_byte
+component_loc(png_byte loc[4], png_uint_32 format)
+{
+ /* Given a format return the number of channels and the location of
+ * each channel.
+ *
+ * The mask 'loc' contains the component offset of the channels in the
+ * following order. Note that if 'format' is grayscale the entries 1-3 must
+ * all contain the location of the gray channel.
+ *
+ * 0: alpha
+ * 1: red or gray
+ * 2: green or gray
+ * 3: blue or gray
+ */
+ png_byte channels;
+
+ if (format & PNG_FORMAT_FLAG_COLOR)
+ {
+ channels = 3;
+
+ loc[2] = 1;
+
+# ifdef PNG_FORMAT_BGR_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_BGR)
+ {
+ loc[1] = 2;
+ loc[3] = 0;
+ }
+
+ else
+# endif
+ {
+ loc[1] = 0;
+ loc[3] = 2;
+ }
+ }
+
+ else
+ {
+ channels = 1;
+ loc[1] = loc[2] = loc[3] = 0;
+ }
+
+ if (format & PNG_FORMAT_FLAG_ALPHA)
+ {
+# ifdef PNG_FORMAT_AFIRST_SUPPORTED
+ if (format & PNG_FORMAT_FLAG_AFIRST)
+ {
+ loc[0] = 0;
+ ++loc[1];
+ ++loc[2];
+ ++loc[3];
+ }
+
+ else
+# endif
+ loc[0] = channels;
+
+ ++channels;
+ }
+
+ else
+ loc[0] = 4; /* not present */
+
+ return channels;
+}
+
+/* Compare two images, the original 'a', which was written out then read back in
+ * to * give image 'b'. The formats may have been changed.
+ */
+static int
+compare_two_images(Image *a, Image *b, int via_linear,
+ png_const_colorp background)
+{
+ ptrdiff_t stridea = a->stride;
+ ptrdiff_t strideb = b->stride;
+ png_const_bytep rowa = a->buffer+16;
+ png_const_bytep rowb = b->buffer+16;
+ const png_uint_32 width = a->image.width;
+ const png_uint_32 height = a->image.height;
+ const png_uint_32 formata = a->image.format;
+ const png_uint_32 formatb = b->image.format;
+ const unsigned int a_sample = PNG_IMAGE_SAMPLE_SIZE(formata);
+ const unsigned int b_sample = PNG_IMAGE_SAMPLE_SIZE(formatb);
+ int alpha_added, alpha_removed;
+ int bchannels;
+ int btoa[4];
+ png_uint_32 y;
+ Transform tr;
+
+ /* This should never happen: */
+ if (width != b->image.width || height != b->image.height)
+ return logerror(a, a->file_name, ": width x height changed: ",
+ b->file_name);
+
+ /* Set up the background and the transform */
+ transform_from_formats(&tr, a, b, background, via_linear);
+
+ /* Find the first row and inter-row space. */
+ if (!(formata & PNG_FORMAT_FLAG_COLORMAP) &&
+ (formata & PNG_FORMAT_FLAG_LINEAR))
+ stridea *= 2;
+
+ if (!(formatb & PNG_FORMAT_FLAG_COLORMAP) &&
+ (formatb & PNG_FORMAT_FLAG_LINEAR))
+ strideb *= 2;
+
+ if (stridea < 0) rowa += (height-1) * (-stridea);
+ if (strideb < 0) rowb += (height-1) * (-strideb);
+
+ /* First shortcut the two colormap case by comparing the image data; if it
+ * matches then we expect the colormaps to match, although this is not
+ * absolutely necessary for an image match. If the colormaps fail to match
+ * then there is a problem in libpng.
+ */
+ if (formata & formatb & PNG_FORMAT_FLAG_COLORMAP)
+ {
+ /* Only check colormap entries that actually exist; */
+ png_const_bytep ppa, ppb;
+ int match;
+ png_byte in_use[256], amax = 0, bmax = 0;
+
+ memset(in_use, 0, sizeof in_use);
+
+ ppa = rowa;
+ ppb = rowb;
+
+ /* Do this the slow way to accumulate the 'in_use' flags, don't break out
+ * of the loop until the end; this validates the color-mapped data to
+ * ensure all pixels are valid color-map indexes.
+ */
+ for (y=0, match=1; y<height && match; ++y, ppa += stridea, ppb += strideb)
+ {
+ png_uint_32 x;
+
+ for (x=0; x<width; ++x)
+ {
+ png_byte bval = ppb[x];
+ png_byte aval = ppa[x];
+
+ if (bval > bmax)
+ bmax = bval;
+
+ if (bval != aval)
+ match = 0;
+
+ in_use[aval] = 1;
+ if (aval > amax)
+ amax = aval;
+ }
+ }
+
+ /* If the buffers match then the colormaps must too. */
+ if (match)
+ {
+ /* Do the color-maps match, entry by entry? Only check the 'in_use'
+ * entries. An error here should be logged as a color-map error.
+ */
+ png_const_bytep a_cmap = (png_const_bytep)a->colormap;
+ png_const_bytep b_cmap = (png_const_bytep)b->colormap;
+ int result = 1; /* match by default */
+
+ /* This is used in logpixel to get the error message correct. */
+ tr.is_palette = 1;
+
+ for (y=0; y<256; ++y, a_cmap += a_sample, b_cmap += b_sample)
+ if (in_use[y])
+ {
+ /* The colormap entries should be valid, but because libpng doesn't
+ * do any checking at present the original image may contain invalid
+ * pixel values. These cause an error here (at present) unless
+ * accumulating errors in which case the program just ignores them.
+ */
+ if (y >= a->image.colormap_entries)
+ {
+ if ((a->opts & ACCUMULATE) == 0)
+ {
+ char pindex[9];
+ sprintf(pindex, "%lu[%lu]", (unsigned long)y,
+ (unsigned long)a->image.colormap_entries);
+ logerror(a, a->file_name, ": bad pixel index: ", pindex);
+ }
+ result = 0;
+ }
+
+ else if (y >= b->image.colormap_entries)
+ {
+ if ((b->opts & ACCUMULATE) == 0)
+ {
+ char pindex[9];
+ sprintf(pindex, "%lu[%lu]", (unsigned long)y,
+ (unsigned long)b->image.colormap_entries);
+ logerror(b, b->file_name, ": bad pixel index: ", pindex);
+ }
+ result = 0;
+ }
+
+ /* All the mismatches are logged here; there can only be 256! */
+ else if (!cmppixel(&tr, a_cmap, b_cmap, 0, y))
+ result = 0;
+ }
+
+ /* If reqested copy the error values back from the Transform. */
+ if (a->opts & ACCUMULATE)
+ {
+ tr.error_ptr[0] = tr.error[0];
+ tr.error_ptr[1] = tr.error[1];
+ tr.error_ptr[2] = tr.error[2];
+ tr.error_ptr[3] = tr.error[3];
+ result = 1; /* force a continue */
+ }
+
+ return result;
+ }
+
+ /* else the image buffers don't match pixel-wise so compare sample values
+ * instead, but first validate that the pixel indexes are in range (but
+ * only if not accumulating, when the error is ignored.)
+ */
+ else if ((a->opts & ACCUMULATE) == 0)
+ {
+ /* Check the original image first,
+ * TODO: deal with input images with bad pixel values?
+ */
+ if (amax >= a->image.colormap_entries)
+ {
+ char pindex[9];
+ sprintf(pindex, "%d[%lu]", amax,
+ (unsigned long)a->image.colormap_entries);
+ return logerror(a, a->file_name, ": bad pixel index: ", pindex);
+ }
+
+ else if (bmax >= b->image.colormap_entries)
+ {
+ char pindex[9];
+ sprintf(pindex, "%d[%lu]", bmax,
+ (unsigned long)b->image.colormap_entries);
+ return logerror(b, b->file_name, ": bad pixel index: ", pindex);
+ }
+ }
+ }
+
+ /* We can directly compare pixel values without the need to use the read
+ * or transform support (i.e. a memory compare) if:
+ *
+ * 1) The bit depth has not changed.
+ * 2) RGB to grayscale has not been done (the reverse is ok; we just compare
+ * the three RGB values to the original grayscale.)
+ * 3) An alpha channel has not been removed from an 8-bit format, or the
+ * 8-bit alpha value of the pixel was 255 (opaque).
+ *
+ * If an alpha channel has been *added* then it must have the relevant opaque
+ * value (255 or 65535).
+ *
+ * The fist two the tests (in the order given above) (using the boolean
+ * equivalence !a && !b == !(a || b))
+ */
+ if (!(((formata ^ formatb) & PNG_FORMAT_FLAG_LINEAR) |
+ (formata & (formatb ^ PNG_FORMAT_FLAG_COLOR) & PNG_FORMAT_FLAG_COLOR)))
+ {
+ /* Was an alpha channel changed? */
+ const png_uint_32 alpha_changed = (formata ^ formatb) &
+ PNG_FORMAT_FLAG_ALPHA;
+
+ /* Was an alpha channel removed? (The third test.) If so the direct
+ * comparison is only possible if the input alpha is opaque.
+ */
+ alpha_removed = (formata & alpha_changed) != 0;
+
+ /* Was an alpha channel added? */
+ alpha_added = (formatb & alpha_changed) != 0;
+
+ /* The channels may have been moved between input and output, this finds
+ * out how, recording the result in the btoa array, which says where in
+ * 'a' to find each channel of 'b'. If alpha was added then btoa[alpha]
+ * ends up as 4 (and is not used.)
+ */
+ {
+ int i;
+ png_byte aloc[4];
+ png_byte bloc[4];
+
+ /* The following are used only if the formats match, except that
+ * 'bchannels' is a flag for matching formats. btoa[x] says, for each
+ * channel in b, where to find the corresponding value in a, for the
+ * bchannels. achannels may be different for a gray to rgb transform
+ * (a will be 1 or 2, b will be 3 or 4 channels.)
+ */
+ (void)component_loc(aloc, formata);
+ bchannels = component_loc(bloc, formatb);
+
+ /* Hence the btoa array. */
+ for (i=0; i<4; ++i) if (bloc[i] < 4)
+ btoa[bloc[i]] = aloc[i]; /* may be '4' for alpha */
+
+ if (alpha_added)
+ alpha_added = bloc[0]; /* location of alpha channel in image b */
+
+ else
+ alpha_added = 4; /* Won't match an image b channel */
+
+ if (alpha_removed)
+ alpha_removed = aloc[0]; /* location of alpha channel in image a */
+
+ else
+ alpha_removed = 4;
+ }
+ }
+
+ else
+ {
+ /* Direct compare is not possible, cancel out all the corresponding local
+ * variables.
+ */
+ bchannels = 0;
+ alpha_removed = alpha_added = 4;
+ btoa[3] = btoa[2] = btoa[1] = btoa[0] = 4; /* 4 == not present */
+ }
+
+ for (y=0; y<height; ++y, rowa += stridea, rowb += strideb)
+ {
+ png_const_bytep ppa, ppb;
+ png_uint_32 x;
+
+ for (x=0, ppa=rowa, ppb=rowb; x<width; ++x)
+ {
+ png_const_bytep psa, psb;
+
+ if (formata & PNG_FORMAT_FLAG_COLORMAP)
+ psa = (png_const_bytep)a->colormap + a_sample * *ppa++;
+ else
+ psa = ppa, ppa += a_sample;
+
+ if (formatb & PNG_FORMAT_FLAG_COLORMAP)
+ psb = (png_const_bytep)b->colormap + b_sample * *ppb++;
+ else
+ psb = ppb, ppb += b_sample;
+
+ /* Do the fast test if possible. */
+ if (bchannels)
+ {
+ /* Check each 'b' channel against either the corresponding 'a'
+ * channel or the opaque alpha value, as appropriate. If
+ * alpha_removed value is set (not 4) then also do this only if the
+ * 'a' alpha channel (alpha_removed) is opaque; only relevant for
+ * the 8-bit case.
+ */
+ if (formatb & PNG_FORMAT_FLAG_LINEAR) /* 16-bit checks */
+ {
+ png_const_uint_16p pua = aligncastconst(png_const_uint_16p, psa);
+ png_const_uint_16p pub = aligncastconst(png_const_uint_16p, psb);
+
+ switch (bchannels)
+ {
+ case 4:
+ if (pua[btoa[3]] != pub[3]) break;
+ case 3:
+ if (pua[btoa[2]] != pub[2]) break;
+ case 2:
+ if (pua[btoa[1]] != pub[1]) break;
+ case 1:
+ if (pua[btoa[0]] != pub[0]) break;
+ if (alpha_added != 4 && pub[alpha_added] != 65535) break;
+ continue; /* x loop */
+ default:
+ break; /* impossible */
+ }
+ }
+
+ else if (alpha_removed == 4 || psa[alpha_removed] == 255)
+ {
+ switch (bchannels)
+ {
+ case 4:
+ if (psa[btoa[3]] != psb[3]) break;
+ case 3:
+ if (psa[btoa[2]] != psb[2]) break;
+ case 2:
+ if (psa[btoa[1]] != psb[1]) break;
+ case 1:
+ if (psa[btoa[0]] != psb[0]) break;
+ if (alpha_added != 4 && psb[alpha_added] != 255) break;
+ continue; /* x loop */
+ default:
+ break; /* impossible */
+ }
+ }
+ }
+
+ /* If we get to here the fast match failed; do the slow match for this
+ * pixel.
+ */
+ if (!cmppixel(&tr, psa, psb, x, y) && (a->opts & KEEP_GOING) == 0)
+ return 0; /* error case */
+ }
+ }
+
+ /* If reqested copy the error values back from the Transform. */
+ if (a->opts & ACCUMULATE)
+ {
+ tr.error_ptr[0] = tr.error[0];
+ tr.error_ptr[1] = tr.error[1];
+ tr.error_ptr[2] = tr.error[2];
+ tr.error_ptr[3] = tr.error[3];
+ }
+
+ return 1;
+}
+
+/* Read the file; how the read gets done depends on which of input_file and
+ * input_memory have been set.
+ */
+static int
+read_file(Image *image, png_uint_32 format, png_const_colorp background)
+{
+ memset(&image->image, 0, sizeof image->image);
+ image->image.version = PNG_IMAGE_VERSION;
+
+ if (image->input_memory != NULL)
+ {
+ if (!png_image_begin_read_from_memory(&image->image, image->input_memory,
+ image->input_memory_size))
+ return logerror(image, "memory init: ", image->file_name, "");
+ }
+
+# ifdef PNG_STDIO_SUPPORTED
+ else if (image->input_file != NULL)
+ {
+ if (!png_image_begin_read_from_stdio(&image->image, image->input_file))
+ return logerror(image, "stdio init: ", image->file_name, "");
+ }
+
+ else
+ {
+ if (!png_image_begin_read_from_file(&image->image, image->file_name))
+ return logerror(image, "file init: ", image->file_name, "");
+ }
+# else
+ else
+ {
+ return logerror(image, "unsupported file/stdio init: ",
+ image->file_name, "");
+ }
+# endif
+
+ /* This must be set after the begin_read call: */
+ if (image->opts & sRGB_16BIT)
+ image->image.flags |= PNG_IMAGE_FLAG_16BIT_sRGB;
+
+ /* Have an initialized image with all the data we need plus, maybe, an
+ * allocated file (myfile) or buffer (mybuffer) that need to be freed.
+ */
+ {
+ int result;
+ png_uint_32 image_format;
+
+ /* Print both original and output formats. */
+ image_format = image->image.format;
+
+ if (image->opts & VERBOSE)
+ {
+ printf("%s %lu x %lu %s -> %s", image->file_name,
+ (unsigned long)image->image.width,
+ (unsigned long)image->image.height,
+ format_names[image_format & FORMAT_MASK],
+ (format & FORMAT_NO_CHANGE) != 0 || image->image.format == format
+ ? "no change" : format_names[format & FORMAT_MASK]);
+
+ if (background != NULL)
+ printf(" background(%d,%d,%d)\n", background->red,
+ background->green, background->blue);
+ else
+ printf("\n");
+
+ fflush(stdout);
+ }
+
+ /* 'NO_CHANGE' combined with the color-map flag forces the base format
+ * flags to be set on read to ensure that the original representation is
+ * not lost in the pass through a colormap format.
+ */
+ if ((format & FORMAT_NO_CHANGE) != 0)
+ {
+ if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+ (image_format & PNG_FORMAT_FLAG_COLORMAP) != 0)
+ format = (image_format & ~BASE_FORMATS) | (format & BASE_FORMATS);
+
+ else
+ format = image_format;
+ }
+
+ image->image.format = format;
+
+ image->stride = PNG_IMAGE_ROW_STRIDE(image->image) + image->stride_extra;
+ allocbuffer(image);
+
+ result = png_image_finish_read(&image->image, background,
+ image->buffer+16, (png_int_32)image->stride, image->colormap);
+
+ checkbuffer(image, image->file_name);
+
+ if (result)
+ return checkopaque(image);
+
+ else
+ return logerror(image, image->file_name, ": image read failed", "");
+ }
+}
+
+/* Reads from a filename, which must be in image->file_name, but uses
+ * image->opts to choose the method. The file is always read in its native
+ * format (the one the simplified API suggests).
+ */
+static int
+read_one_file(Image *image)
+{
+ if (!(image->opts & READ_FILE) || (image->opts & USE_STDIO))
+ {
+ /* memory or stdio. */
+ FILE *f = fopen(image->file_name, "rb");
+
+ if (f != NULL)
+ {
+ if (image->opts & READ_FILE)
+ image->input_file = f;
+
+ else /* memory */
+ {
+ if (fseek(f, 0, SEEK_END) == 0)
+ {
+ long int cb = ftell(f);
+
+ if (cb > 0)
+ {
+#ifndef __COVERITY__
+ if ((unsigned long int)cb <= (size_t)~(size_t)0)
+#endif
+ {
+ png_bytep b = voidcast(png_bytep, malloc((size_t)cb));
+
+ if (b != NULL)
+ {
+ rewind(f);
+
+ if (fread(b, (size_t)cb, 1, f) == 1)
+ {
+ fclose(f);
+ image->input_memory_size = cb;
+ image->input_memory = b;
+ }
+
+ else
+ {
+ free(b);
+ return logclose(image, f, image->file_name,
+ ": read failed: ");
+ }
+ }
+
+ else
+ return logclose(image, f, image->file_name,
+ ": out of memory: ");
+ }
+
+ else
+ return logclose(image, f, image->file_name,
+ ": file too big for this architecture: ");
+ /* cb is the length of the file as a (long) and
+ * this is greater than the maximum amount of
+ * memory that can be requested from malloc.
+ */
+ }
+
+ else if (cb == 0)
+ return logclose(image, f, image->file_name,
+ ": zero length: ");
+
+ else
+ return logclose(image, f, image->file_name,
+ ": tell failed: ");
+ }
+
+ else
+ return logclose(image, f, image->file_name, ": seek failed: ");
+ }
+ }
+
+ else
+ return logerror(image, image->file_name, ": open failed: ",
+ strerror(errno));
+ }
+
+ return read_file(image, FORMAT_NO_CHANGE, NULL);
+}
+
+#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+static int
+write_one_file(Image *output, Image *image, int convert_to_8bit)
+{
+ if (image->opts & FAST_WRITE)
+ image->image.flags |= PNG_IMAGE_FLAG_FAST;
+
+ if (image->opts & USE_STDIO)
+ {
+#ifndef PNG_USE_MKSTEMP
+ FILE *f = tmpfile();
+#else
+ /* Experimental. Coverity says tmpfile() is insecure because it
+ * generates predictable names.
+ *
+ * It is possible to satisfy Coverity by using mkstemp(); however,
+ * any platform supporting mkstemp() undoubtedly has a secure tmpfile()
+ * implementation as well, and doesn't need the fix. Note that
+ * the fix won't work on platforms that don't support mkstemp().
+ *
+ * https://www.securecoding.cert.org/confluence/display/c/
+ * FIO21-C.+Do+not+create+temporary+files+in+shared+directories
+ * says that most historic implementations of tmpfile() provide
+ * only a limited number of possible temporary file names
+ * (usually 26) before file names are recycled. That article also
+ * provides a secure solution that unfortunately depends upon mkstemp().
+ */
+ char tmpfile[] = "pngstest-XXXXXX";
+ int filedes;
+ FILE *f;
+ umask(0177);
+ filedes = mkstemp(tmpfile);
+ if (filedes < 0)
+ f = NULL;
+ else
+ {
+ f = fdopen(filedes,"w+");
+ /* Hide the filename immediately and ensure that the file does
+ * not exist after the program ends
+ */
+ (void) unlink(tmpfile);
+ }
+#endif
+
+ if (f != NULL)
+ {
+ if (png_image_write_to_stdio(&image->image, f, convert_to_8bit,
+ image->buffer+16, (png_int_32)image->stride, image->colormap))
+ {
+ if (fflush(f) == 0)
+ {
+ rewind(f);
+ initimage(output, image->opts, "tmpfile", image->stride_extra);
+ output->input_file = f;
+ if (!checkopaque(image))
+ return 0;
+ }
+
+ else
+ return logclose(image, f, "tmpfile", ": flush: ");
+ }
+
+ else
+ {
+ fclose(f);
+ return logerror(image, "tmpfile", ": write failed", "");
+ }
+ }
+
+ else
+ return logerror(image, "tmpfile", ": open: ", strerror(errno));
+ }
+
+ else
+ {
+ static int counter = 0;
+ char name[32];
+
+ sprintf(name, "%s%d.png", tmpf, ++counter);
+
+ if (png_image_write_to_file(&image->image, name, convert_to_8bit,
+ image->buffer+16, (png_int_32)image->stride, image->colormap))
+ {
+ initimage(output, image->opts, output->tmpfile_name,
+ image->stride_extra);
+ /* Afterwards, or freeimage will delete it! */
+ strcpy(output->tmpfile_name, name);
+
+ if (!checkopaque(image))
+ return 0;
+ }
+
+ else
+ return logerror(image, name, ": write failed", "");
+ }
+
+ /* 'output' has an initialized temporary image, read this back in and compare
+ * this against the original: there should be no change since the original
+ * format was written unmodified unless 'convert_to_8bit' was specified.
+ * However, if the original image was color-mapped, a simple read will zap
+ * the linear, color and maybe alpha flags, this will cause spurious failures
+ * under some circumstances.
+ */
+ if (read_file(output, image->image.format | FORMAT_NO_CHANGE, NULL))
+ {
+ png_uint_32 original_format = image->image.format;
+
+ if (convert_to_8bit)
+ original_format &= ~PNG_FORMAT_FLAG_LINEAR;
+
+ if ((output->image.format & BASE_FORMATS) !=
+ (original_format & BASE_FORMATS))
+ return logerror(image, image->file_name, ": format changed on read: ",
+ output->file_name);
+
+ return compare_two_images(image, output, 0/*via linear*/, NULL);
+ }
+
+ else
+ return logerror(output, output->tmpfile_name,
+ ": read of new file failed", "");
+}
+#endif
+
+static int
+testimage(Image *image, png_uint_32 opts, format_list *pf)
+{
+ int result;
+ Image copy;
+
+ /* Copy the original data, stealing it from 'image' */
+ checkopaque(image);
+ copy = *image;
+
+ copy.opts = opts;
+ copy.buffer = NULL;
+ copy.bufsize = 0;
+ copy.allocsize = 0;
+
+ image->input_file = NULL;
+ image->input_memory = NULL;
+ image->input_memory_size = 0;
+ image->tmpfile_name[0] = 0;
+
+ {
+ png_uint_32 counter;
+ Image output;
+
+ newimage(&output);
+
+ result = 1;
+
+ /* Use the low bit of 'counter' to indicate whether or not to do alpha
+ * removal with a background color or by composting onto the image; this
+ * step gets skipped if it isn't relevant
+ */
+ for (counter=0; counter<2*FORMAT_COUNT; ++counter)
+ if (format_isset(pf, counter >> 1))
+ {
+ png_uint_32 format = counter >> 1;
+
+ png_color background_color;
+ png_colorp background = NULL;
+
+ /* If there is a format change that removes the alpha channel then
+ * the background is relevant. If the output is 8-bit color-mapped
+ * then a background color *must* be provided, otherwise there are
+ * two tests to do - one with a color, the other with NULL. The
+ * NULL test happens second.
+ */
+ if ((counter & 1) == 0)
+ {
+ if ((format & PNG_FORMAT_FLAG_ALPHA) == 0 &&
+ (image->image.format & PNG_FORMAT_FLAG_ALPHA) != 0)
+ {
+ /* Alpha/transparency will be removed, the background is
+ * relevant: make it a color the first time
+ */
+ random_color(&background_color);
+ background = &background_color;
+
+ /* BUT if the output is to a color-mapped 8-bit format then
+ * the background must always be a color, so increment 'counter'
+ * to skip the NULL test.
+ */
+ if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0 &&
+ (format & PNG_FORMAT_FLAG_LINEAR) == 0)
+ ++counter;
+ }
+
+ /* Otherwise an alpha channel is not being eliminated, just leave
+ * background NULL and skip the (counter & 1) NULL test.
+ */
+ else
+ ++counter;
+ }
+ /* else just use NULL for background */
+
+ resetimage(©);
+ copy.opts = opts; /* in case read_file needs to change it */
+
+ result = read_file(©, format, background);
+ if (!result)
+ break;
+
+ /* Make sure the file just read matches the original file. */
+ result = compare_two_images(image, ©, 0/*via linear*/, background);
+ if (!result)
+ break;
+
+# ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+ /* Write the *copy* just made to a new file to make sure the write
+ * side works ok. Check the conversion to sRGB if the copy is
+ * linear.
+ */
+ output.opts = opts;
+ result = write_one_file(&output, ©, 0/*convert to 8bit*/);
+ if (!result)
+ break;
+
+ /* Validate against the original too; the background is needed here
+ * as well so that compare_two_images knows what color was used.
+ */
+ result = compare_two_images(image, &output, 0, background);
+ if (!result)
+ break;
+
+ if ((format & PNG_FORMAT_FLAG_LINEAR) != 0 &&
+ (format & PNG_FORMAT_FLAG_COLORMAP) == 0)
+ {
+ /* 'output' is linear, convert to the corresponding sRGB format.
+ */
+ output.opts = opts;
+ result = write_one_file(&output, ©, 1/*convert to 8bit*/);
+ if (!result)
+ break;
+
+ /* This may involve a conversion via linear; in the ideal world
+ * this would round-trip correctly, but libpng 1.5.7 is not the
+ * ideal world so allow a drift (error_via_linear).
+ *
+ * 'image' has an alpha channel but 'output' does not then there
+ * will a strip-alpha-channel operation (because 'output' is
+ * linear), handle this by composing on black when doing the
+ * comparison.
+ */
+ result = compare_two_images(image, &output, 1/*via_linear*/,
+ background);
+ if (!result)
+ break;
+ }
+# endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */
+ }
+
+ freeimage(&output);
+ }
+
+ freeimage(©);
+
+ return result;
+}
+
+static int
+test_one_file(const char *file_name, format_list *formats, png_uint_32 opts,
+ int stride_extra, int log_pass)
+{
+ int result;
+ Image image;
+
+ newimage(&image);
+ initimage(&image, opts, file_name, stride_extra);
+ result = read_one_file(&image);
+ if (result)
+ result = testimage(&image, opts, formats);
+ freeimage(&image);
+
+ /* Ensure that stderr is flushed into any log file */
+ fflush(stderr);
+
+ if (log_pass)
+ {
+ if (result)
+ printf("PASS:");
+
+ else
+ printf("FAIL:");
+
+# ifndef PNG_SIMPLIFIED_WRITE_SUPPORTED
+ printf(" (no write)");
+# endif
+
+ print_opts(opts);
+ printf(" %s\n", file_name);
+ /* stdout may not be line-buffered if it is piped to a file, so: */
+ fflush(stdout);
+ }
+
+ else if (!result)
+ exit(1);
+
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ png_uint_32 opts = FAST_WRITE;
+ format_list formats;
+ const char *touch = NULL;
+ int log_pass = 0;
+ int redundant = 0;
+ int stride_extra = 0;
+ int retval = 0;
+ int c;
+
+ init_sRGB_to_d();
+#if 0
+ init_error_via_linear();
+#endif
+ format_init(&formats);
+
+ for (c=1; c<argc; ++c)
+ {
+ const char *arg = argv[c];
+
+ if (strcmp(arg, "--log") == 0)
+ log_pass = 1;
+ else if (strcmp(arg, "--fresh") == 0)
+ {
+ memset(gpc_error, 0, sizeof gpc_error);
+ memset(gpc_error_via_linear, 0, sizeof gpc_error_via_linear);
+ }
+ else if (strcmp(arg, "--file") == 0)
+# ifdef PNG_STDIO_SUPPORTED
+ opts |= READ_FILE;
+# else
+ return 77; /* skipped: no support */
+# endif
+ else if (strcmp(arg, "--memory") == 0)
+ opts &= ~READ_FILE;
+ else if (strcmp(arg, "--stdio") == 0)
+# ifdef PNG_STDIO_SUPPORTED
+ opts |= USE_STDIO;
+# else
+ return 77; /* skipped: no support */
+# endif
+ else if (strcmp(arg, "--name") == 0)
+ opts &= ~USE_STDIO;
+ else if (strcmp(arg, "--verbose") == 0)
+ opts |= VERBOSE;
+ else if (strcmp(arg, "--quiet") == 0)
+ opts &= ~VERBOSE;
+ else if (strcmp(arg, "--preserve") == 0)
+ opts |= KEEP_TMPFILES;
+ else if (strcmp(arg, "--nopreserve") == 0)
+ opts &= ~KEEP_TMPFILES;
+ else if (strcmp(arg, "--keep-going") == 0)
+ opts |= KEEP_GOING;
+ else if (strcmp(arg, "--fast") == 0)
+ opts |= FAST_WRITE;
+ else if (strcmp(arg, "--slow") == 0)
+ opts &= ~FAST_WRITE;
+ else if (strcmp(arg, "--accumulate") == 0)
+ opts |= ACCUMULATE;
+ else if (strcmp(arg, "--redundant") == 0)
+ redundant = 1;
+ else if (strcmp(arg, "--stop") == 0)
+ opts &= ~KEEP_GOING;
+ else if (strcmp(arg, "--strict") == 0)
+ opts |= STRICT;
+ else if (strcmp(arg, "--sRGB-16bit") == 0)
+ opts |= sRGB_16BIT;
+ else if (strcmp(arg, "--linear-16bit") == 0)
+ opts &= ~sRGB_16BIT;
+ else if (strcmp(arg, "--tmpfile") == 0)
+ {
+ if (c+1 < argc)
+ {
+ if (strlen(argv[++c]) >= sizeof tmpf)
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: %s is too long for a temp file prefix\n",
+ argv[0], argv[c]);
+ exit(99);
+ }
+
+ /* Safe: checked above */
+ strncpy(tmpf, argv[c], sizeof (tmpf)-1);
+ }
+
+ else
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: %s requires a temporary file prefix\n",
+ argv[0], arg);
+ exit(99);
+ }
+ }
+ else if (strcmp(arg, "--touch") == 0)
+ {
+ if (c+1 < argc)
+ touch = argv[++c];
+
+ else
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: %s requires a file name argument\n",
+ argv[0], arg);
+ exit(99);
+ }
+ }
+ else if (arg[0] == '+')
+ {
+ png_uint_32 format = formatof(arg+1);
+
+ if (format > FORMAT_COUNT)
+ exit(99);
+
+ format_set(&formats, format);
+ }
+ else if (arg[0] == '-' && arg[1] != 0 && (arg[1] != '0' || arg[2] != 0))
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: unknown option: %s\n", argv[0], arg);
+ exit(99);
+ }
+ else
+ {
+ if (format_is_initial(&formats))
+ format_default(&formats, redundant);
+
+ if (arg[0] == '-')
+ {
+ const int term = (arg[1] == '0' ? 0 : '\n');
+ unsigned int ich = 0;
+
+ /* Loop reading files, use a static buffer to simplify this and just
+ * stop if the name gets to long.
+ */
+ static char buffer[4096];
+
+ do
+ {
+ int ch = getchar();
+
+ /* Don't allow '\0' in file names, and terminate with '\n' or,
+ * for -0, just '\0' (use -print0 to find to make this work!)
+ */
+ if (ch == EOF || ch == term || ch == 0)
+ {
+ buffer[ich] = 0;
+
+ if (ich > 0 && !test_one_file(buffer, &formats, opts,
+ stride_extra, log_pass))
+ retval = 1;
+
+ if (ch == EOF)
+ break;
+
+ ich = 0;
+ --ich; /* so that the increment below sets it to 0 again */
+ }
+
+ else
+ buffer[ich] = (char)ch;
+ } while (++ich < sizeof buffer);
+
+ if (ich)
+ {
+ buffer[32] = 0;
+ buffer[4095] = 0;
+ fprintf(stderr, "%s...%s: file name too long\n", buffer,
+ buffer+(4096-32));
+ exit(99);
+ }
+ }
+
+ else if (!test_one_file(arg, &formats, opts, stride_extra, log_pass))
+ retval = 1;
+ }
+ }
+
+ if (opts & ACCUMULATE)
+ {
+ unsigned int in;
+
+ printf("/* contrib/libtests/pngstest-errors.h\n");
+ printf(" *\n");
+ printf(" * BUILT USING:" PNG_HEADER_VERSION_STRING);
+ printf(" *\n");
+ printf(" * This code is released under the libpng license.\n");
+ printf(" * For conditions of distribution and use, see the disclaimer\n");
+ printf(" * and license in png.h\n");
+ printf(" *\n");
+ printf(" * THIS IS A MACHINE GENERATED FILE: do not edit it directly!\n");
+ printf(" * Instead run:\n");
+ printf(" *\n");
+ printf(" * pngstest --accumulate\n");
+ printf(" *\n");
+ printf(" * on as many PNG files as possible; at least PNGSuite and\n");
+ printf(" * contrib/libtests/testpngs.\n");
+ printf(" */\n");
+
+ printf("static png_uint_16 gpc_error[16/*in*/][16/*out*/][4/*a*/] =\n");
+ printf("{\n");
+ for (in=0; in<16; ++in)
+ {
+ unsigned int out;
+ printf(" { /* input: %s */\n ", format_names[in]);
+ for (out=0; out<16; ++out)
+ {
+ unsigned int alpha;
+ printf(" {");
+ for (alpha=0; alpha<4; ++alpha)
+ {
+ printf(" %d", gpc_error[in][out][alpha]);
+ if (alpha < 3) putchar(',');
+ }
+ printf(" }");
+ if (out < 15)
+ {
+ putchar(',');
+ if (out % 4 == 3) printf("\n ");
+ }
+ }
+ printf("\n }");
+
+ if (in < 15)
+ putchar(',');
+ else
+ putchar('\n');
+ }
+ printf("};\n");
+
+ printf("static png_uint_16 gpc_error_via_linear[16][4/*out*/][4] =\n");
+ printf("{\n");
+ for (in=0; in<16; ++in)
+ {
+ unsigned int out;
+ printf(" { /* input: %s */\n ", format_names[in]);
+ for (out=0; out<4; ++out)
+ {
+ unsigned int alpha;
+ printf(" {");
+ for (alpha=0; alpha<4; ++alpha)
+ {
+ printf(" %d", gpc_error_via_linear[in][out][alpha]);
+ if (alpha < 3) putchar(',');
+ }
+ printf(" }");
+ if (out < 3)
+ putchar(',');
+ }
+ printf("\n }");
+
+ if (in < 15)
+ putchar(',');
+ else
+ putchar('\n');
+ }
+ printf("};\n");
+
+ printf("static png_uint_16 gpc_error_to_colormap[8/*i*/][8/*o*/][4] =\n");
+ printf("{\n");
+ for (in=0; in<8; ++in)
+ {
+ unsigned int out;
+ printf(" { /* input: %s */\n ", format_names[in]);
+ for (out=0; out<8; ++out)
+ {
+ unsigned int alpha;
+ printf(" {");
+ for (alpha=0; alpha<4; ++alpha)
+ {
+ printf(" %d", gpc_error_to_colormap[in][out][alpha]);
+ if (alpha < 3) putchar(',');
+ }
+ printf(" }");
+ if (out < 7)
+ {
+ putchar(',');
+ if (out % 4 == 3) printf("\n ");
+ }
+ }
+ printf("\n }");
+
+ if (in < 7)
+ putchar(',');
+ else
+ putchar('\n');
+ }
+ printf("};\n");
+ printf("/* END MACHINE GENERATED */\n");
+ }
+
+ if (retval == 0 && touch != NULL)
+ {
+ FILE *fsuccess = fopen(touch, "wt");
+
+ if (fsuccess != NULL)
+ {
+ int error = 0;
+ fprintf(fsuccess, "PNG simple API tests succeeded\n");
+ fflush(fsuccess);
+ error = ferror(fsuccess);
+
+ if (fclose(fsuccess) || error)
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: write failed\n", touch);
+ exit(99);
+ }
+ }
+
+ else
+ {
+ fflush(stdout);
+ fprintf(stderr, "%s: open failed\n", touch);
+ exit(99);
+ }
+ }
+
+ return retval;
+}
+
+#else /* !PNG_SIMPLIFIED_READ_SUPPORTED */
+int main(void)
+{
+ fprintf(stderr, "pngstest: no read support in libpng, test skipped\n");
+ /* So the test is skipped: */
+ return 77;
+}
+#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
diff --git a/contrib/libtests/pngunknown.c b/contrib/libtests/pngunknown.c
new file mode 100644
index 0000000..0d52ff8
--- /dev/null
+++ b/contrib/libtests/pngunknown.c
@@ -0,0 +1,1261 @@
+
+/* pngunknown.c - test the read side unknown chunk handling
+ *
+ * Last changed in libpng 1.6.10 [March 6, 2014]
+ * Copyright (c) 2014 Glenn Randers-Pehrson
+ * Written by John Cunningham Bowler
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * NOTES:
+ * This is a C program that is intended to be linked against libpng. It
+ * allows the libpng unknown handling code to be tested by interpreting
+ * arguments to save or discard combinations of chunks. The program is
+ * currently just a minimal validation for the built-in libpng facilities.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <setjmp.h>
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+/* Since this program tests the ability to change the unknown chunk handling
+ * these must be defined:
+ */
+#if defined(PNG_SET_UNKNOWN_CHUNKS_SUPPORTED) &&\
+ defined(PNG_READ_SUPPORTED)
+
+/* One of these must be defined to allow us to find out what happened. It is
+ * still useful to set unknown chunk handling without either of these in order
+ * to cause *known* chunks to be discarded. This can be a significant
+ * efficiency gain, but it can't really be tested here.
+ */
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) ||\
+ defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+
+#if PNG_LIBPNG_VER < 10500
+/* This deliberately lacks the PNG_CONST. */
+typedef png_byte *png_const_bytep;
+
+/* This is copied from 1.5.1 png.h: */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+ -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+ -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
+ (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(xIn, pass) \
+ (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+#define PNG_PASS_MASK(pass,off) ( \
+ ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
+ ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+ ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+ ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
+/* These are needed too for the default build: */
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+
+/* This comes from pnglibconf.h afer 1.5: */
+#define PNG_FP_1 100000
+#define PNG_GAMMA_THRESHOLD_FIXED\
+ ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1))
+#endif
+
+#if PNG_LIBPNG_VER < 10600
+ /* 1.6.0 constifies many APIs. The following exists to allow pngvalid to be
+ * compiled against earlier versions.
+ */
+# define png_const_structp png_structp
+#endif
+
+#if PNG_LIBPNG_VER < 10700
+ /* Copied from libpng 1.7.0 png.h */
+#define PNG_u2(b1, b2) (((unsigned int)(b1) << 8) + (b2))
+
+#define PNG_U16(b1, b2) ((png_uint_16)PNG_u2(b1, b2))
+#define PNG_U32(b1, b2, b3, b4)\
+ (((png_uint_32)PNG_u2(b1, b2) << 16) + PNG_u2(b3, b4))
+
+/* Constants for known chunk types.
+ */
+#define png_IDAT PNG_U32( 73, 68, 65, 84)
+#define png_IEND PNG_U32( 73, 69, 78, 68)
+#define png_IHDR PNG_U32( 73, 72, 68, 82)
+#define png_PLTE PNG_U32( 80, 76, 84, 69)
+#define png_bKGD PNG_U32( 98, 75, 71, 68)
+#define png_cHRM PNG_U32( 99, 72, 82, 77)
+#define png_fRAc PNG_U32(102, 82, 65, 99) /* registered, not defined */
+#define png_gAMA PNG_U32(103, 65, 77, 65)
+#define png_gIFg PNG_U32(103, 73, 70, 103)
+#define png_gIFt PNG_U32(103, 73, 70, 116) /* deprecated */
+#define png_gIFx PNG_U32(103, 73, 70, 120)
+#define png_hIST PNG_U32(104, 73, 83, 84)
+#define png_iCCP PNG_U32(105, 67, 67, 80)
+#define png_iTXt PNG_U32(105, 84, 88, 116)
+#define png_oFFs PNG_U32(111, 70, 70, 115)
+#define png_pCAL PNG_U32(112, 67, 65, 76)
+#define png_pHYs PNG_U32(112, 72, 89, 115)
+#define png_sBIT PNG_U32(115, 66, 73, 84)
+#define png_sCAL PNG_U32(115, 67, 65, 76)
+#define png_sPLT PNG_U32(115, 80, 76, 84)
+#define png_sRGB PNG_U32(115, 82, 71, 66)
+#define png_sTER PNG_U32(115, 84, 69, 82)
+#define png_tEXt PNG_U32(116, 69, 88, 116)
+#define png_tIME PNG_U32(116, 73, 77, 69)
+#define png_tRNS PNG_U32(116, 82, 78, 83)
+#define png_zTXt PNG_U32(122, 84, 88, 116)
+
+/* Test on flag values as defined in the spec (section 5.4): */
+#define PNG_CHUNK_ANCILLARY(c) (1 & ((c) >> 29))
+#define PNG_CHUNK_CRITICAL(c) (!PNG_CHUNK_ANCILLARY(c))
+#define PNG_CHUNK_PRIVATE(c) (1 & ((c) >> 21))
+#define PNG_CHUNK_RESERVED(c) (1 & ((c) >> 13))
+#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >> 5))
+
+#endif /* PNG_LIBPNG_VER < 10700 */
+
+#ifdef __cplusplus
+# define this not_the_cpp_this
+# define new not_the_cpp_new
+# define voidcast(type, value) static_cast<type>(value)
+#else
+# define voidcast(type, value) (value)
+#endif /* __cplusplus */
+
+/* Unused formal parameter errors are removed using the following macro which is
+ * expected to have no bad effects on performance.
+ */
+#ifndef UNUSED
+# if defined(__GNUC__) || defined(_MSC_VER)
+# define UNUSED(param) (void)param;
+# else
+# define UNUSED(param)
+# endif
+#endif
+
+/* Types of chunks not known to libpng */
+#define png_vpAg PNG_U32(118, 112, 65, 103)
+
+/* Chunk information */
+#define PNG_INFO_tEXt 0x10000000U
+#define PNG_INFO_iTXt 0x20000000U
+#define PNG_INFO_zTXt 0x40000000U
+
+#define PNG_INFO_sTER 0x01000000U
+#define PNG_INFO_vpAg 0x02000000U
+
+#define ABSENT 0
+#define START 1
+#define END 2
+
+static struct
+{
+ char name[5];
+ png_uint_32 flag;
+ png_uint_32 tag;
+ int unknown; /* Chunk not known to libpng */
+ int all; /* Chunk set by the '-1' option */
+ int position; /* position in pngtest.png */
+ int keep; /* unknown handling setting */
+} chunk_info[] = {
+ /* Critical chunks */
+ { "IDAT", PNG_INFO_IDAT, png_IDAT, 0, 0, START, 0 }, /* must be [0] */
+ { "PLTE", PNG_INFO_PLTE, png_PLTE, 0, 0, ABSENT, 0 },
+
+ /* Non-critical chunks that libpng handles */
+ /* This is a mess but it seems to be the only way to do it - there is no way
+ * to check for a definition outside a #if.
+ */
+ { "bKGD", PNG_INFO_bKGD, png_bKGD,
+# ifdef PNG_READ_bKGD_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "cHRM", PNG_INFO_cHRM, png_cHRM,
+# ifdef PNG_READ_cHRM_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "gAMA", PNG_INFO_gAMA, png_gAMA,
+# ifdef PNG_READ_gAMA_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "hIST", PNG_INFO_hIST, png_hIST,
+# ifdef PNG_READ_hIST_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, ABSENT, 0 },
+ { "iCCP", PNG_INFO_iCCP, png_iCCP,
+# ifdef PNG_READ_iCCP_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, ABSENT, 0 },
+ { "iTXt", PNG_INFO_iTXt, png_iTXt,
+# ifdef PNG_READ_iTXt_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, ABSENT, 0 },
+ { "oFFs", PNG_INFO_oFFs, png_oFFs,
+# ifdef PNG_READ_oFFs_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "pCAL", PNG_INFO_pCAL, png_pCAL,
+# ifdef PNG_READ_pCAL_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "pHYs", PNG_INFO_pHYs, png_pHYs,
+# ifdef PNG_READ_pHYs_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "sBIT", PNG_INFO_sBIT, png_sBIT,
+# ifdef PNG_READ_sBIT_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "sCAL", PNG_INFO_sCAL, png_sCAL,
+# ifdef PNG_READ_sCAL_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "sPLT", PNG_INFO_sPLT, png_sPLT,
+# ifdef PNG_READ_sPLT_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, ABSENT, 0 },
+ { "sRGB", PNG_INFO_sRGB, png_sRGB,
+# ifdef PNG_READ_sRGB_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "tEXt", PNG_INFO_tEXt, png_tEXt,
+# ifdef PNG_READ_tEXt_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "tIME", PNG_INFO_tIME, png_tIME,
+# ifdef PNG_READ_tIME_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, START, 0 },
+ { "tRNS", PNG_INFO_tRNS, png_tRNS,
+# ifdef PNG_READ_tRNS_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 0, ABSENT, 0 },
+ { "zTXt", PNG_INFO_zTXt, png_zTXt,
+# ifdef PNG_READ_zTXt_SUPPORTED
+ 0,
+# else
+ 1,
+# endif
+ 1, END, 0 },
+
+ /* No libpng handling */
+ { "sTER", PNG_INFO_sTER, png_sTER, 1, 1, START, 0 },
+ { "vpAg", PNG_INFO_vpAg, png_vpAg, 1, 0, START, 0 },
+};
+
+#define NINFO ((int)((sizeof chunk_info)/(sizeof chunk_info[0])))
+
+static void
+clear_keep(void)
+{
+ int i = NINFO;
+ while (--i >= 0)
+ chunk_info[i].keep = 0;
+}
+
+static int
+find(const char *name)
+{
+ int i = NINFO;
+ while (--i >= 0)
+ {
+ if (memcmp(chunk_info[i].name, name, 4) == 0)
+ break;
+ }
+
+ return i;
+}
+
+static int
+findb(const png_byte *name)
+{
+ int i = NINFO;
+ while (--i >= 0)
+ {
+ if (memcmp(chunk_info[i].name, name, 4) == 0)
+ break;
+ }
+
+ return i;
+}
+
+static int
+find_by_flag(png_uint_32 flag)
+{
+ int i = NINFO;
+
+ while (--i >= 0) if (chunk_info[i].flag == flag) return i;
+
+ fprintf(stderr, "pngunknown: internal error\n");
+ exit(4);
+}
+
+static int
+ancillary(const char *name)
+{
+ return PNG_CHUNK_ANCILLARY(PNG_U32(name[0], name[1], name[2], name[3]));
+}
+
+#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+static int
+ancillaryb(const png_byte *name)
+{
+ return PNG_CHUNK_ANCILLARY(PNG_U32(name[0], name[1], name[2], name[3]));
+}
+#endif
+
+/* Type of an error_ptr */
+typedef struct
+{
+ jmp_buf error_return;
+ png_structp png_ptr;
+ png_infop info_ptr, end_ptr;
+ png_uint_32 before_IDAT;
+ png_uint_32 after_IDAT;
+ int error_count;
+ int warning_count;
+ int keep; /* the default value */
+ const char *program;
+ const char *file;
+ const char *test;
+} display;
+
+static const char init[] = "initialization";
+static const char cmd[] = "command line";
+
+static void
+init_display(display *d, const char *program)
+{
+ memset(d, 0, sizeof *d);
+ d->png_ptr = NULL;
+ d->info_ptr = d->end_ptr = NULL;
+ d->error_count = d->warning_count = 0;
+ d->program = program;
+ d->file = program;
+ d->test = init;
+}
+
+static void
+clean_display(display *d)
+{
+ png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_ptr);
+
+ /* This must not happen - it might cause an app crash */
+ if (d->png_ptr != NULL || d->info_ptr != NULL || d->end_ptr != NULL)
+ {
+ fprintf(stderr, "%s(%s): png_destroy_read_struct error\n", d->file,
+ d->test);
+ exit(1);
+ }
+}
+
+PNG_FUNCTION(void, display_exit, (display *d), static PNG_NORETURN)
+{
+ ++(d->error_count);
+
+ if (d->png_ptr != NULL)
+ clean_display(d);
+
+ /* During initialization and if this is a single command line argument set
+ * exit now - there is only one test, otherwise longjmp to do the next test.
+ */
+ if (d->test == init || d->test == cmd)
+ exit(1);
+
+ longjmp(d->error_return, 1);
+}
+
+static int
+display_rc(const display *d, int strict)
+{
+ return d->error_count + (strict ? d->warning_count : 0);
+}
+
+/* libpng error and warning callbacks */
+PNG_FUNCTION(void, (PNGCBAPI error), (png_structp png_ptr, const char *message),
+ static PNG_NORETURN)
+{
+ display *d = (display*)png_get_error_ptr(png_ptr);
+
+ fprintf(stderr, "%s(%s): libpng error: %s\n", d->file, d->test, message);
+ display_exit(d);
+}
+
+static void PNGCBAPI
+warning(png_structp png_ptr, const char *message)
+{
+ display *d = (display*)png_get_error_ptr(png_ptr);
+
+ fprintf(stderr, "%s(%s): libpng warning: %s\n", d->file, d->test, message);
+ ++(d->warning_count);
+}
+
+static png_uint_32
+get_valid(display *d, png_infop info_ptr)
+{
+ png_uint_32 flags = png_get_valid(d->png_ptr, info_ptr, (png_uint_32)~0);
+
+ /* Map the text chunks back into the flags */
+ {
+ png_textp text;
+ png_uint_32 ntext = png_get_text(d->png_ptr, info_ptr, &text, NULL);
+
+ while (ntext-- > 0) switch (text[ntext].compression)
+ {
+ case -1:
+ flags |= PNG_INFO_tEXt;
+ break;
+ case 0:
+ flags |= PNG_INFO_zTXt;
+ break;
+ case 1:
+ case 2:
+ flags |= PNG_INFO_iTXt;
+ break;
+ default:
+ fprintf(stderr, "%s(%s): unknown text compression %d\n", d->file,
+ d->test, text[ntext].compression);
+ display_exit(d);
+ }
+ }
+
+ return flags;
+}
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+static int PNGCBAPI
+read_callback(png_structp pp, png_unknown_chunkp pc)
+{
+ /* This function mimics the behavior of png_set_keep_unknown_chunks by
+ * returning '0' to keep the chunk and '1' to discard it.
+ */
+ display *d = voidcast(display*, png_get_user_chunk_ptr(pp));
+ int chunk = findb(pc->name);
+ int keep, discard;
+
+ if (chunk < 0) /* not one in our list, so not a known chunk */
+ keep = d->keep;
+
+ else
+ {
+ keep = chunk_info[chunk].keep;
+ if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+ {
+ /* See the comments in png.h - use the default for unknown chunks,
+ * do not keep known chunks.
+ */
+ if (chunk_info[chunk].unknown)
+ keep = d->keep;
+
+ else
+ keep = PNG_HANDLE_CHUNK_NEVER;
+ }
+ }
+
+ switch (keep)
+ {
+ default:
+ fprintf(stderr, "%s(%s): %d: unrecognized chunk option\n", d->file,
+ d->test, chunk_info[chunk].keep);
+ display_exit(d);
+
+ case PNG_HANDLE_CHUNK_AS_DEFAULT:
+ case PNG_HANDLE_CHUNK_NEVER:
+ discard = 1/*handled; discard*/;
+ break;
+
+ case PNG_HANDLE_CHUNK_IF_SAFE:
+ case PNG_HANDLE_CHUNK_ALWAYS:
+ discard = 0/*not handled; keep*/;
+ break;
+ }
+
+ /* Also store information about this chunk in the display, the relevant flag
+ * is set if the chunk is to be kept ('not handled'.)
+ */
+ if (chunk >= 0) if (!discard) /* stupidity to stop a GCC warning */
+ {
+ png_uint_32 flag = chunk_info[chunk].flag;
+
+ if (pc->location & PNG_AFTER_IDAT)
+ d->after_IDAT |= flag;
+
+ else
+ d->before_IDAT |= flag;
+ }
+
+ /* However if there is no support to store unknown chunks don't ask libpng to
+ * do it; there will be an png_error.
+ */
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ return discard;
+# else
+ return 1; /*handled; discard*/
+# endif
+}
+#endif /* READ_USER_CHUNKS_SUPPORTED */
+
+#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+static png_uint_32
+get_unknown(display *d, png_infop info_ptr, int after_IDAT)
+{
+ /* Create corresponding 'unknown' flags */
+ png_uint_32 flags = 0;
+
+ UNUSED(after_IDAT)
+
+ {
+ png_unknown_chunkp unknown;
+ int num_unknown = png_get_unknown_chunks(d->png_ptr, info_ptr, &unknown);
+
+ while (--num_unknown >= 0)
+ {
+ int chunk = findb(unknown[num_unknown].name);
+
+ /* Chunks not known to pngunknown must be validated here; since they
+ * must also be unknown to libpng the 'display->keep' behavior should
+ * have been used.
+ */
+ if (chunk < 0) switch (d->keep)
+ {
+ default: /* impossible */
+ case PNG_HANDLE_CHUNK_AS_DEFAULT:
+ case PNG_HANDLE_CHUNK_NEVER:
+ fprintf(stderr, "%s(%s): %s: %s: unknown chunk saved\n",
+ d->file, d->test, d->keep ? "discard" : "default",
+ unknown[num_unknown].name);
+ ++(d->error_count);
+ break;
+
+ case PNG_HANDLE_CHUNK_IF_SAFE:
+ if (!ancillaryb(unknown[num_unknown].name))
+ {
+ fprintf(stderr,
+ "%s(%s): if-safe: %s: unknown critical chunk saved\n",
+ d->file, d->test, unknown[num_unknown].name);
+ ++(d->error_count);
+ break;
+ }
+ /* FALL THROUGH (safe) */
+ case PNG_HANDLE_CHUNK_ALWAYS:
+ break;
+ }
+
+ else
+ flags |= chunk_info[chunk].flag;
+ }
+ }
+
+ return flags;
+}
+#else
+static png_uint_32
+get_unknown(display *d, png_infop info_ptr, int after_IDAT)
+ /* Otherwise this will return the cached values set by any user callback */
+{
+ UNUSED(info_ptr);
+
+ if (after_IDAT)
+ return d->after_IDAT;
+
+ else
+ return d->before_IDAT;
+}
+
+# ifndef PNG_READ_USER_CHUNKS_SUPPORTED
+ /* The #defines above should mean this is never reached, it's just here as
+ * a check to ensure the logic is correct.
+ */
+# error No store support and no user chunk support, this will not work
+# endif
+#endif
+
+static int
+check(FILE *fp, int argc, const char **argv, png_uint_32p flags/*out*/,
+ display *d, int set_callback)
+{
+ int i, npasses, ipass;
+ png_uint_32 height;
+
+ d->keep = PNG_HANDLE_CHUNK_AS_DEFAULT;
+ d->before_IDAT = 0;
+ d->after_IDAT = 0;
+
+ /* Some of these errors are permanently fatal and cause an exit here, others
+ * are per-test and cause an error return.
+ */
+ d->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, d, error,
+ warning);
+ if (d->png_ptr == NULL)
+ {
+ fprintf(stderr, "%s(%s): could not allocate png struct\n", d->file,
+ d->test);
+ /* Terminate here, this error is not test specific. */
+ exit(1);
+ }
+
+ d->info_ptr = png_create_info_struct(d->png_ptr);
+ d->end_ptr = png_create_info_struct(d->png_ptr);
+ if (d->info_ptr == NULL || d->end_ptr == NULL)
+ {
+ fprintf(stderr, "%s(%s): could not allocate png info\n", d->file,
+ d->test);
+ clean_display(d);
+ exit(1);
+ }
+
+ png_init_io(d->png_ptr, fp);
+
+# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ /* This is only done if requested by the caller; it interferes with the
+ * standard store/save mechanism.
+ */
+ if (set_callback)
+ png_set_read_user_chunk_fn(d->png_ptr, d, read_callback);
+# else
+ UNUSED(set_callback)
+# endif
+
+ /* Handle each argument in turn; multiple settings are possible for the same
+ * chunk and multiple calls will occur (the last one should override all
+ * preceding ones).
+ */
+ for (i=0; i<argc; ++i)
+ {
+ const char *equals = strchr(argv[i], '=');
+
+ if (equals != NULL)
+ {
+ int chunk, option;
+
+ if (strcmp(equals+1, "default") == 0)
+ option = PNG_HANDLE_CHUNK_AS_DEFAULT;
+ else if (strcmp(equals+1, "discard") == 0)
+ option = PNG_HANDLE_CHUNK_NEVER;
+ else if (strcmp(equals+1, "if-safe") == 0)
+ option = PNG_HANDLE_CHUNK_IF_SAFE;
+ else if (strcmp(equals+1, "save") == 0)
+ option = PNG_HANDLE_CHUNK_ALWAYS;
+ else
+ {
+ fprintf(stderr, "%s(%s): %s: unrecognized chunk option\n", d->file,
+ d->test, argv[i]);
+ display_exit(d);
+ }
+
+ switch (equals - argv[i])
+ {
+ case 4: /* chunk name */
+ chunk = find(argv[i]);
+
+ if (chunk >= 0)
+ {
+ /* These #if tests have the effect of skipping the arguments
+ * if SAVE support is unavailable - we can't do a useful test
+ * in this case, so we just check the arguments! This could
+ * be improved in the future by using the read callback.
+ */
+# if PNG_LIBPNG_VER >= 10700 &&\
+ !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+ if (option < PNG_HANDLE_CHUNK_IF_SAFE)
+# endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
+ {
+ png_byte name[5];
+
+ memcpy(name, chunk_info[chunk].name, 5);
+ png_set_keep_unknown_chunks(d->png_ptr, option, name, 1);
+ chunk_info[chunk].keep = option;
+ }
+ continue;
+ }
+
+ break;
+
+ case 7: /* default */
+ if (memcmp(argv[i], "default", 7) == 0)
+ {
+# if PNG_LIBPNG_VER >= 10700 &&\
+ !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+ if (option < PNG_HANDLE_CHUNK_IF_SAFE)
+# endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
+ png_set_keep_unknown_chunks(d->png_ptr, option, NULL, 0);
+
+ d->keep = option;
+ continue;
+ }
+
+ break;
+
+ case 3: /* all */
+ if (memcmp(argv[i], "all", 3) == 0)
+ {
+# if PNG_LIBPNG_VER >= 10700 &&\
+ !defined(PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED)
+ if (option < PNG_HANDLE_CHUNK_IF_SAFE)
+# endif /* 1.7+ SAVE_UNKNOWN_CHUNKS */
+ png_set_keep_unknown_chunks(d->png_ptr, option, NULL, -1);
+
+ d->keep = option;
+
+ for (chunk = 0; chunk < NINFO; ++chunk)
+ if (chunk_info[chunk].all)
+ chunk_info[chunk].keep = option;
+ continue;
+ }
+
+ break;
+
+ default: /* some misplaced = */
+
+ break;
+ }
+ }
+
+ fprintf(stderr, "%s(%s): %s: unrecognized chunk argument\n", d->file,
+ d->test, argv[i]);
+ display_exit(d);
+ }
+
+ png_read_info(d->png_ptr, d->info_ptr);
+
+ switch (png_get_interlace_type(d->png_ptr, d->info_ptr))
+ {
+ case PNG_INTERLACE_NONE:
+ npasses = 1;
+ break;
+
+ case PNG_INTERLACE_ADAM7:
+ npasses = PNG_INTERLACE_ADAM7_PASSES;
+ break;
+
+ default:
+ /* Hard error because it is not test specific */
+ fprintf(stderr, "%s(%s): invalid interlace type\n", d->file, d->test);
+ clean_display(d);
+ exit(1);
+ }
+
+ /* Skip the image data, if IDAT is not being handled then don't do this
+ * because it will cause a CRC error.
+ */
+ if (chunk_info[0/*IDAT*/].keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+ {
+ png_start_read_image(d->png_ptr);
+ height = png_get_image_height(d->png_ptr, d->info_ptr);
+
+ if (npasses > 1)
+ {
+ png_uint_32 width = png_get_image_width(d->png_ptr, d->info_ptr);
+
+ for (ipass=0; ipass<npasses; ++ipass)
+ {
+ png_uint_32 wPass = PNG_PASS_COLS(width, ipass);
+
+ if (wPass > 0)
+ {
+ png_uint_32 y;
+
+ for (y=0; y<height; ++y) if (PNG_ROW_IN_INTERLACE_PASS(y, ipass))
+ png_read_row(d->png_ptr, NULL, NULL);
+ }
+ }
+ } /* interlaced */
+
+ else /* not interlaced */
+ {
+ png_uint_32 y;
+
+ for (y=0; y<height; ++y)
+ png_read_row(d->png_ptr, NULL, NULL);
+ }
+ }
+
+ png_read_end(d->png_ptr, d->end_ptr);
+
+ flags[0] = get_valid(d, d->info_ptr);
+ flags[1] = get_unknown(d, d->info_ptr, 0/*before IDAT*/);
+
+ /* Only png_read_png sets PNG_INFO_IDAT! */
+ flags[chunk_info[0/*IDAT*/].keep != PNG_HANDLE_CHUNK_AS_DEFAULT] |=
+ PNG_INFO_IDAT;
+
+ flags[2] = get_valid(d, d->end_ptr);
+ flags[3] = get_unknown(d, d->end_ptr, 1/*after IDAT*/);
+
+ clean_display(d);
+
+ return d->keep;
+}
+
+static void
+check_error(display *d, png_uint_32 flags, const char *message)
+{
+ while (flags)
+ {
+ png_uint_32 flag = flags & -(png_int_32)flags;
+ int i = find_by_flag(flag);
+
+ fprintf(stderr, "%s(%s): chunk %s: %s\n", d->file, d->test,
+ chunk_info[i].name, message);
+ ++(d->error_count);
+
+ flags &= ~flag;
+ }
+}
+
+static void
+check_handling(display *d, int def, png_uint_32 chunks, png_uint_32 known,
+ png_uint_32 unknown, const char *position, int set_callback)
+{
+ while (chunks)
+ {
+ png_uint_32 flag = chunks & -(png_int_32)chunks;
+ int i = find_by_flag(flag);
+ int keep = chunk_info[i].keep;
+ const char *type;
+ const char *errorx = NULL;
+
+ if (chunk_info[i].unknown)
+ {
+ if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT)
+ {
+ type = "UNKNOWN (default)";
+ keep = def;
+ }
+
+ else
+ type = "UNKNOWN (specified)";
+
+ if (flag & known)
+ errorx = "chunk processed";
+
+ else switch (keep)
+ {
+ case PNG_HANDLE_CHUNK_AS_DEFAULT:
+ if (flag & unknown)
+ errorx = "DEFAULT: unknown chunk saved";
+ break;
+
+ case PNG_HANDLE_CHUNK_NEVER:
+ if (flag & unknown)
+ errorx = "DISCARD: unknown chunk saved";
+ break;
+
+ case PNG_HANDLE_CHUNK_IF_SAFE:
+ if (ancillary(chunk_info[i].name))
+ {
+ if (!(flag & unknown))
+ errorx = "IF-SAFE: unknown ancillary chunk lost";
+ }
+
+ else if (flag & unknown)
+ errorx = "IF-SAFE: unknown critical chunk saved";
+ break;
+
+ case PNG_HANDLE_CHUNK_ALWAYS:
+ if (!(flag & unknown))
+ errorx = "SAVE: unknown chunk lost";
+ break;
+
+ default:
+ errorx = "internal error: bad keep";
+ break;
+ }
+ } /* unknown chunk */
+
+ else /* known chunk */
+ {
+ type = "KNOWN";
+
+ if (flag & known)
+ {
+ /* chunk was processed, it won't have been saved because that is
+ * caught below when checking for inconsistent processing.
+ */
+ if (keep != PNG_HANDLE_CHUNK_AS_DEFAULT)
+ errorx = "!DEFAULT: known chunk processed";
+ }
+
+ else /* not processed */ switch (keep)
+ {
+ case PNG_HANDLE_CHUNK_AS_DEFAULT:
+ errorx = "DEFAULT: known chunk not processed";
+ break;
+
+ case PNG_HANDLE_CHUNK_NEVER:
+ if (flag & unknown)
+ errorx = "DISCARD: known chunk saved";
+ break;
+
+ case PNG_HANDLE_CHUNK_IF_SAFE:
+ if (ancillary(chunk_info[i].name))
+ {
+ if (!(flag & unknown))
+ errorx = "IF-SAFE: known ancillary chunk lost";
+ }
+
+ else if (flag & unknown)
+ errorx = "IF-SAFE: known critical chunk saved";
+ break;
+
+ case PNG_HANDLE_CHUNK_ALWAYS:
+ if (!(flag & unknown))
+ errorx = "SAVE: known chunk lost";
+ break;
+
+ default:
+ errorx = "internal error: bad keep (2)";
+ break;
+ }
+ }
+
+ if (errorx != NULL)
+ {
+ ++(d->error_count);
+ fprintf(stderr, "%s(%s%s): %s %s %s: %s\n", d->file, d->test,
+ set_callback ? ",callback" : "",
+ type, chunk_info[i].name, position, errorx);
+ }
+
+ chunks &= ~flag;
+ }
+}
+
+static void
+perform_one_test(FILE *fp, int argc, const char **argv,
+ png_uint_32 *default_flags, display *d, int set_callback)
+{
+ int def;
+ png_uint_32 flags[2][4];
+
+ rewind(fp);
+ clear_keep();
+ memcpy(flags[0], default_flags, sizeof flags[0]);
+
+ def = check(fp, argc, argv, flags[1], d, set_callback);
+
+ /* Chunks should either be known or unknown, never both and this should apply
+ * whether the chunk is before or after the IDAT (actually, the app can
+ * probably change this by swapping the handling after the image, but this
+ * test does not do that.)
+ */
+ check_error(d, (flags[0][0]|flags[0][2]) & (flags[0][1]|flags[0][3]),
+ "chunk handled inconsistently in count tests");
+ check_error(d, (flags[1][0]|flags[1][2]) & (flags[1][1]|flags[1][3]),
+ "chunk handled inconsistently in option tests");
+
+ /* Now find out what happened to each chunk before and after the IDAT and
+ * determine if the behavior was correct. First some basic sanity checks,
+ * any known chunk should be known in the original count, any unknown chunk
+ * should be either known or unknown in the original.
+ */
+ {
+ png_uint_32 test;
+
+ test = flags[1][0] & ~flags[0][0];
+ check_error(d, test, "new known chunk before IDAT");
+ test = flags[1][1] & ~(flags[0][0] | flags[0][1]);
+ check_error(d, test, "new unknown chunk before IDAT");
+ test = flags[1][2] & ~flags[0][2];
+ check_error(d, test, "new known chunk after IDAT");
+ test = flags[1][3] & ~(flags[0][2] | flags[0][3]);
+ check_error(d, test, "new unknown chunk after IDAT");
+ }
+
+ /* Now each chunk in the original list should have been handled according to
+ * the options set for that chunk, regardless of whether libpng knows about
+ * it or not.
+ */
+ check_handling(d, def, flags[0][0] | flags[0][1], flags[1][0], flags[1][1],
+ "before IDAT", set_callback);
+ check_handling(d, def, flags[0][2] | flags[0][3], flags[1][2], flags[1][3],
+ "after IDAT", set_callback);
+}
+
+static void
+perform_one_test_safe(FILE *fp, int argc, const char **argv,
+ png_uint_32 *default_flags, display *d, const char *test)
+{
+ if (setjmp(d->error_return) == 0)
+ {
+ d->test = test; /* allow use of d->error_return */
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ perform_one_test(fp, argc, argv, default_flags, d, 0);
+# endif
+# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ perform_one_test(fp, argc, argv, default_flags, d, 1);
+# endif
+ d->test = init; /* prevent use of d->error_return */
+ }
+}
+
+static const char *standard_tests[] =
+{
+ "discard", "default=discard", 0,
+ "save", "default=save", 0,
+ "if-safe", "default=if-safe", 0,
+ "vpAg", "vpAg=if-safe", 0,
+ "sTER", "sTER=if-safe", 0,
+ "IDAT", "default=discard", "IDAT=save", 0,
+ "sAPI", "bKGD=save", "cHRM=save", "gAMA=save", "all=discard", "iCCP=save",
+ "sBIT=save", "sRGB=save", 0,
+ 0/*end*/
+};
+
+static PNG_NORETURN void
+usage(const char *program, const char *reason)
+{
+ fprintf(stderr, "pngunknown: %s: usage:\n %s [--strict] "
+ "--default|{(CHNK|default|all)=(default|discard|if-safe|save)} "
+ "testfile.png\n", reason, program);
+ exit(99);
+}
+
+int
+main(int argc, const char **argv)
+{
+ FILE *fp;
+ png_uint_32 default_flags[4/*valid,unknown{before,after}*/];
+ int strict = 0, default_tests = 0;
+ const char *count_argv = "default=save";
+ const char *touch_file = NULL;
+ display d;
+
+ init_display(&d, argv[0]);
+
+ while (++argv, --argc > 0)
+ {
+ if (strcmp(*argv, "--strict") == 0)
+ strict = 1;
+
+ else if (strcmp(*argv, "--default") == 0)
+ default_tests = 1;
+
+ else if (strcmp(*argv, "--touch") == 0)
+ {
+ if (argc > 1)
+ touch_file = *++argv, --argc;
+
+ else
+ usage(d.program, "--touch: missing file name");
+ }
+
+ else
+ break;
+ }
+
+ /* A file name is required, but there should be no other arguments if
+ * --default was specified.
+ */
+ if (argc <= 0)
+ usage(d.program, "missing test file");
+
+ /* GCC BUG: if (default_tests && argc != 1) triggers some weird GCC argc
+ * optimization which causes warnings with -Wstrict-overflow!
+ */
+ else if (default_tests) if (argc != 1)
+ usage(d.program, "extra arguments");
+
+ /* The name of the test file is the last argument; remove it. */
+ d.file = argv[--argc];
+
+ fp = fopen(d.file, "rb");
+ if (fp == NULL)
+ {
+ perror(d.file);
+ exit(99);
+ }
+
+ /* First find all the chunks, known and unknown, in the test file, a failure
+ * here aborts the whole test.
+ *
+ * If 'save' is supported then the normal saving method should happen,
+ * otherwise if 'read' is supported then the read callback will do the
+ * same thing. If both are supported the 'read' callback won't be
+ * instantiated by default. If 'save' is *not* supported then a user
+ * callback is required even though we can call png_get_unknown_chunks.
+ */
+ if (check(fp, 1, &count_argv, default_flags, &d,
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ 0
+# else
+ 1
+# endif
+ ) != PNG_HANDLE_CHUNK_ALWAYS)
+ {
+ fprintf(stderr, "%s: %s: internal error\n", d.program, d.file);
+ exit(99);
+ }
+
+ /* Now find what the various supplied options cause to change: */
+ if (!default_tests)
+ {
+ d.test = cmd; /* acts as a flag to say exit, do not longjmp */
+# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+ perform_one_test(fp, argc, argv, default_flags, &d, 0);
+# endif
+# ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ perform_one_test(fp, argc, argv, default_flags, &d, 1);
+# endif
+ d.test = init;
+ }
+
+ else
+ {
+ const char **test = standard_tests;
+
+ /* Set the exit_test pointer here so we can continue after a libpng error.
+ * NOTE: this leaks memory because the png_struct data from the failing
+ * test is never freed.
+ */
+ while (*test)
+ {
+ const char *this_test = *test++;
+ const char **next = test;
+ int count = display_rc(&d, strict), new_count;
+ const char *result;
+ int arg_count = 0;
+
+ while (*next) ++next, ++arg_count;
+
+ perform_one_test_safe(fp, arg_count, test, default_flags, &d,
+ this_test);
+
+ new_count = display_rc(&d, strict);
+
+ if (new_count == count)
+ result = "PASS";
+
+ else
+ result = "FAIL";
+
+ printf("%s: %s %s\n", result, d.program, this_test);
+
+ test = next+1;
+ }
+ }
+
+ fclose(fp);
+
+ if (display_rc(&d, strict) == 0)
+ {
+ /* Success, touch the success file if appropriate */
+ if (touch_file != NULL)
+ {
+ FILE *fsuccess = fopen(touch_file, "wt");
+
+ if (fsuccess != NULL)
+ {
+ int err = 0;
+ fprintf(fsuccess, "PNG unknown tests succeeded\n");
+ fflush(fsuccess);
+ err = ferror(fsuccess);
+
+ if (fclose(fsuccess) || err)
+ {
+ fprintf(stderr, "%s: write failed\n", touch_file);
+ exit(99);
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "%s: open failed\n", touch_file);
+ exit(99);
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+#else /* !(READ_USER_CHUNKS || SAVE_UNKNOWN_CHUNKS) */
+int
+main(void)
+{
+ fprintf(stderr,
+ " test ignored: no support to find out about unknown chunks\n");
+ /* So the test is skipped: */
+ return 77;
+}
+#endif /* READ_USER_CHUNKS || SAVE_UNKNOWN_CHUNKS */
+
+#else /* !(SET_UNKNOWN_CHUNKS && READ) */
+int
+main(void)
+{
+ fprintf(stderr,
+ " test ignored: no support to modify unknown chunk handling\n");
+ /* So the test is skipped: */
+ return 77;
+}
+#endif /* SET_UNKNOWN_CHUNKS && READ*/
diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c
new file mode 100644
index 0000000..bdd3c91
--- /dev/null
+++ b/contrib/libtests/pngvalid.c
@@ -0,0 +1,11746 @@
+
+/* pngvalid.c - validate libpng by constructing then reading png files.
+ *
+ * Last changed in libpng 1.5.25 [December 3, 2015]
+ * Copyright (c) 2014-2015 Glenn Randers-Pehrson
+ * Written by John Cunningham Bowler
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * NOTES:
+ * This is a C program that is intended to be linked against libpng. It
+ * generates bitmaps internally, stores them as PNG files (using the
+ * sequential write code) then reads them back (using the sequential
+ * read code) and validates that the result has the correct data.
+ *
+ * The program can be modified and extended to test the correctness of
+ * transformations performed by libpng.
+ */
+
+#define _POSIX_SOURCE 1
+#define _ISOC99_SOURCE 1 /* For floating point */
+#define _GNU_SOURCE 1 /* For the floating point exception extension */
+
+#include <signal.h>
+#include <stdio.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+#ifdef HAVE_FEENABLEEXCEPT /* from config.h, if included */
+# include <fenv.h>
+#endif
+
+#ifndef FE_DIVBYZERO
+# define FE_DIVBYZERO 0
+#endif
+#ifndef FE_INVALID
+# define FE_INVALID 0
+#endif
+#ifndef FE_OVERFLOW
+# define FE_OVERFLOW 0
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+#ifdef PNG_ZLIB_HEADER
+# include PNG_ZLIB_HEADER
+#else
+# include <zlib.h> /* For crc32 */
+#endif
+
+/* 1.6.1 added support for the configure test harness, which uses 77 to indicate
+ * a skipped test, in earlier versions we need to succeed on a skipped test, so:
+ */
+#if PNG_LIBPNG_VER < 10601
+# define SKIP 0
+#else
+# define SKIP 77
+#endif
+
+/* pngvalid requires write support and one of the fixed or floating point APIs.
+ */
+#if defined(PNG_WRITE_SUPPORTED) &&\
+ (defined(PNG_FIXED_POINT_SUPPORTED) || defined(PNG_FLOATING_POINT_SUPPORTED))
+
+#if PNG_LIBPNG_VER < 10500
+/* This deliberately lacks the const. */
+typedef png_byte *png_const_bytep;
+
+/* This is copied from 1.5.1 png.h: */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+ -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+ -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
+ (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(xIn, pass) \
+ (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+#define PNG_PASS_MASK(pass,off) ( \
+ ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
+ ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+ ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+ ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
+/* These are needed too for the default build: */
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+
+/* This comes from pnglibconf.h afer 1.5: */
+#define PNG_FP_1 100000
+#define PNG_GAMMA_THRESHOLD_FIXED\
+ ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1))
+#endif
+
+#if PNG_LIBPNG_VER < 10600
+ /* 1.6.0 constifies many APIs, the following exists to allow pngvalid to be
+ * compiled against earlier versions.
+ */
+# define png_const_structp png_structp
+#endif
+
+#include <float.h> /* For floating point constants */
+#include <stdlib.h> /* For malloc */
+#include <string.h> /* For memcpy, memset */
+#include <math.h> /* For floor */
+
+/* Unused formal parameter errors are removed using the following macro which is
+ * expected to have no bad effects on performance.
+ */
+#ifndef UNUSED
+# if defined(__GNUC__) || defined(_MSC_VER)
+# define UNUSED(param) (void)param;
+# else
+# define UNUSED(param)
+# endif
+#endif
+
+/***************************** EXCEPTION HANDLING *****************************/
+#ifdef PNG_FREESTANDING_TESTS
+# include <cexcept.h>
+#else
+# include "../visupng/cexcept.h"
+#endif
+
+#ifdef __cplusplus
+# define this not_the_cpp_this
+# define new not_the_cpp_new
+# define voidcast(type, value) static_cast<type>(value)
+#else
+# define voidcast(type, value) (value)
+#endif /* __cplusplus */
+
+struct png_store;
+define_exception_type(struct png_store*);
+
+/* The following are macros to reduce typing everywhere where the well known
+ * name 'the_exception_context' must be defined.
+ */
+#define anon_context(ps) struct exception_context *the_exception_context = \
+ &(ps)->exception_context
+#define context(ps,fault) anon_context(ps); png_store *fault
+
+/* This macro returns the number of elements in an array as an (unsigned int),
+ * it is necessary to avoid the inability of certain versions of GCC to use
+ * the value of a compile-time constant when performing range checks. It must
+ * be passed an array name.
+ */
+#define ARRAY_SIZE(a) ((unsigned int)((sizeof (a))/(sizeof (a)[0])))
+
+/* GCC BUG 66447 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66447) requires
+ * some broken GCC versions to be fixed up to avoid invalid whining about auto
+ * variables that are *not* changed within the scope of a setjmp being changed.
+ *
+ * Feel free to extend the list of broken versions.
+ */
+#define is_gnu(major,minor)\
+ (defined __GNUC__) && __GNUC__ == (major) && __GNUC_MINOR__ == (minor)
+#define is_gnu_patch(major,minor,patch)\
+ is_gnu(major,minor) && __GNUC_PATCHLEVEL__ == 0
+/* For the moment just do it always; all versions of GCC seem to be broken: */
+#ifdef __GNUC__
+ const void * volatile make_volatile_for_gnu;
+# define gnu_volatile(x) make_volatile_for_gnu = &x;
+#else /* !GNUC broken versions */
+# define gnu_volatile(x)
+#endif /* !GNUC broken versions */
+
+/******************************* UTILITIES ************************************/
+/* Error handling is particularly problematic in production code - error
+ * handlers often themselves have bugs which lead to programs that detect
+ * minor errors crashing. The following functions deal with one very
+ * common class of errors in error handlers - attempting to format error or
+ * warning messages into buffers that are too small.
+ */
+static size_t safecat(char *buffer, size_t bufsize, size_t pos,
+ const char *cat)
+{
+ while (pos < bufsize && cat != NULL && *cat != 0)
+ buffer[pos++] = *cat++;
+
+ if (pos >= bufsize)
+ pos = bufsize-1;
+
+ buffer[pos] = 0;
+ return pos;
+}
+
+static size_t safecatn(char *buffer, size_t bufsize, size_t pos, int n)
+{
+ char number[64];
+ sprintf(number, "%d", n);
+ return safecat(buffer, bufsize, pos, number);
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static size_t safecatd(char *buffer, size_t bufsize, size_t pos, double d,
+ int precision)
+{
+ char number[64];
+ sprintf(number, "%.*f", precision, d);
+ return safecat(buffer, bufsize, pos, number);
+}
+#endif
+
+static const char invalid[] = "invalid";
+static const char sep[] = ": ";
+
+static const char *colour_types[8] =
+{
+ "grayscale", invalid, "truecolour", "indexed-colour",
+ "grayscale with alpha", invalid, "truecolour with alpha", invalid
+};
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Convert a double precision value to fixed point. */
+static png_fixed_point
+fix(double d)
+{
+ d = floor(d * PNG_FP_1 + .5);
+ return (png_fixed_point)d;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Generate random bytes. This uses a boring repeatable algorithm and it
+ * is implemented here so that it gives the same set of numbers on every
+ * architecture. It's a linear congruential generator (Knuth or Sedgewick
+ * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
+ * Hill, "The Art of Electronics" (Pseudo-Random Bit Sequences and Noise
+ * Generation.)
+ */
+static void
+make_random_bytes(png_uint_32* seed, void* pv, size_t size)
+{
+ png_uint_32 u0 = seed[0], u1 = seed[1];
+ png_bytep bytes = voidcast(png_bytep, pv);
+
+ /* There are thirty three bits, the next bit in the sequence is bit-33 XOR
+ * bit-20. The top 1 bit is in u1, the bottom 32 are in u0.
+ */
+ size_t i;
+ for (i=0; i<size; ++i)
+ {
+ /* First generate 8 new bits then shift them in at the end. */
+ png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
+ u1 <<= 8;
+ u1 |= u0 >> 24;
+ u0 <<= 8;
+ u0 |= u;
+ *bytes++ = (png_byte)u;
+ }
+
+ seed[0] = u0;
+ seed[1] = u1;
+}
+
+static void
+make_four_random_bytes(png_uint_32* seed, png_bytep bytes)
+{
+ make_random_bytes(seed, bytes, 4);
+}
+
+#if defined PNG_READ_SUPPORTED || defined PNG_WRITE_tRNS_SUPPORTED
+static void
+randomize(void *pv, size_t size)
+{
+ static png_uint_32 random_seed[2] = {0x56789abc, 0xd};
+ make_random_bytes(random_seed, pv, size);
+}
+
+#define RANDOMIZE(this) randomize(&(this), sizeof (this))
+#endif /* READ || WRITE_tRNS */
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static unsigned int
+random_mod(unsigned int max)
+{
+ unsigned int x;
+
+ RANDOMIZE(x);
+
+ return x % max; /* 0 .. max-1 */
+}
+
+#if (defined PNG_READ_RGB_TO_GRAY_SUPPORTED) ||\
+ (defined PNG_READ_FILLER_SUPPORTED)
+static int
+random_choice(void)
+{
+ unsigned char x;
+
+ RANDOMIZE(x);
+
+ return x & 1;
+}
+#endif
+#endif /* PNG_READ_SUPPORTED */
+
+/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
+ * simply records whether pngvalid did the interlace itself or whether it
+ * was done by libpng. Width and height must be less than 256. 'palette' is an
+ * index of the palette to use for formats with a palette otherwise a boolean
+ * indicating if a tRNS chunk was generated.
+ */
+#define FILEID(col, depth, palette, interlace, width, height, do_interlace) \
+ ((png_uint_32)((col) + ((depth)<<3) + ((palette)<<8) + ((interlace)<<13) + \
+ (((do_interlace)!=0)<<15) + ((width)<<16) + ((height)<<24)))
+
+#define COL_FROM_ID(id) ((png_byte)((id)& 0x7U))
+#define DEPTH_FROM_ID(id) ((png_byte)(((id) >> 3) & 0x1fU))
+#define PALETTE_FROM_ID(id) (((id) >> 8) & 0x1f)
+#define INTERLACE_FROM_ID(id) ((png_byte)(((id) >> 13) & 0x3))
+#define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1))
+#define WIDTH_FROM_ID(id) (((id)>>16) & 0xff)
+#define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff)
+
+/* Utility to construct a standard name for a standard image. */
+static size_t
+standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
+ int bit_depth, unsigned int npalette, int interlace_type,
+ png_uint_32 w, png_uint_32 h, int do_interlace)
+{
+ pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
+ if (colour_type == 3) /* must have a palette */
+ {
+ pos = safecat(buffer, bufsize, pos, "[");
+ pos = safecatn(buffer, bufsize, pos, npalette);
+ pos = safecat(buffer, bufsize, pos, "]");
+ }
+
+ else if (npalette != 0)
+ pos = safecat(buffer, bufsize, pos, "+tRNS");
+
+ pos = safecat(buffer, bufsize, pos, " ");
+ pos = safecatn(buffer, bufsize, pos, bit_depth);
+ pos = safecat(buffer, bufsize, pos, " bit");
+
+ if (interlace_type != PNG_INTERLACE_NONE)
+ {
+ pos = safecat(buffer, bufsize, pos, " interlaced");
+ if (do_interlace)
+ pos = safecat(buffer, bufsize, pos, "(pngvalid)");
+ else
+ pos = safecat(buffer, bufsize, pos, "(libpng)");
+ }
+
+ if (w > 0 || h > 0)
+ {
+ pos = safecat(buffer, bufsize, pos, " ");
+ pos = safecatn(buffer, bufsize, pos, w);
+ pos = safecat(buffer, bufsize, pos, "x");
+ pos = safecatn(buffer, bufsize, pos, h);
+ }
+
+ return pos;
+}
+
+static size_t
+standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
+{
+ return standard_name(buffer, bufsize, pos, COL_FROM_ID(id),
+ DEPTH_FROM_ID(id), PALETTE_FROM_ID(id), INTERLACE_FROM_ID(id),
+ WIDTH_FROM_ID(id), HEIGHT_FROM_ID(id), DO_INTERLACE_FROM_ID(id));
+}
+
+/* Convenience API and defines to list valid formats. Note that 16 bit read and
+ * write support is required to do 16 bit read tests (we must be able to make a
+ * 16 bit image to test!)
+ */
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+# define WRITE_BDHI 4
+# ifdef PNG_READ_16BIT_SUPPORTED
+# define READ_BDHI 4
+# define DO_16BIT
+# endif
+#else
+# define WRITE_BDHI 3
+#endif
+#ifndef DO_16BIT
+# define READ_BDHI 3
+#endif
+
+/* The following defines the number of different palettes to generate for
+ * each log bit depth of a colour type 3 standard image.
+ */
+#define PALETTE_COUNT(bit_depth) ((bit_depth) > 4 ? 1U : 16U)
+
+static int
+next_format(png_bytep colour_type, png_bytep bit_depth,
+ unsigned int* palette_number, int low_depth_gray, int tRNS)
+{
+ if (*bit_depth == 0)
+ {
+ *colour_type = 0;
+ if (low_depth_gray)
+ *bit_depth = 1;
+ else
+ *bit_depth = 8;
+ *palette_number = 0;
+ return 1;
+ }
+
+ if (*colour_type < 4/*no alpha channel*/)
+ {
+ /* Add multiple palettes for colour type 3, one image with tRNS
+ * and one without for other non-alpha formats:
+ */
+ unsigned int pn = ++*palette_number;
+ png_byte ct = *colour_type;
+
+ if (((ct == 0/*GRAY*/ || ct/*RGB*/ == 2) && tRNS && pn < 2) ||
+ (ct == 3/*PALETTE*/ && pn < PALETTE_COUNT(*bit_depth)))
+ return 1;
+
+ /* No: next bit depth */
+ *palette_number = 0;
+ }
+
+ *bit_depth = (png_byte)(*bit_depth << 1);
+
+ /* Palette images are restricted to 8 bit depth */
+ if (*bit_depth <= 8
+#ifdef DO_16BIT
+ || (*colour_type != 3 && *bit_depth <= 16)
+#endif
+ )
+ return 1;
+
+ /* Move to the next color type, or return 0 at the end. */
+ switch (*colour_type)
+ {
+ case 0:
+ *colour_type = 2;
+ *bit_depth = 8;
+ return 1;
+
+ case 2:
+ *colour_type = 3;
+ *bit_depth = 1;
+ return 1;
+
+ case 3:
+ *colour_type = 4;
+ *bit_depth = 8;
+ return 1;
+
+ case 4:
+ *colour_type = 6;
+ *bit_depth = 8;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static unsigned int
+sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
+ png_uint_32 x, unsigned int sample_index, int swap16, int littleendian)
+{
+ png_uint_32 bit_index, result;
+
+ /* Find a sample index for the desired sample: */
+ x *= bit_depth;
+ bit_index = x;
+
+ if ((colour_type & 1) == 0) /* !palette */
+ {
+ if (colour_type & 2)
+ bit_index *= 3;
+
+ if (colour_type & 4)
+ bit_index += x; /* Alpha channel */
+
+ /* Multiple channels; select one: */
+ if (colour_type & (2+4))
+ bit_index += sample_index * bit_depth;
+ }
+
+ /* Return the sample from the row as an integer. */
+ row += bit_index >> 3;
+ result = *row;
+
+ if (bit_depth == 8)
+ return result;
+
+ else if (bit_depth > 8)
+ {
+ if (swap16)
+ return (*++row << 8) + result;
+ else
+ return (result << 8) + *++row;
+ }
+
+ /* Less than 8 bits per sample. By default PNG has the big end of
+ * the egg on the left of the screen, but if littleendian is set
+ * then the big end is on the right.
+ */
+ bit_index &= 7;
+
+ if (!littleendian)
+ bit_index = 8-bit_index-bit_depth;
+
+ return (result >> bit_index) & ((1U<<bit_depth)-1);
+}
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/* Copy a single pixel, of a given size, from one buffer to another -
+ * while this is basically bit addressed there is an implicit assumption
+ * that pixels 8 or more bits in size are byte aligned and that pixels
+ * do not otherwise cross byte boundaries. (This is, so far as I know,
+ * universally true in bitmap computer graphics. [JCB 20101212])
+ *
+ * NOTE: The to and from buffers may be the same.
+ */
+static void
+pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
+ png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize,
+ int littleendian)
+{
+ /* Assume we can multiply by 'size' without overflow because we are
+ * just working in a single buffer.
+ */
+ toIndex *= pixelSize;
+ fromIndex *= pixelSize;
+ if (pixelSize < 8) /* Sub-byte */
+ {
+ /* Mask to select the location of the copied pixel: */
+ unsigned int destMask = ((1U<<pixelSize)-1) <<
+ (littleendian ? toIndex&7 : 8-pixelSize-(toIndex&7));
+ /* The following read the entire pixels and clears the extra: */
+ unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
+ unsigned int sourceByte = fromBuffer[fromIndex >> 3];
+
+ /* Don't rely on << or >> supporting '0' here, just in case: */
+ fromIndex &= 7;
+ if (littleendian)
+ {
+ if (fromIndex > 0) sourceByte >>= fromIndex;
+ if ((toIndex & 7) > 0) sourceByte <<= toIndex & 7;
+ }
+
+ else
+ {
+ if (fromIndex > 0) sourceByte <<= fromIndex;
+ if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
+ }
+
+ toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
+ }
+ else /* One or more bytes */
+ memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3);
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Copy a complete row of pixels, taking into account potential partial
+ * bytes at the end.
+ */
+static void
+row_copy(png_bytep toBuffer, png_const_bytep fromBuffer, unsigned int bitWidth,
+ int littleendian)
+{
+ memcpy(toBuffer, fromBuffer, bitWidth >> 3);
+
+ if ((bitWidth & 7) != 0)
+ {
+ unsigned int mask;
+
+ toBuffer += bitWidth >> 3;
+ fromBuffer += bitWidth >> 3;
+ if (littleendian)
+ mask = 0xff << (bitWidth & 7);
+ else
+ mask = 0xff >> (bitWidth & 7);
+ *toBuffer = (png_byte)((*toBuffer & mask) | (*fromBuffer & ~mask));
+ }
+}
+
+/* Compare pixels - they are assumed to start at the first byte in the
+ * given buffers.
+ */
+static int
+pixel_cmp(png_const_bytep pa, png_const_bytep pb, png_uint_32 bit_width)
+{
+#if PNG_LIBPNG_VER < 10506
+ if (memcmp(pa, pb, bit_width>>3) == 0)
+ {
+ png_uint_32 p;
+
+ if ((bit_width & 7) == 0) return 0;
+
+ /* Ok, any differences? */
+ p = pa[bit_width >> 3];
+ p ^= pb[bit_width >> 3];
+
+ if (p == 0) return 0;
+
+ /* There are, but they may not be significant, remove the bits
+ * after the end (the low order bits in PNG.)
+ */
+ bit_width &= 7;
+ p >>= 8-bit_width;
+
+ if (p == 0) return 0;
+ }
+#else
+ /* From libpng-1.5.6 the overwrite should be fixed, so compare the trailing
+ * bits too:
+ */
+ if (memcmp(pa, pb, (bit_width+7)>>3) == 0)
+ return 0;
+#endif
+
+ /* Return the index of the changed byte. */
+ {
+ png_uint_32 where = 0;
+
+ while (pa[where] == pb[where]) ++where;
+ return 1+where;
+ }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/*************************** BASIC PNG FILE WRITING ***************************/
+/* A png_store takes data from the sequential writer or provides data
+ * to the sequential reader. It can also store the result of a PNG
+ * write for later retrieval.
+ */
+#define STORE_BUFFER_SIZE 500 /* arbitrary */
+typedef struct png_store_buffer
+{
+ struct png_store_buffer* prev; /* NOTE: stored in reverse order */
+ png_byte buffer[STORE_BUFFER_SIZE];
+} png_store_buffer;
+
+#define FILE_NAME_SIZE 64
+
+typedef struct store_palette_entry /* record of a single palette entry */
+{
+ png_byte red;
+ png_byte green;
+ png_byte blue;
+ png_byte alpha;
+} store_palette_entry, store_palette[256];
+
+typedef struct png_store_file
+{
+ struct png_store_file* next; /* as many as you like... */
+ char name[FILE_NAME_SIZE];
+ png_uint_32 id; /* must be correct (see FILEID) */
+ png_size_t datacount; /* In this (the last) buffer */
+ png_store_buffer data; /* Last buffer in file */
+ int npalette; /* Number of entries in palette */
+ store_palette_entry* palette; /* May be NULL */
+} png_store_file;
+
+/* The following is a pool of memory allocated by a single libpng read or write
+ * operation.
+ */
+typedef struct store_pool
+{
+ struct png_store *store; /* Back pointer */
+ struct store_memory *list; /* List of allocated memory */
+ png_byte mark[4]; /* Before and after data */
+
+ /* Statistics for this run. */
+ png_alloc_size_t max; /* Maximum single allocation */
+ png_alloc_size_t current; /* Current allocation */
+ png_alloc_size_t limit; /* Highest current allocation */
+ png_alloc_size_t total; /* Total allocation */
+
+ /* Overall statistics (retained across successive runs). */
+ png_alloc_size_t max_max;
+ png_alloc_size_t max_limit;
+ png_alloc_size_t max_total;
+} store_pool;
+
+typedef struct png_store
+{
+ /* For cexcept.h exception handling - simply store one of these;
+ * the context is a self pointer but it may point to a different
+ * png_store (in fact it never does in this program.)
+ */
+ struct exception_context
+ exception_context;
+
+ unsigned int verbose :1;
+ unsigned int treat_warnings_as_errors :1;
+ unsigned int expect_error :1;
+ unsigned int expect_warning :1;
+ unsigned int saw_warning :1;
+ unsigned int speed :1;
+ unsigned int progressive :1; /* use progressive read */
+ unsigned int validated :1; /* used as a temporary flag */
+ int nerrors;
+ int nwarnings;
+ int noptions; /* number of options below: */
+ struct {
+ unsigned char option; /* option number, 0..30 */
+ unsigned char setting; /* setting (unset,invalid,on,off) */
+ } options[16];
+ char test[128]; /* Name of test */
+ char error[256];
+
+ /* Read fields */
+ png_structp pread; /* Used to read a saved file */
+ png_infop piread;
+ png_store_file* current; /* Set when reading */
+ png_store_buffer* next; /* Set when reading */
+ png_size_t readpos; /* Position in *next */
+ png_byte* image; /* Buffer for reading interlaced images */
+ png_size_t cb_image; /* Size of this buffer */
+ png_size_t cb_row; /* Row size of the image(s) */
+ png_uint_32 image_h; /* Number of rows in a single image */
+ store_pool read_memory_pool;
+
+ /* Write fields */
+ png_store_file* saved;
+ png_structp pwrite; /* Used when writing a new file */
+ png_infop piwrite;
+ png_size_t writepos; /* Position in .new */
+ char wname[FILE_NAME_SIZE];
+ png_store_buffer new; /* The end of the new PNG file being written. */
+ store_pool write_memory_pool;
+ store_palette_entry* palette;
+ int npalette;
+} png_store;
+
+/* Initialization and cleanup */
+static void
+store_pool_mark(png_bytep mark)
+{
+ static png_uint_32 store_seed[2] = { 0x12345678, 1};
+
+ make_four_random_bytes(store_seed, mark);
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Use this for random 32 bit values; this function makes sure the result is
+ * non-zero.
+ */
+static png_uint_32
+random_32(void)
+{
+
+ for (;;)
+ {
+ png_byte mark[4];
+ png_uint_32 result;
+
+ store_pool_mark(mark);
+ result = png_get_uint_32(mark);
+
+ if (result != 0)
+ return result;
+ }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+static void
+store_pool_init(png_store *ps, store_pool *pool)
+{
+ memset(pool, 0, sizeof *pool);
+
+ pool->store = ps;
+ pool->list = NULL;
+ pool->max = pool->current = pool->limit = pool->total = 0;
+ pool->max_max = pool->max_limit = pool->max_total = 0;
+ store_pool_mark(pool->mark);
+}
+
+static void
+store_init(png_store* ps)
+{
+ memset(ps, 0, sizeof *ps);
+ init_exception_context(&ps->exception_context);
+ store_pool_init(ps, &ps->read_memory_pool);
+ store_pool_init(ps, &ps->write_memory_pool);
+ ps->verbose = 0;
+ ps->treat_warnings_as_errors = 0;
+ ps->expect_error = 0;
+ ps->expect_warning = 0;
+ ps->saw_warning = 0;
+ ps->speed = 0;
+ ps->progressive = 0;
+ ps->validated = 0;
+ ps->nerrors = ps->nwarnings = 0;
+ ps->pread = NULL;
+ ps->piread = NULL;
+ ps->saved = ps->current = NULL;
+ ps->next = NULL;
+ ps->readpos = 0;
+ ps->image = NULL;
+ ps->cb_image = 0;
+ ps->cb_row = 0;
+ ps->image_h = 0;
+ ps->pwrite = NULL;
+ ps->piwrite = NULL;
+ ps->writepos = 0;
+ ps->new.prev = NULL;
+ ps->palette = NULL;
+ ps->npalette = 0;
+ ps->noptions = 0;
+}
+
+static void
+store_freebuffer(png_store_buffer* psb)
+{
+ if (psb->prev)
+ {
+ store_freebuffer(psb->prev);
+ free(psb->prev);
+ psb->prev = NULL;
+ }
+}
+
+static void
+store_freenew(png_store *ps)
+{
+ store_freebuffer(&ps->new);
+ ps->writepos = 0;
+ if (ps->palette != NULL)
+ {
+ free(ps->palette);
+ ps->palette = NULL;
+ ps->npalette = 0;
+ }
+}
+
+static void
+store_storenew(png_store *ps)
+{
+ png_store_buffer *pb;
+
+ if (ps->writepos != STORE_BUFFER_SIZE)
+ png_error(ps->pwrite, "invalid store call");
+
+ pb = voidcast(png_store_buffer*, malloc(sizeof *pb));
+
+ if (pb == NULL)
+ png_error(ps->pwrite, "store new: OOM");
+
+ *pb = ps->new;
+ ps->new.prev = pb;
+ ps->writepos = 0;
+}
+
+static void
+store_freefile(png_store_file **ppf)
+{
+ if (*ppf != NULL)
+ {
+ store_freefile(&(*ppf)->next);
+
+ store_freebuffer(&(*ppf)->data);
+ (*ppf)->datacount = 0;
+ if ((*ppf)->palette != NULL)
+ {
+ free((*ppf)->palette);
+ (*ppf)->palette = NULL;
+ (*ppf)->npalette = 0;
+ }
+ free(*ppf);
+ *ppf = NULL;
+ }
+}
+
+/* Main interface to file storeage, after writing a new PNG file (see the API
+ * below) call store_storefile to store the result with the given name and id.
+ */
+static void
+store_storefile(png_store *ps, png_uint_32 id)
+{
+ png_store_file *pf = voidcast(png_store_file*, malloc(sizeof *pf));
+ if (pf == NULL)
+ png_error(ps->pwrite, "storefile: OOM");
+ safecat(pf->name, sizeof pf->name, 0, ps->wname);
+ pf->id = id;
+ pf->data = ps->new;
+ pf->datacount = ps->writepos;
+ ps->new.prev = NULL;
+ ps->writepos = 0;
+ pf->palette = ps->palette;
+ pf->npalette = ps->npalette;
+ ps->palette = 0;
+ ps->npalette = 0;
+
+ /* And save it. */
+ pf->next = ps->saved;
+ ps->saved = pf;
+}
+
+/* Generate an error message (in the given buffer) */
+static size_t
+store_message(png_store *ps, png_const_structp pp, char *buffer, size_t bufsize,
+ size_t pos, const char *msg)
+{
+ if (pp != NULL && pp == ps->pread)
+ {
+ /* Reading a file */
+ pos = safecat(buffer, bufsize, pos, "read: ");
+
+ if (ps->current != NULL)
+ {
+ pos = safecat(buffer, bufsize, pos, ps->current->name);
+ pos = safecat(buffer, bufsize, pos, sep);
+ }
+ }
+
+ else if (pp != NULL && pp == ps->pwrite)
+ {
+ /* Writing a file */
+ pos = safecat(buffer, bufsize, pos, "write: ");
+ pos = safecat(buffer, bufsize, pos, ps->wname);
+ pos = safecat(buffer, bufsize, pos, sep);
+ }
+
+ else
+ {
+ /* Neither reading nor writing (or a memory error in struct delete) */
+ pos = safecat(buffer, bufsize, pos, "pngvalid: ");
+ }
+
+ if (ps->test[0] != 0)
+ {
+ pos = safecat(buffer, bufsize, pos, ps->test);
+ pos = safecat(buffer, bufsize, pos, sep);
+ }
+ pos = safecat(buffer, bufsize, pos, msg);
+ return pos;
+}
+
+/* Verbose output to the error stream: */
+static void
+store_verbose(png_store *ps, png_const_structp pp, png_const_charp prefix,
+ png_const_charp message)
+{
+ char buffer[512];
+
+ if (prefix)
+ fputs(prefix, stderr);
+
+ (void)store_message(ps, pp, buffer, sizeof buffer, 0, message);
+ fputs(buffer, stderr);
+ fputc('\n', stderr);
+}
+
+/* Log an error or warning - the relevant count is always incremented. */
+static void
+store_log(png_store* ps, png_const_structp pp, png_const_charp message,
+ int is_error)
+{
+ /* The warning is copied to the error buffer if there are no errors and it is
+ * the first warning. The error is copied to the error buffer if it is the
+ * first error (overwriting any prior warnings).
+ */
+ if (is_error ? (ps->nerrors)++ == 0 :
+ (ps->nwarnings)++ == 0 && ps->nerrors == 0)
+ store_message(ps, pp, ps->error, sizeof ps->error, 0, message);
+
+ if (ps->verbose)
+ store_verbose(ps, pp, is_error ? "error: " : "warning: ", message);
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Internal error function, called with a png_store but no libpng stuff. */
+static void
+internal_error(png_store *ps, png_const_charp message)
+{
+ store_log(ps, NULL, message, 1 /* error */);
+
+ /* And finally throw an exception. */
+ {
+ struct exception_context *the_exception_context = &ps->exception_context;
+ Throw ps;
+ }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Functions to use as PNG callbacks. */
+static void PNGCBAPI
+store_error(png_structp ppIn, png_const_charp message) /* PNG_NORETURN */
+{
+ png_const_structp pp = ppIn;
+ png_store *ps = voidcast(png_store*, png_get_error_ptr(pp));
+
+ if (!ps->expect_error)
+ store_log(ps, pp, message, 1 /* error */);
+
+ /* And finally throw an exception. */
+ {
+ struct exception_context *the_exception_context = &ps->exception_context;
+ Throw ps;
+ }
+}
+
+static void PNGCBAPI
+store_warning(png_structp ppIn, png_const_charp message)
+{
+ png_const_structp pp = ppIn;
+ png_store *ps = voidcast(png_store*, png_get_error_ptr(pp));
+
+ if (!ps->expect_warning)
+ store_log(ps, pp, message, 0 /* warning */);
+ else
+ ps->saw_warning = 1;
+}
+
+/* These somewhat odd functions are used when reading an image to ensure that
+ * the buffer is big enough, the png_structp is for errors.
+ */
+/* Return a single row from the correct image. */
+static png_bytep
+store_image_row(const png_store* ps, png_const_structp pp, int nImage,
+ png_uint_32 y)
+{
+ png_size_t coffset = (nImage * ps->image_h + y) * (ps->cb_row + 5) + 2;
+
+ if (ps->image == NULL)
+ png_error(pp, "no allocated image");
+
+ if (coffset + ps->cb_row + 3 > ps->cb_image)
+ png_error(pp, "image too small");
+
+ return ps->image + coffset;
+}
+
+static void
+store_image_free(png_store *ps, png_const_structp pp)
+{
+ if (ps->image != NULL)
+ {
+ png_bytep image = ps->image;
+
+ if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
+ {
+ if (pp != NULL)
+ png_error(pp, "png_store image overwrite (1)");
+ else
+ store_log(ps, NULL, "png_store image overwrite (2)", 1);
+ }
+
+ ps->image = NULL;
+ ps->cb_image = 0;
+ --image;
+ free(image);
+ }
+}
+
+static void
+store_ensure_image(png_store *ps, png_const_structp pp, int nImages,
+ png_size_t cbRow, png_uint_32 cRows)
+{
+ png_size_t cb = nImages * cRows * (cbRow + 5);
+
+ if (ps->cb_image < cb)
+ {
+ png_bytep image;
+
+ store_image_free(ps, pp);
+
+ /* The buffer is deliberately mis-aligned. */
+ image = voidcast(png_bytep, malloc(cb+2));
+ if (image == NULL)
+ {
+ /* Called from the startup - ignore the error for the moment. */
+ if (pp == NULL)
+ return;
+
+ png_error(pp, "OOM allocating image buffer");
+ }
+
+ /* These magic tags are used to detect overwrites above. */
+ ++image;
+ image[-1] = 0xed;
+ image[cb] = 0xfe;
+
+ ps->image = image;
+ ps->cb_image = cb;
+ }
+
+ /* We have an adequate sized image; lay out the rows. There are 2 bytes at
+ * the start and three at the end of each (this ensures that the row
+ * alignment starts out odd - 2+1 and changes for larger images on each row.)
+ */
+ ps->cb_row = cbRow;
+ ps->image_h = cRows;
+
+ /* For error checking, the whole buffer is set to 10110010 (0xb2 - 178).
+ * This deliberately doesn't match the bits in the size test image which are
+ * outside the image; these are set to 0xff (all 1). To make the row
+ * comparison work in the 'size' test case the size rows are pre-initialized
+ * to the same value prior to calling 'standard_row'.
+ */
+ memset(ps->image, 178, cb);
+
+ /* Then put in the marks. */
+ while (--nImages >= 0)
+ {
+ png_uint_32 y;
+
+ for (y=0; y<cRows; ++y)
+ {
+ png_bytep row = store_image_row(ps, pp, nImages, y);
+
+ /* The markers: */
+ row[-2] = 190;
+ row[-1] = 239;
+ row[cbRow] = 222;
+ row[cbRow+1] = 173;
+ row[cbRow+2] = 17;
+ }
+ }
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+store_image_check(const png_store* ps, png_const_structp pp, int iImage)
+{
+ png_const_bytep image = ps->image;
+
+ if (image[-1] != 0xed || image[ps->cb_image] != 0xfe)
+ png_error(pp, "image overwrite");
+ else
+ {
+ png_size_t cbRow = ps->cb_row;
+ png_uint_32 rows = ps->image_h;
+
+ image += iImage * (cbRow+5) * ps->image_h;
+
+ image += 2; /* skip image first row markers */
+
+ while (rows-- > 0)
+ {
+ if (image[-2] != 190 || image[-1] != 239)
+ png_error(pp, "row start overwritten");
+
+ if (image[cbRow] != 222 || image[cbRow+1] != 173 ||
+ image[cbRow+2] != 17)
+ png_error(pp, "row end overwritten");
+
+ image += cbRow+5;
+ }
+ }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+static void PNGCBAPI
+store_write(png_structp ppIn, png_bytep pb, png_size_t st)
+{
+ png_const_structp pp = ppIn;
+ png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
+
+ if (ps->pwrite != pp)
+ png_error(pp, "store state damaged");
+
+ while (st > 0)
+ {
+ size_t cb;
+
+ if (ps->writepos >= STORE_BUFFER_SIZE)
+ store_storenew(ps);
+
+ cb = st;
+
+ if (cb > STORE_BUFFER_SIZE - ps->writepos)
+ cb = STORE_BUFFER_SIZE - ps->writepos;
+
+ memcpy(ps->new.buffer + ps->writepos, pb, cb);
+ pb += cb;
+ st -= cb;
+ ps->writepos += cb;
+ }
+}
+
+static void PNGCBAPI
+store_flush(png_structp ppIn)
+{
+ UNUSED(ppIn) /*DOES NOTHING*/
+}
+
+#ifdef PNG_READ_SUPPORTED
+static size_t
+store_read_buffer_size(png_store *ps)
+{
+ /* Return the bytes available for read in the current buffer. */
+ if (ps->next != &ps->current->data)
+ return STORE_BUFFER_SIZE;
+
+ return ps->current->datacount;
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* Return total bytes available for read. */
+static size_t
+store_read_buffer_avail(png_store *ps)
+{
+ if (ps->current != NULL && ps->next != NULL)
+ {
+ png_store_buffer *next = &ps->current->data;
+ size_t cbAvail = ps->current->datacount;
+
+ while (next != ps->next && next != NULL)
+ {
+ next = next->prev;
+ cbAvail += STORE_BUFFER_SIZE;
+ }
+
+ if (next != ps->next)
+ png_error(ps->pread, "buffer read error");
+
+ if (cbAvail > ps->readpos)
+ return cbAvail - ps->readpos;
+ }
+
+ return 0;
+}
+#endif
+
+static int
+store_read_buffer_next(png_store *ps)
+{
+ png_store_buffer *pbOld = ps->next;
+ png_store_buffer *pbNew = &ps->current->data;
+ if (pbOld != pbNew)
+ {
+ while (pbNew != NULL && pbNew->prev != pbOld)
+ pbNew = pbNew->prev;
+
+ if (pbNew != NULL)
+ {
+ ps->next = pbNew;
+ ps->readpos = 0;
+ return 1;
+ }
+
+ png_error(ps->pread, "buffer lost");
+ }
+
+ return 0; /* EOF or error */
+}
+
+/* Need separate implementation and callback to allow use of the same code
+ * during progressive read, where the io_ptr is set internally by libpng.
+ */
+static void
+store_read_imp(png_store *ps, png_bytep pb, png_size_t st)
+{
+ if (ps->current == NULL || ps->next == NULL)
+ png_error(ps->pread, "store state damaged");
+
+ while (st > 0)
+ {
+ size_t cbAvail = store_read_buffer_size(ps) - ps->readpos;
+
+ if (cbAvail > 0)
+ {
+ if (cbAvail > st) cbAvail = st;
+ memcpy(pb, ps->next->buffer + ps->readpos, cbAvail);
+ st -= cbAvail;
+ pb += cbAvail;
+ ps->readpos += cbAvail;
+ }
+
+ else if (!store_read_buffer_next(ps))
+ png_error(ps->pread, "read beyond end of file");
+ }
+}
+
+static void PNGCBAPI
+store_read(png_structp ppIn, png_bytep pb, png_size_t st)
+{
+ png_const_structp pp = ppIn;
+ png_store *ps = voidcast(png_store*, png_get_io_ptr(pp));
+
+ if (ps == NULL || ps->pread != pp)
+ png_error(pp, "bad store read call");
+
+ store_read_imp(ps, pb, st);
+}
+
+static void
+store_progressive_read(png_store *ps, png_structp pp, png_infop pi)
+{
+ /* Notice that a call to store_read will cause this function to fail because
+ * readpos will be set.
+ */
+ if (ps->pread != pp || ps->current == NULL || ps->next == NULL)
+ png_error(pp, "store state damaged (progressive)");
+
+ do
+ {
+ if (ps->readpos != 0)
+ png_error(pp, "store_read called during progressive read");
+
+ png_process_data(pp, pi, ps->next->buffer, store_read_buffer_size(ps));
+ }
+ while (store_read_buffer_next(ps));
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* The caller must fill this in: */
+static store_palette_entry *
+store_write_palette(png_store *ps, int npalette)
+{
+ if (ps->pwrite == NULL)
+ store_log(ps, NULL, "attempt to write palette without write stream", 1);
+
+ if (ps->palette != NULL)
+ png_error(ps->pwrite, "multiple store_write_palette calls");
+
+ /* This function can only return NULL if called with '0'! */
+ if (npalette > 0)
+ {
+ ps->palette = voidcast(store_palette_entry*, malloc(npalette *
+ sizeof *ps->palette));
+
+ if (ps->palette == NULL)
+ png_error(ps->pwrite, "store new palette: OOM");
+
+ ps->npalette = npalette;
+ }
+
+ return ps->palette;
+}
+
+#ifdef PNG_READ_SUPPORTED
+static store_palette_entry *
+store_current_palette(png_store *ps, int *npalette)
+{
+ /* This is an internal error (the call has been made outside a read
+ * operation.)
+ */
+ if (ps->current == NULL)
+ {
+ store_log(ps, ps->pread, "no current stream for palette", 1);
+ return NULL;
+ }
+
+ /* The result may be null if there is no palette. */
+ *npalette = ps->current->npalette;
+ return ps->current->palette;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/***************************** MEMORY MANAGEMENT*** ***************************/
+#ifdef PNG_USER_MEM_SUPPORTED
+/* A store_memory is simply the header for an allocated block of memory. The
+ * pointer returned to libpng is just after the end of the header block, the
+ * allocated memory is followed by a second copy of the 'mark'.
+ */
+typedef struct store_memory
+{
+ store_pool *pool; /* Originating pool */
+ struct store_memory *next; /* Singly linked list */
+ png_alloc_size_t size; /* Size of memory allocated */
+ png_byte mark[4]; /* ID marker */
+} store_memory;
+
+/* Handle a fatal error in memory allocation. This calls png_error if the
+ * libpng struct is non-NULL, else it outputs a message and returns. This means
+ * that a memory problem while libpng is running will abort (png_error) the
+ * handling of particular file while one in cleanup (after the destroy of the
+ * struct has returned) will simply keep going and free (or attempt to free)
+ * all the memory.
+ */
+static void
+store_pool_error(png_store *ps, png_const_structp pp, const char *msg)
+{
+ if (pp != NULL)
+ png_error(pp, msg);
+
+ /* Else we have to do it ourselves. png_error eventually calls store_log,
+ * above. store_log accepts a NULL png_structp - it just changes what gets
+ * output by store_message.
+ */
+ store_log(ps, pp, msg, 1 /* error */);
+}
+
+static void
+store_memory_free(png_const_structp pp, store_pool *pool, store_memory *memory)
+{
+ /* Note that pp may be NULL (see store_pool_delete below), the caller has
+ * found 'memory' in pool->list *and* unlinked this entry, so this is a valid
+ * pointer (for sure), but the contents may have been trashed.
+ */
+ if (memory->pool != pool)
+ store_pool_error(pool->store, pp, "memory corrupted (pool)");
+
+ else if (memcmp(memory->mark, pool->mark, sizeof memory->mark) != 0)
+ store_pool_error(pool->store, pp, "memory corrupted (start)");
+
+ /* It should be safe to read the size field now. */
+ else
+ {
+ png_alloc_size_t cb = memory->size;
+
+ if (cb > pool->max)
+ store_pool_error(pool->store, pp, "memory corrupted (size)");
+
+ else if (memcmp((png_bytep)(memory+1)+cb, pool->mark, sizeof pool->mark)
+ != 0)
+ store_pool_error(pool->store, pp, "memory corrupted (end)");
+
+ /* Finally give the library a chance to find problems too: */
+ else
+ {
+ pool->current -= cb;
+ free(memory);
+ }
+ }
+}
+
+static void
+store_pool_delete(png_store *ps, store_pool *pool)
+{
+ if (pool->list != NULL)
+ {
+ fprintf(stderr, "%s: %s %s: memory lost (list follows):\n", ps->test,
+ pool == &ps->read_memory_pool ? "read" : "write",
+ pool == &ps->read_memory_pool ? (ps->current != NULL ?
+ ps->current->name : "unknown file") : ps->wname);
+ ++ps->nerrors;
+
+ do
+ {
+ store_memory *next = pool->list;
+ pool->list = next->next;
+ next->next = NULL;
+
+ fprintf(stderr, "\t%lu bytes @ %p\n",
+ (unsigned long)next->size, (const void*)(next+1));
+ /* The NULL means this will always return, even if the memory is
+ * corrupted.
+ */
+ store_memory_free(NULL, pool, next);
+ }
+ while (pool->list != NULL);
+ }
+
+ /* And reset the other fields too for the next time. */
+ if (pool->max > pool->max_max) pool->max_max = pool->max;
+ pool->max = 0;
+ if (pool->current != 0) /* unexpected internal error */
+ fprintf(stderr, "%s: %s %s: memory counter mismatch (internal error)\n",
+ ps->test, pool == &ps->read_memory_pool ? "read" : "write",
+ pool == &ps->read_memory_pool ? (ps->current != NULL ?
+ ps->current->name : "unknown file") : ps->wname);
+ pool->current = 0;
+
+ if (pool->limit > pool->max_limit)
+ pool->max_limit = pool->limit;
+
+ pool->limit = 0;
+
+ if (pool->total > pool->max_total)
+ pool->max_total = pool->total;
+
+ pool->total = 0;
+
+ /* Get a new mark too. */
+ store_pool_mark(pool->mark);
+}
+
+/* The memory callbacks: */
+static png_voidp PNGCBAPI
+store_malloc(png_structp ppIn, png_alloc_size_t cb)
+{
+ png_const_structp pp = ppIn;
+ store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp));
+ store_memory *new = voidcast(store_memory*, malloc(cb + (sizeof *new) +
+ (sizeof pool->mark)));
+
+ if (new != NULL)
+ {
+ if (cb > pool->max)
+ pool->max = cb;
+
+ pool->current += cb;
+
+ if (pool->current > pool->limit)
+ pool->limit = pool->current;
+
+ pool->total += cb;
+
+ new->size = cb;
+ memcpy(new->mark, pool->mark, sizeof new->mark);
+ memcpy((png_byte*)(new+1) + cb, pool->mark, sizeof pool->mark);
+ new->pool = pool;
+ new->next = pool->list;
+ pool->list = new;
+ ++new;
+ }
+
+ else
+ {
+ /* NOTE: the PNG user malloc function cannot use the png_ptr it is passed
+ * other than to retrieve the allocation pointer! libpng calls the
+ * store_malloc callback in two basic cases:
+ *
+ * 1) From png_malloc; png_malloc will do a png_error itself if NULL is
+ * returned.
+ * 2) From png_struct or png_info structure creation; png_malloc is
+ * to return so cleanup can be performed.
+ *
+ * To handle this store_malloc can log a message, but can't do anything
+ * else.
+ */
+ store_log(pool->store, pp, "out of memory", 1 /* is_error */);
+ }
+
+ return new;
+}
+
+static void PNGCBAPI
+store_free(png_structp ppIn, png_voidp memory)
+{
+ png_const_structp pp = ppIn;
+ store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp));
+ store_memory *this = voidcast(store_memory*, memory), **test;
+
+ /* Because libpng calls store_free with a dummy png_struct when deleting
+ * png_struct or png_info via png_destroy_struct_2 it is necessary to check
+ * the passed in png_structp to ensure it is valid, and not pass it to
+ * png_error if it is not.
+ */
+ if (pp != pool->store->pread && pp != pool->store->pwrite)
+ pp = NULL;
+
+ /* First check that this 'memory' really is valid memory - it must be in the
+ * pool list. If it is, use the shared memory_free function to free it.
+ */
+ --this;
+ for (test = &pool->list; *test != this; test = &(*test)->next)
+ {
+ if (*test == NULL)
+ {
+ store_pool_error(pool->store, pp, "bad pointer to free");
+ return;
+ }
+ }
+
+ /* Unlink this entry, *test == this. */
+ *test = this->next;
+ this->next = NULL;
+ store_memory_free(pp, pool, this);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+/* Setup functions. */
+/* Cleanup when aborting a write or after storing the new file. */
+static void
+store_write_reset(png_store *ps)
+{
+ if (ps->pwrite != NULL)
+ {
+ anon_context(ps);
+
+ Try
+ png_destroy_write_struct(&ps->pwrite, &ps->piwrite);
+
+ Catch_anonymous
+ {
+ /* memory corruption: continue. */
+ }
+
+ ps->pwrite = NULL;
+ ps->piwrite = NULL;
+ }
+
+ /* And make sure that all the memory has been freed - this will output
+ * spurious errors in the case of memory corruption above, but this is safe.
+ */
+# ifdef PNG_USER_MEM_SUPPORTED
+ store_pool_delete(ps, &ps->write_memory_pool);
+# endif
+
+ store_freenew(ps);
+}
+
+/* The following is the main write function, it returns a png_struct and,
+ * optionally, a png_info suitable for writiing a new PNG file. Use
+ * store_storefile above to record this file after it has been written. The
+ * returned libpng structures as destroyed by store_write_reset above.
+ */
+static png_structp
+set_store_for_write(png_store *ps, png_infopp ppi, const char *name)
+{
+ anon_context(ps);
+
+ Try
+ {
+ if (ps->pwrite != NULL)
+ png_error(ps->pwrite, "write store already in use");
+
+ store_write_reset(ps);
+ safecat(ps->wname, sizeof ps->wname, 0, name);
+
+ /* Don't do the slow memory checks if doing a speed test, also if user
+ * memory is not supported we can't do it anyway.
+ */
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (!ps->speed)
+ ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
+ ps, store_error, store_warning, &ps->write_memory_pool,
+ store_malloc, store_free);
+
+ else
+# endif
+ ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ ps, store_error, store_warning);
+
+ png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
+
+# ifdef PNG_SET_OPTION_SUPPORTED
+ {
+ int opt;
+ for (opt=0; opt<ps->noptions; ++opt)
+ if (png_set_option(ps->pwrite, ps->options[opt].option,
+ ps->options[opt].setting) == PNG_OPTION_INVALID)
+ png_error(ps->pwrite, "png option invalid");
+ }
+# endif
+
+ if (ppi != NULL)
+ *ppi = ps->piwrite = png_create_info_struct(ps->pwrite);
+ }
+
+ Catch_anonymous
+ return NULL;
+
+ return ps->pwrite;
+}
+
+/* Cleanup when finished reading (either due to error or in the success case).
+ * This routine exists even when there is no read support to make the code
+ * tidier (avoid a mass of ifdefs) and so easier to maintain.
+ */
+static void
+store_read_reset(png_store *ps)
+{
+# ifdef PNG_READ_SUPPORTED
+ if (ps->pread != NULL)
+ {
+ anon_context(ps);
+
+ Try
+ png_destroy_read_struct(&ps->pread, &ps->piread, NULL);
+
+ Catch_anonymous
+ {
+ /* error already output: continue */
+ }
+
+ ps->pread = NULL;
+ ps->piread = NULL;
+ }
+# endif
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ /* Always do this to be safe. */
+ store_pool_delete(ps, &ps->read_memory_pool);
+# endif
+
+ ps->current = NULL;
+ ps->next = NULL;
+ ps->readpos = 0;
+ ps->validated = 0;
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+store_read_set(png_store *ps, png_uint_32 id)
+{
+ png_store_file *pf = ps->saved;
+
+ while (pf != NULL)
+ {
+ if (pf->id == id)
+ {
+ ps->current = pf;
+ ps->next = NULL;
+ store_read_buffer_next(ps);
+ return;
+ }
+
+ pf = pf->next;
+ }
+
+ {
+ size_t pos;
+ char msg[FILE_NAME_SIZE+64];
+
+ pos = standard_name_from_id(msg, sizeof msg, 0, id);
+ pos = safecat(msg, sizeof msg, pos, ": file not found");
+ png_error(ps->pread, msg);
+ }
+}
+
+/* The main interface for reading a saved file - pass the id number of the file
+ * to retrieve. Ids must be unique or the earlier file will be hidden. The API
+ * returns a png_struct and, optionally, a png_info. Both of these will be
+ * destroyed by store_read_reset above.
+ */
+static png_structp
+set_store_for_read(png_store *ps, png_infopp ppi, png_uint_32 id,
+ const char *name)
+{
+ /* Set the name for png_error */
+ safecat(ps->test, sizeof ps->test, 0, name);
+
+ if (ps->pread != NULL)
+ png_error(ps->pread, "read store already in use");
+
+ store_read_reset(ps);
+
+ /* Both the create APIs can return NULL if used in their default mode
+ * (because there is no other way of handling an error because the jmp_buf
+ * by default is stored in png_struct and that has not been allocated!)
+ * However, given that store_error works correctly in these circumstances
+ * we don't ever expect NULL in this program.
+ */
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (!ps->speed)
+ ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
+ store_error, store_warning, &ps->read_memory_pool, store_malloc,
+ store_free);
+
+ else
+# endif
+ ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps, store_error,
+ store_warning);
+
+ if (ps->pread == NULL)
+ {
+ struct exception_context *the_exception_context = &ps->exception_context;
+
+ store_log(ps, NULL, "png_create_read_struct returned NULL (unexpected)",
+ 1 /*error*/);
+
+ Throw ps;
+ }
+
+# ifdef PNG_SET_OPTION_SUPPORTED
+ {
+ int opt;
+ for (opt=0; opt<ps->noptions; ++opt)
+ if (png_set_option(ps->pread, ps->options[opt].option,
+ ps->options[opt].setting) == PNG_OPTION_INVALID)
+ png_error(ps->pread, "png option invalid");
+ }
+# endif
+
+ store_read_set(ps, id);
+
+ if (ppi != NULL)
+ *ppi = ps->piread = png_create_info_struct(ps->pread);
+
+ return ps->pread;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* The overall cleanup of a store simply calls the above then removes all the
+ * saved files. This does not delete the store itself.
+ */
+static void
+store_delete(png_store *ps)
+{
+ store_write_reset(ps);
+ store_read_reset(ps);
+ store_freefile(&ps->saved);
+ store_image_free(ps, NULL);
+}
+
+/*********************** PNG FILE MODIFICATION ON READ ************************/
+/* Files may be modified on read. The following structure contains a complete
+ * png_store together with extra members to handle modification and a special
+ * read callback for libpng. To use this the 'modifications' field must be set
+ * to a list of png_modification structures that actually perform the
+ * modification, otherwise a png_modifier is functionally equivalent to a
+ * png_store. There is a special read function, set_modifier_for_read, which
+ * replaces set_store_for_read.
+ */
+typedef enum modifier_state
+{
+ modifier_start, /* Initial value */
+ modifier_signature, /* Have a signature */
+ modifier_IHDR /* Have an IHDR */
+} modifier_state;
+
+typedef struct CIE_color
+{
+ /* A single CIE tristimulus value, representing the unique response of a
+ * standard observer to a variety of light spectra. The observer recognizes
+ * all spectra that produce this response as the same color, therefore this
+ * is effectively a description of a color.
+ */
+ double X, Y, Z;
+} CIE_color;
+
+typedef struct color_encoding
+{
+ /* A description of an (R,G,B) encoding of color (as defined above); this
+ * includes the actual colors of the (R,G,B) triples (1,0,0), (0,1,0) and
+ * (0,0,1) plus an encoding value that is used to encode the linear
+ * components R, G and B to give the actual values R^gamma, G^gamma and
+ * B^gamma that are stored.
+ */
+ double gamma; /* Encoding (file) gamma of space */
+ CIE_color red, green, blue; /* End points */
+} color_encoding;
+
+#ifdef PNG_READ_SUPPORTED
+#if defined PNG_READ_TRANSFORMS_SUPPORTED && defined PNG_READ_cHRM_SUPPORTED
+static double
+chromaticity_x(CIE_color c)
+{
+ return c.X / (c.X + c.Y + c.Z);
+}
+
+static double
+chromaticity_y(CIE_color c)
+{
+ return c.Y / (c.X + c.Y + c.Z);
+}
+
+static CIE_color
+white_point(const color_encoding *encoding)
+{
+ CIE_color white;
+
+ white.X = encoding->red.X + encoding->green.X + encoding->blue.X;
+ white.Y = encoding->red.Y + encoding->green.Y + encoding->blue.Y;
+ white.Z = encoding->red.Z + encoding->green.Z + encoding->blue.Z;
+
+ return white;
+}
+#endif /* READ_TRANSFORMS && READ_cHRM */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+static void
+normalize_color_encoding(color_encoding *encoding)
+{
+ const double whiteY = encoding->red.Y + encoding->green.Y +
+ encoding->blue.Y;
+
+ if (whiteY != 1)
+ {
+ encoding->red.X /= whiteY;
+ encoding->red.Y /= whiteY;
+ encoding->red.Z /= whiteY;
+ encoding->green.X /= whiteY;
+ encoding->green.Y /= whiteY;
+ encoding->green.Z /= whiteY;
+ encoding->blue.X /= whiteY;
+ encoding->blue.Y /= whiteY;
+ encoding->blue.Z /= whiteY;
+ }
+}
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static size_t
+safecat_color_encoding(char *buffer, size_t bufsize, size_t pos,
+ const color_encoding *e, double encoding_gamma)
+{
+ if (e != 0)
+ {
+ if (encoding_gamma != 0)
+ pos = safecat(buffer, bufsize, pos, "(");
+ pos = safecat(buffer, bufsize, pos, "R(");
+ pos = safecatd(buffer, bufsize, pos, e->red.X, 4);
+ pos = safecat(buffer, bufsize, pos, ",");
+ pos = safecatd(buffer, bufsize, pos, e->red.Y, 4);
+ pos = safecat(buffer, bufsize, pos, ",");
+ pos = safecatd(buffer, bufsize, pos, e->red.Z, 4);
+ pos = safecat(buffer, bufsize, pos, "),G(");
+ pos = safecatd(buffer, bufsize, pos, e->green.X, 4);
+ pos = safecat(buffer, bufsize, pos, ",");
+ pos = safecatd(buffer, bufsize, pos, e->green.Y, 4);
+ pos = safecat(buffer, bufsize, pos, ",");
+ pos = safecatd(buffer, bufsize, pos, e->green.Z, 4);
+ pos = safecat(buffer, bufsize, pos, "),B(");
+ pos = safecatd(buffer, bufsize, pos, e->blue.X, 4);
+ pos = safecat(buffer, bufsize, pos, ",");
+ pos = safecatd(buffer, bufsize, pos, e->blue.Y, 4);
+ pos = safecat(buffer, bufsize, pos, ",");
+ pos = safecatd(buffer, bufsize, pos, e->blue.Z, 4);
+ pos = safecat(buffer, bufsize, pos, ")");
+ if (encoding_gamma != 0)
+ pos = safecat(buffer, bufsize, pos, ")");
+ }
+
+ if (encoding_gamma != 0)
+ {
+ pos = safecat(buffer, bufsize, pos, "^");
+ pos = safecatd(buffer, bufsize, pos, encoding_gamma, 5);
+ }
+
+ return pos;
+}
+#endif /* READ_TRANSFORMS */
+#endif /* PNG_READ_SUPPORTED */
+
+typedef struct png_modifier
+{
+ png_store this; /* I am a png_store */
+ struct png_modification *modifications; /* Changes to make */
+
+ modifier_state state; /* My state */
+
+ /* Information from IHDR: */
+ png_byte bit_depth; /* From IHDR */
+ png_byte colour_type; /* From IHDR */
+
+ /* While handling PLTE, IDAT and IEND these chunks may be pended to allow
+ * other chunks to be inserted.
+ */
+ png_uint_32 pending_len;
+ png_uint_32 pending_chunk;
+
+ /* Test values */
+ double *gammas;
+ unsigned int ngammas;
+ unsigned int ngamma_tests; /* Number of gamma tests to run*/
+ double current_gamma; /* 0 if not set */
+ const color_encoding *encodings;
+ unsigned int nencodings;
+ const color_encoding *current_encoding; /* If an encoding has been set */
+ unsigned int encoding_counter; /* For iteration */
+ int encoding_ignored; /* Something overwrote it */
+
+ /* Control variables used to iterate through possible encodings, the
+ * following must be set to 0 and tested by the function that uses the
+ * png_modifier because the modifier only sets it to 1 (true.)
+ */
+ unsigned int repeat :1; /* Repeat this transform test. */
+ unsigned int test_uses_encoding :1;
+
+ /* Lowest sbit to test (pre-1.7 libpng fails for sbit < 8) */
+ png_byte sbitlow;
+
+ /* Error control - these are the limits on errors accepted by the gamma tests
+ * below.
+ */
+ double maxout8; /* Maximum output value error */
+ double maxabs8; /* Absolute sample error 0..1 */
+ double maxcalc8; /* Absolute sample error 0..1 */
+ double maxpc8; /* Percentage sample error 0..100% */
+ double maxout16; /* Maximum output value error */
+ double maxabs16; /* Absolute sample error 0..1 */
+ double maxcalc16;/* Absolute sample error 0..1 */
+ double maxcalcG; /* Absolute sample error 0..1 */
+ double maxpc16; /* Percentage sample error 0..100% */
+
+ /* This is set by transforms that need to allow a higher limit, it is an
+ * internal check on pngvalid to ensure that the calculated error limits are
+ * not ridiculous; without this it is too easy to make a mistake in pngvalid
+ * that allows any value through.
+ */
+ double limit; /* limit on error values, normally 4E-3 */
+
+ /* Log limits - values above this are logged, but not necessarily
+ * warned.
+ */
+ double log8; /* Absolute error in 8 bits to log */
+ double log16; /* Absolute error in 16 bits to log */
+
+ /* Logged 8 and 16 bit errors ('output' values): */
+ double error_gray_2;
+ double error_gray_4;
+ double error_gray_8;
+ double error_gray_16;
+ double error_color_8;
+ double error_color_16;
+ double error_indexed;
+
+ /* Flags: */
+ /* Whether to call png_read_update_info, not png_read_start_image, and how
+ * many times to call it.
+ */
+ int use_update_info;
+
+ /* Whether or not to interlace. */
+ int interlace_type :9; /* int, but must store '1' */
+
+ /* Run the standard tests? */
+ unsigned int test_standard :1;
+
+ /* Run the odd-sized image and interlace read/write tests? */
+ unsigned int test_size :1;
+
+ /* Run tests on reading with a combination of transforms, */
+ unsigned int test_transform :1;
+ unsigned int test_tRNS :1; /* Includes tRNS images */
+
+ /* When to use the use_input_precision option, this controls the gamma
+ * validation code checks. If set any value that is within the transformed
+ * range input-.5 to input+.5 will be accepted, otherwise the value must be
+ * within the normal limits. It should not be necessary to set this; the
+ * result should always be exact within the permitted error limits.
+ */
+ unsigned int use_input_precision :1;
+ unsigned int use_input_precision_sbit :1;
+ unsigned int use_input_precision_16to8 :1;
+
+ /* If set assume that the calculation bit depth is set by the input
+ * precision, not the output precision.
+ */
+ unsigned int calculations_use_input_precision :1;
+
+ /* If set assume that the calculations are done in 16 bits even if the sample
+ * depth is 8 bits.
+ */
+ unsigned int assume_16_bit_calculations :1;
+
+ /* Which gamma tests to run: */
+ unsigned int test_gamma_threshold :1;
+ unsigned int test_gamma_transform :1; /* main tests */
+ unsigned int test_gamma_sbit :1;
+ unsigned int test_gamma_scale16 :1;
+ unsigned int test_gamma_background :1;
+ unsigned int test_gamma_alpha_mode :1;
+ unsigned int test_gamma_expand16 :1;
+ unsigned int test_exhaustive :1;
+
+ /* Whether or not to run the low-bit-depth grayscale tests. This fails on
+ * gamma images in some cases because of gross inaccuracies in the grayscale
+ * gamma handling for low bit depth.
+ */
+ unsigned int test_lbg :1;
+ unsigned int test_lbg_gamma_threshold :1;
+ unsigned int test_lbg_gamma_transform :1;
+ unsigned int test_lbg_gamma_sbit :1;
+ unsigned int test_lbg_gamma_composition :1;
+
+ unsigned int log :1; /* Log max error */
+
+ /* Buffer information, the buffer size limits the size of the chunks that can
+ * be modified - they must fit (including header and CRC) into the buffer!
+ */
+ size_t flush; /* Count of bytes to flush */
+ size_t buffer_count; /* Bytes in buffer */
+ size_t buffer_position; /* Position in buffer */
+ png_byte buffer[1024];
+} png_modifier;
+
+/* This returns true if the test should be stopped now because it has already
+ * failed and it is running silently.
+ */
+static int fail(png_modifier *pm)
+{
+ return !pm->log && !pm->this.verbose && (pm->this.nerrors > 0 ||
+ (pm->this.treat_warnings_as_errors && pm->this.nwarnings > 0));
+}
+
+static void
+modifier_init(png_modifier *pm)
+{
+ memset(pm, 0, sizeof *pm);
+ store_init(&pm->this);
+ pm->modifications = NULL;
+ pm->state = modifier_start;
+ pm->sbitlow = 1U;
+ pm->ngammas = 0;
+ pm->ngamma_tests = 0;
+ pm->gammas = 0;
+ pm->current_gamma = 0;
+ pm->encodings = 0;
+ pm->nencodings = 0;
+ pm->current_encoding = 0;
+ pm->encoding_counter = 0;
+ pm->encoding_ignored = 0;
+ pm->repeat = 0;
+ pm->test_uses_encoding = 0;
+ pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0;
+ pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0;
+ pm->maxcalcG = 0;
+ pm->limit = 4E-3;
+ pm->log8 = pm->log16 = 0; /* Means 'off' */
+ pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
+ pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
+ pm->error_indexed = 0;
+ pm->use_update_info = 0;
+ pm->interlace_type = PNG_INTERLACE_NONE;
+ pm->test_standard = 0;
+ pm->test_size = 0;
+ pm->test_transform = 0;
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ pm->test_tRNS = 1;
+# else
+ pm->test_tRNS = 0;
+# endif
+ pm->use_input_precision = 0;
+ pm->use_input_precision_sbit = 0;
+ pm->use_input_precision_16to8 = 0;
+ pm->calculations_use_input_precision = 0;
+ pm->assume_16_bit_calculations = 0;
+ pm->test_gamma_threshold = 0;
+ pm->test_gamma_transform = 0;
+ pm->test_gamma_sbit = 0;
+ pm->test_gamma_scale16 = 0;
+ pm->test_gamma_background = 0;
+ pm->test_gamma_alpha_mode = 0;
+ pm->test_gamma_expand16 = 0;
+ pm->test_lbg = 1;
+ pm->test_lbg_gamma_threshold = 1;
+ pm->test_lbg_gamma_transform = 1;
+ pm->test_lbg_gamma_sbit = 1;
+ pm->test_lbg_gamma_composition = 1;
+ pm->test_exhaustive = 0;
+ pm->log = 0;
+
+ /* Rely on the memset for all the other fields - there are no pointers */
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+
+/* This controls use of checks that explicitly know how libpng digitizes the
+ * samples in calculations; setting this circumvents simple error limit checking
+ * in the rgb_to_gray check, replacing it with an exact copy of the libpng 1.5
+ * algorithm.
+ */
+#define DIGITIZE PNG_LIBPNG_VER < 10700
+
+/* If pm->calculations_use_input_precision is set then operations will happen
+ * with the precision of the input, not the precision of the output depth.
+ *
+ * If pm->assume_16_bit_calculations is set then even 8 bit calculations use 16
+ * bit precision. This only affects those of the following limits that pertain
+ * to a calculation - not a digitization operation - unless the following API is
+ * called directly.
+ */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+#if DIGITIZE
+static double digitize(double value, int depth, int do_round)
+{
+ /* 'value' is in the range 0 to 1, the result is the same value rounded to a
+ * multiple of the digitization factor - 8 or 16 bits depending on both the
+ * sample depth and the 'assume' setting. Digitization is normally by
+ * rounding and 'do_round' should be 1, if it is 0 the digitized value will
+ * be truncated.
+ */
+ const unsigned int digitization_factor = (1U << depth) -1;
+
+ /* Limiting the range is done as a convenience to the caller - it's easier to
+ * do it once here than every time at the call site.
+ */
+ if (value <= 0)
+ value = 0;
+
+ else if (value >= 1)
+ value = 1;
+
+ value *= digitization_factor;
+ if (do_round) value += .5;
+ return floor(value)/digitization_factor;
+}
+#endif
+#endif /* RGB_TO_GRAY */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+static double abserr(const png_modifier *pm, int in_depth, int out_depth)
+{
+ /* Absolute error permitted in linear values - affected by the bit depth of
+ * the calculations.
+ */
+ if (pm->assume_16_bit_calculations ||
+ (pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+ return pm->maxabs16;
+ else
+ return pm->maxabs8;
+}
+
+static double calcerr(const png_modifier *pm, int in_depth, int out_depth)
+{
+ /* Error in the linear composition arithmetic - only relevant when
+ * composition actually happens (0 < alpha < 1).
+ */
+ if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+ return pm->maxcalc16;
+ else if (pm->assume_16_bit_calculations)
+ return pm->maxcalcG;
+ else
+ return pm->maxcalc8;
+}
+
+static double pcerr(const png_modifier *pm, int in_depth, int out_depth)
+{
+ /* Percentage error permitted in the linear values. Note that the specified
+ * value is a percentage but this routine returns a simple number.
+ */
+ if (pm->assume_16_bit_calculations ||
+ (pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+ return pm->maxpc16 * .01;
+ else
+ return pm->maxpc8 * .01;
+}
+
+/* Output error - the error in the encoded value. This is determined by the
+ * digitization of the output so can be +/-0.5 in the actual output value. In
+ * the expand_16 case with the current code in libpng the expand happens after
+ * all the calculations are done in 8 bit arithmetic, so even though the output
+ * depth is 16 the output error is determined by the 8 bit calculation.
+ *
+ * This limit is not determined by the bit depth of internal calculations.
+ *
+ * The specified parameter does *not* include the base .5 digitization error but
+ * it is added here.
+ */
+static double outerr(const png_modifier *pm, int in_depth, int out_depth)
+{
+ /* There is a serious error in the 2 and 4 bit grayscale transform because
+ * the gamma table value (8 bits) is simply shifted, not rounded, so the
+ * error in 4 bit grayscale gamma is up to the value below. This is a hack
+ * to allow pngvalid to succeed:
+ *
+ * TODO: fix this in libpng
+ */
+ if (out_depth == 2)
+ return .73182-.5;
+
+ if (out_depth == 4)
+ return .90644-.5;
+
+ if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+ return pm->maxout16;
+
+ /* This is the case where the value was calculated at 8-bit precision then
+ * scaled to 16 bits.
+ */
+ else if (out_depth == 16)
+ return pm->maxout8 * 257;
+
+ else
+ return pm->maxout8;
+}
+
+/* This does the same thing as the above however it returns the value to log,
+ * rather than raising a warning. This is useful for debugging to track down
+ * exactly what set of parameters cause high error values.
+ */
+static double outlog(const png_modifier *pm, int in_depth, int out_depth)
+{
+ /* The command line parameters are either 8 bit (0..255) or 16 bit (0..65535)
+ * and so must be adjusted for low bit depth grayscale:
+ */
+ if (out_depth <= 8)
+ {
+ if (pm->log8 == 0) /* switched off */
+ return 256;
+
+ if (out_depth < 8)
+ return pm->log8 / 255 * ((1<<out_depth)-1);
+
+ return pm->log8;
+ }
+
+ if ((pm->calculations_use_input_precision ? in_depth : out_depth) == 16)
+ {
+ if (pm->log16 == 0)
+ return 65536;
+
+ return pm->log16;
+ }
+
+ /* This is the case where the value was calculated at 8-bit precision then
+ * scaled to 16 bits.
+ */
+ if (pm->log8 == 0)
+ return 65536;
+
+ return pm->log8 * 257;
+}
+
+/* This complements the above by providing the appropriate quantization for the
+ * final value. Normally this would just be quantization to an integral value,
+ * but in the 8 bit calculation case it's actually quantization to a multiple of
+ * 257!
+ */
+static int output_quantization_factor(const png_modifier *pm, int in_depth,
+ int out_depth)
+{
+ if (out_depth == 16 && in_depth != 16 &&
+ pm->calculations_use_input_precision)
+ return 257;
+ else
+ return 1;
+}
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+
+/* One modification structure must be provided for each chunk to be modified (in
+ * fact more than one can be provided if multiple separate changes are desired
+ * for a single chunk.) Modifications include adding a new chunk when a
+ * suitable chunk does not exist.
+ *
+ * The caller of modify_fn will reset the CRC of the chunk and record 'modified'
+ * or 'added' as appropriate if the modify_fn returns 1 (true). If the
+ * modify_fn is NULL the chunk is simply removed.
+ */
+typedef struct png_modification
+{
+ struct png_modification *next;
+ png_uint_32 chunk;
+
+ /* If the following is NULL all matching chunks will be removed: */
+ int (*modify_fn)(struct png_modifier *pm,
+ struct png_modification *me, int add);
+
+ /* If the following is set to PLTE, IDAT or IEND and the chunk has not been
+ * found and modified (and there is a modify_fn) the modify_fn will be called
+ * to add the chunk before the relevant chunk.
+ */
+ png_uint_32 add;
+ unsigned int modified :1; /* Chunk was modified */
+ unsigned int added :1; /* Chunk was added */
+ unsigned int removed :1; /* Chunk was removed */
+} png_modification;
+
+static void
+modification_reset(png_modification *pmm)
+{
+ if (pmm != NULL)
+ {
+ pmm->modified = 0;
+ pmm->added = 0;
+ pmm->removed = 0;
+ modification_reset(pmm->next);
+ }
+}
+
+static void
+modification_init(png_modification *pmm)
+{
+ memset(pmm, 0, sizeof *pmm);
+ pmm->next = NULL;
+ pmm->chunk = 0;
+ pmm->modify_fn = NULL;
+ pmm->add = 0;
+ modification_reset(pmm);
+}
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+static void
+modifier_current_encoding(const png_modifier *pm, color_encoding *ce)
+{
+ if (pm->current_encoding != 0)
+ *ce = *pm->current_encoding;
+
+ else
+ memset(ce, 0, sizeof *ce);
+
+ ce->gamma = pm->current_gamma;
+}
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+static size_t
+safecat_current_encoding(char *buffer, size_t bufsize, size_t pos,
+ const png_modifier *pm)
+{
+ pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding,
+ pm->current_gamma);
+
+ if (pm->encoding_ignored)
+ pos = safecat(buffer, bufsize, pos, "[overridden]");
+
+ return pos;
+}
+#endif
+
+/* Iterate through the usefully testable color encodings. An encoding is one
+ * of:
+ *
+ * 1) Nothing (no color space, no gamma).
+ * 2) Just a gamma value from the gamma array (including 1.0)
+ * 3) A color space from the encodings array with the corresponding gamma.
+ * 4) The same, but with gamma 1.0 (only really useful with 16 bit calculations)
+ *
+ * The iterator selects these in turn, the randomizer selects one at random,
+ * which is used depends on the setting of the 'test_exhaustive' flag. Notice
+ * that this function changes the colour space encoding so it must only be
+ * called on completion of the previous test. This is what 'modifier_reset'
+ * does, below.
+ *
+ * After the function has been called the 'repeat' flag will still be set; the
+ * caller of modifier_reset must reset it at the start of each run of the test!
+ */
+static unsigned int
+modifier_total_encodings(const png_modifier *pm)
+{
+ return 1 + /* (1) nothing */
+ pm->ngammas + /* (2) gamma values to test */
+ pm->nencodings + /* (3) total number of encodings */
+ /* The following test only works after the first time through the
+ * png_modifier code because 'bit_depth' is set when the IHDR is read.
+ * modifier_reset, below, preserves the setting until after it has called
+ * the iterate function (also below.)
+ *
+ * For this reason do not rely on this function outside a call to
+ * modifier_reset.
+ */
+ ((pm->bit_depth == 16 || pm->assume_16_bit_calculations) ?
+ pm->nencodings : 0); /* (4) encodings with gamma == 1.0 */
+}
+
+static void
+modifier_encoding_iterate(png_modifier *pm)
+{
+ if (!pm->repeat && /* Else something needs the current encoding again. */
+ pm->test_uses_encoding) /* Some transform is encoding dependent */
+ {
+ if (pm->test_exhaustive)
+ {
+ if (++pm->encoding_counter >= modifier_total_encodings(pm))
+ pm->encoding_counter = 0; /* This will stop the repeat */
+ }
+
+ else
+ {
+ /* Not exhaustive - choose an encoding at random; generate a number in
+ * the range 1..(max-1), so the result is always non-zero:
+ */
+ if (pm->encoding_counter == 0)
+ pm->encoding_counter = random_mod(modifier_total_encodings(pm)-1)+1;
+ else
+ pm->encoding_counter = 0;
+ }
+
+ if (pm->encoding_counter > 0)
+ pm->repeat = 1;
+ }
+
+ else if (!pm->repeat)
+ pm->encoding_counter = 0;
+}
+
+static void
+modifier_reset(png_modifier *pm)
+{
+ store_read_reset(&pm->this);
+ pm->limit = 4E-3;
+ pm->pending_len = pm->pending_chunk = 0;
+ pm->flush = pm->buffer_count = pm->buffer_position = 0;
+ pm->modifications = NULL;
+ pm->state = modifier_start;
+ modifier_encoding_iterate(pm);
+ /* The following must be set in the next run. In particular
+ * test_uses_encodings must be set in the _ini function of each transform
+ * that looks at the encodings. (Not the 'add' function!)
+ */
+ pm->test_uses_encoding = 0;
+ pm->current_gamma = 0;
+ pm->current_encoding = 0;
+ pm->encoding_ignored = 0;
+ /* These only become value after IHDR is read: */
+ pm->bit_depth = pm->colour_type = 0;
+}
+
+/* The following must be called before anything else to get the encoding set up
+ * on the modifier. In particular it must be called before the transform init
+ * functions are called.
+ */
+static void
+modifier_set_encoding(png_modifier *pm)
+{
+ /* Set the encoding to the one specified by the current encoding counter,
+ * first clear out all the settings - this corresponds to an encoding_counter
+ * of 0.
+ */
+ pm->current_gamma = 0;
+ pm->current_encoding = 0;
+ pm->encoding_ignored = 0; /* not ignored yet - happens in _ini functions. */
+
+ /* Now, if required, set the gamma and encoding fields. */
+ if (pm->encoding_counter > 0)
+ {
+ /* The gammas[] array is an array of screen gammas, not encoding gammas,
+ * so we need the inverse:
+ */
+ if (pm->encoding_counter <= pm->ngammas)
+ pm->current_gamma = 1/pm->gammas[pm->encoding_counter-1];
+
+ else
+ {
+ unsigned int i = pm->encoding_counter - pm->ngammas;
+
+ if (i >= pm->nencodings)
+ {
+ i %= pm->nencodings;
+ pm->current_gamma = 1; /* Linear, only in the 16 bit case */
+ }
+
+ else
+ pm->current_gamma = pm->encodings[i].gamma;
+
+ pm->current_encoding = pm->encodings + i;
+ }
+ }
+}
+
+/* Enquiry functions to find out what is set. Notice that there is an implicit
+ * assumption below that the first encoding in the list is the one for sRGB.
+ */
+static int
+modifier_color_encoding_is_sRGB(const png_modifier *pm)
+{
+ return pm->current_encoding != 0 && pm->current_encoding == pm->encodings &&
+ pm->current_encoding->gamma == pm->current_gamma;
+}
+
+static int
+modifier_color_encoding_is_set(const png_modifier *pm)
+{
+ return pm->current_gamma != 0;
+}
+
+/* Convenience macros. */
+#define CHUNK(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+#define CHUNK_IHDR CHUNK(73,72,68,82)
+#define CHUNK_PLTE CHUNK(80,76,84,69)
+#define CHUNK_IDAT CHUNK(73,68,65,84)
+#define CHUNK_IEND CHUNK(73,69,78,68)
+#define CHUNK_cHRM CHUNK(99,72,82,77)
+#define CHUNK_gAMA CHUNK(103,65,77,65)
+#define CHUNK_sBIT CHUNK(115,66,73,84)
+#define CHUNK_sRGB CHUNK(115,82,71,66)
+
+/* The guts of modification are performed during a read. */
+static void
+modifier_crc(png_bytep buffer)
+{
+ /* Recalculate the chunk CRC - a complete chunk must be in
+ * the buffer, at the start.
+ */
+ uInt datalen = png_get_uint_32(buffer);
+ uLong crc = crc32(0, buffer+4, datalen+4);
+ /* The cast to png_uint_32 is safe because a crc32 is always a 32 bit value.
+ */
+ png_save_uint_32(buffer+datalen+8, (png_uint_32)crc);
+}
+
+static void
+modifier_setbuffer(png_modifier *pm)
+{
+ modifier_crc(pm->buffer);
+ pm->buffer_count = png_get_uint_32(pm->buffer)+12;
+ pm->buffer_position = 0;
+}
+
+/* Separate the callback into the actual implementation (which is passed the
+ * png_modifier explicitly) and the callback, which gets the modifier from the
+ * png_struct.
+ */
+static void
+modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st)
+{
+ while (st > 0)
+ {
+ size_t cb;
+ png_uint_32 len, chunk;
+ png_modification *mod;
+
+ if (pm->buffer_position >= pm->buffer_count) switch (pm->state)
+ {
+ static png_byte sign[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ case modifier_start:
+ store_read_imp(&pm->this, pm->buffer, 8); /* size of signature. */
+ pm->buffer_count = 8;
+ pm->buffer_position = 0;
+
+ if (memcmp(pm->buffer, sign, 8) != 0)
+ png_error(pm->this.pread, "invalid PNG file signature");
+ pm->state = modifier_signature;
+ break;
+
+ case modifier_signature:
+ store_read_imp(&pm->this, pm->buffer, 13+12); /* size of IHDR */
+ pm->buffer_count = 13+12;
+ pm->buffer_position = 0;
+
+ if (png_get_uint_32(pm->buffer) != 13 ||
+ png_get_uint_32(pm->buffer+4) != CHUNK_IHDR)
+ png_error(pm->this.pread, "invalid IHDR");
+
+ /* Check the list of modifiers for modifications to the IHDR. */
+ mod = pm->modifications;
+ while (mod != NULL)
+ {
+ if (mod->chunk == CHUNK_IHDR && mod->modify_fn &&
+ (*mod->modify_fn)(pm, mod, 0))
+ {
+ mod->modified = 1;
+ modifier_setbuffer(pm);
+ }
+
+ /* Ignore removal or add if IHDR! */
+ mod = mod->next;
+ }
+
+ /* Cache information from the IHDR (the modified one.) */
+ pm->bit_depth = pm->buffer[8+8];
+ pm->colour_type = pm->buffer[8+8+1];
+
+ pm->state = modifier_IHDR;
+ pm->flush = 0;
+ break;
+
+ case modifier_IHDR:
+ default:
+ /* Read a new chunk and process it until we see PLTE, IDAT or
+ * IEND. 'flush' indicates that there is still some data to
+ * output from the preceding chunk.
+ */
+ if ((cb = pm->flush) > 0)
+ {
+ if (cb > st) cb = st;
+ pm->flush -= cb;
+ store_read_imp(&pm->this, pb, cb);
+ pb += cb;
+ st -= cb;
+ if (st == 0) return;
+ }
+
+ /* No more bytes to flush, read a header, or handle a pending
+ * chunk.
+ */
+ if (pm->pending_chunk != 0)
+ {
+ png_save_uint_32(pm->buffer, pm->pending_len);
+ png_save_uint_32(pm->buffer+4, pm->pending_chunk);
+ pm->pending_len = 0;
+ pm->pending_chunk = 0;
+ }
+ else
+ store_read_imp(&pm->this, pm->buffer, 8);
+
+ pm->buffer_count = 8;
+ pm->buffer_position = 0;
+
+ /* Check for something to modify or a terminator chunk. */
+ len = png_get_uint_32(pm->buffer);
+ chunk = png_get_uint_32(pm->buffer+4);
+
+ /* Terminators first, they may have to be delayed for added
+ * chunks
+ */
+ if (chunk == CHUNK_PLTE || chunk == CHUNK_IDAT ||
+ chunk == CHUNK_IEND)
+ {
+ mod = pm->modifications;
+
+ while (mod != NULL)
+ {
+ if ((mod->add == chunk ||
+ (mod->add == CHUNK_PLTE && chunk == CHUNK_IDAT)) &&
+ mod->modify_fn != NULL && !mod->modified && !mod->added)
+ {
+ /* Regardless of what the modify function does do not run
+ * this again.
+ */
+ mod->added = 1;
+
+ if ((*mod->modify_fn)(pm, mod, 1 /*add*/))
+ {
+ /* Reset the CRC on a new chunk */
+ if (pm->buffer_count > 0)
+ modifier_setbuffer(pm);
+
+ else
+ {
+ pm->buffer_position = 0;
+ mod->removed = 1;
+ }
+
+ /* The buffer has been filled with something (we assume)
+ * so output this. Pend the current chunk.
+ */
+ pm->pending_len = len;
+ pm->pending_chunk = chunk;
+ break; /* out of while */
+ }
+ }
+
+ mod = mod->next;
+ }
+
+ /* Don't do any further processing if the buffer was modified -
+ * otherwise the code will end up modifying a chunk that was
+ * just added.
+ */
+ if (mod != NULL)
+ break; /* out of switch */
+ }
+
+ /* If we get to here then this chunk may need to be modified. To
+ * do this it must be less than 1024 bytes in total size, otherwise
+ * it just gets flushed.
+ */
+ if (len+12 <= sizeof pm->buffer)
+ {
+ store_read_imp(&pm->this, pm->buffer+pm->buffer_count,
+ len+12-pm->buffer_count);
+ pm->buffer_count = len+12;
+
+ /* Check for a modification, else leave it be. */
+ mod = pm->modifications;
+ while (mod != NULL)
+ {
+ if (mod->chunk == chunk)
+ {
+ if (mod->modify_fn == NULL)
+ {
+ /* Remove this chunk */
+ pm->buffer_count = pm->buffer_position = 0;
+ mod->removed = 1;
+ break; /* Terminate the while loop */
+ }
+
+ else if ((*mod->modify_fn)(pm, mod, 0))
+ {
+ mod->modified = 1;
+ /* The chunk may have been removed: */
+ if (pm->buffer_count == 0)
+ {
+ pm->buffer_position = 0;
+ break;
+ }
+ modifier_setbuffer(pm);
+ }
+ }
+
+ mod = mod->next;
+ }
+ }
+
+ else
+ pm->flush = len+12 - pm->buffer_count; /* data + crc */
+
+ /* Take the data from the buffer (if there is any). */
+ break;
+ }
+
+ /* Here to read from the modifier buffer (not directly from
+ * the store, as in the flush case above.)
+ */
+ cb = pm->buffer_count - pm->buffer_position;
+
+ if (cb > st)
+ cb = st;
+
+ memcpy(pb, pm->buffer + pm->buffer_position, cb);
+ st -= cb;
+ pb += cb;
+ pm->buffer_position += cb;
+ }
+}
+
+/* The callback: */
+static void PNGCBAPI
+modifier_read(png_structp ppIn, png_bytep pb, png_size_t st)
+{
+ png_const_structp pp = ppIn;
+ png_modifier *pm = voidcast(png_modifier*, png_get_io_ptr(pp));
+
+ if (pm == NULL || pm->this.pread != pp)
+ png_error(pp, "bad modifier_read call");
+
+ modifier_read_imp(pm, pb, st);
+}
+
+/* Like store_progressive_read but the data is getting changed as we go so we
+ * need a local buffer.
+ */
+static void
+modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi)
+{
+ if (pm->this.pread != pp || pm->this.current == NULL ||
+ pm->this.next == NULL)
+ png_error(pp, "store state damaged (progressive)");
+
+ /* This is another Horowitz and Hill random noise generator. In this case
+ * the aim is to stress the progressive reader with truly horrible variable
+ * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers
+ * is generated. We could probably just count from 1 to 32767 and get as
+ * good a result.
+ */
+ for (;;)
+ {
+ static png_uint_32 noise = 1;
+ png_size_t cb, cbAvail;
+ png_byte buffer[512];
+
+ /* Generate 15 more bits of stuff: */
+ noise = (noise << 9) | ((noise ^ (noise >> (9-5))) & 0x1ff);
+ cb = noise & 0x1ff;
+
+ /* Check that this number of bytes are available (in the current buffer.)
+ * (This doesn't quite work - the modifier might delete a chunk; unlikely
+ * but possible, it doesn't happen at present because the modifier only
+ * adds chunks to standard images.)
+ */
+ cbAvail = store_read_buffer_avail(&pm->this);
+ if (pm->buffer_count > pm->buffer_position)
+ cbAvail += pm->buffer_count - pm->buffer_position;
+
+ if (cb > cbAvail)
+ {
+ /* Check for EOF: */
+ if (cbAvail == 0)
+ break;
+
+ cb = cbAvail;
+ }
+
+ modifier_read_imp(pm, buffer, cb);
+ png_process_data(pp, pi, buffer, cb);
+ }
+
+ /* Check the invariants at the end (if this fails it's a problem in this
+ * file!)
+ */
+ if (pm->buffer_count > pm->buffer_position ||
+ pm->this.next != &pm->this.current->data ||
+ pm->this.readpos < pm->this.current->datacount)
+ png_error(pp, "progressive read implementation error");
+}
+
+/* Set up a modifier. */
+static png_structp
+set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
+ const char *name)
+{
+ /* Do this first so that the modifier fields are cleared even if an error
+ * happens allocating the png_struct. No allocation is done here so no
+ * cleanup is required.
+ */
+ pm->state = modifier_start;
+ pm->bit_depth = 0;
+ pm->colour_type = 255;
+
+ pm->pending_len = 0;
+ pm->pending_chunk = 0;
+ pm->flush = 0;
+ pm->buffer_count = 0;
+ pm->buffer_position = 0;
+
+ return set_store_for_read(&pm->this, ppi, id, name);
+}
+
+
+/******************************** MODIFICATIONS *******************************/
+/* Standard modifications to add chunks. These do not require the _SUPPORTED
+ * macros because the chunks can be there regardless of whether this specific
+ * libpng supports them.
+ */
+typedef struct gama_modification
+{
+ png_modification this;
+ png_fixed_point gamma;
+} gama_modification;
+
+static int
+gama_modify(png_modifier *pm, png_modification *me, int add)
+{
+ UNUSED(add)
+ /* This simply dumps the given gamma value into the buffer. */
+ png_save_uint_32(pm->buffer, 4);
+ png_save_uint_32(pm->buffer+4, CHUNK_gAMA);
+ png_save_uint_32(pm->buffer+8, ((gama_modification*)me)->gamma);
+ return 1;
+}
+
+static void
+gama_modification_init(gama_modification *me, png_modifier *pm, double gammad)
+{
+ double g;
+
+ modification_init(&me->this);
+ me->this.chunk = CHUNK_gAMA;
+ me->this.modify_fn = gama_modify;
+ me->this.add = CHUNK_PLTE;
+ g = fix(gammad);
+ me->gamma = (png_fixed_point)g;
+ me->this.next = pm->modifications;
+ pm->modifications = &me->this;
+}
+
+typedef struct chrm_modification
+{
+ png_modification this;
+ const color_encoding *encoding;
+ png_fixed_point wx, wy, rx, ry, gx, gy, bx, by;
+} chrm_modification;
+
+static int
+chrm_modify(png_modifier *pm, png_modification *me, int add)
+{
+ UNUSED(add)
+ /* As with gAMA this just adds the required cHRM chunk to the buffer. */
+ png_save_uint_32(pm->buffer , 32);
+ png_save_uint_32(pm->buffer+ 4, CHUNK_cHRM);
+ png_save_uint_32(pm->buffer+ 8, ((chrm_modification*)me)->wx);
+ png_save_uint_32(pm->buffer+12, ((chrm_modification*)me)->wy);
+ png_save_uint_32(pm->buffer+16, ((chrm_modification*)me)->rx);
+ png_save_uint_32(pm->buffer+20, ((chrm_modification*)me)->ry);
+ png_save_uint_32(pm->buffer+24, ((chrm_modification*)me)->gx);
+ png_save_uint_32(pm->buffer+28, ((chrm_modification*)me)->gy);
+ png_save_uint_32(pm->buffer+32, ((chrm_modification*)me)->bx);
+ png_save_uint_32(pm->buffer+36, ((chrm_modification*)me)->by);
+ return 1;
+}
+
+static void
+chrm_modification_init(chrm_modification *me, png_modifier *pm,
+ const color_encoding *encoding)
+{
+ CIE_color white = white_point(encoding);
+
+ /* Original end points: */
+ me->encoding = encoding;
+
+ /* Chromaticities (in fixed point): */
+ me->wx = fix(chromaticity_x(white));
+ me->wy = fix(chromaticity_y(white));
+
+ me->rx = fix(chromaticity_x(encoding->red));
+ me->ry = fix(chromaticity_y(encoding->red));
+ me->gx = fix(chromaticity_x(encoding->green));
+ me->gy = fix(chromaticity_y(encoding->green));
+ me->bx = fix(chromaticity_x(encoding->blue));
+ me->by = fix(chromaticity_y(encoding->blue));
+
+ modification_init(&me->this);
+ me->this.chunk = CHUNK_cHRM;
+ me->this.modify_fn = chrm_modify;
+ me->this.add = CHUNK_PLTE;
+ me->this.next = pm->modifications;
+ pm->modifications = &me->this;
+}
+
+typedef struct srgb_modification
+{
+ png_modification this;
+ png_byte intent;
+} srgb_modification;
+
+static int
+srgb_modify(png_modifier *pm, png_modification *me, int add)
+{
+ UNUSED(add)
+ /* As above, ignore add and just make a new chunk */
+ png_save_uint_32(pm->buffer, 1);
+ png_save_uint_32(pm->buffer+4, CHUNK_sRGB);
+ pm->buffer[8] = ((srgb_modification*)me)->intent;
+ return 1;
+}
+
+static void
+srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent)
+{
+ modification_init(&me->this);
+ me->this.chunk = CHUNK_sBIT;
+
+ if (intent <= 3) /* if valid, else *delete* sRGB chunks */
+ {
+ me->this.modify_fn = srgb_modify;
+ me->this.add = CHUNK_PLTE;
+ me->intent = intent;
+ }
+
+ else
+ {
+ me->this.modify_fn = 0;
+ me->this.add = 0;
+ me->intent = 0;
+ }
+
+ me->this.next = pm->modifications;
+ pm->modifications = &me->this;
+}
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+typedef struct sbit_modification
+{
+ png_modification this;
+ png_byte sbit;
+} sbit_modification;
+
+static int
+sbit_modify(png_modifier *pm, png_modification *me, int add)
+{
+ png_byte sbit = ((sbit_modification*)me)->sbit;
+ if (pm->bit_depth > sbit)
+ {
+ int cb = 0;
+ switch (pm->colour_type)
+ {
+ case 0:
+ cb = 1;
+ break;
+
+ case 2:
+ case 3:
+ cb = 3;
+ break;
+
+ case 4:
+ cb = 2;
+ break;
+
+ case 6:
+ cb = 4;
+ break;
+
+ default:
+ png_error(pm->this.pread,
+ "unexpected colour type in sBIT modification");
+ }
+
+ png_save_uint_32(pm->buffer, cb);
+ png_save_uint_32(pm->buffer+4, CHUNK_sBIT);
+
+ while (cb > 0)
+ (pm->buffer+8)[--cb] = sbit;
+
+ return 1;
+ }
+ else if (!add)
+ {
+ /* Remove the sBIT chunk */
+ pm->buffer_count = pm->buffer_position = 0;
+ return 1;
+ }
+ else
+ return 0; /* do nothing */
+}
+
+static void
+sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit)
+{
+ modification_init(&me->this);
+ me->this.chunk = CHUNK_sBIT;
+ me->this.modify_fn = sbit_modify;
+ me->this.add = CHUNK_PLTE;
+ me->sbit = sbit;
+ me->this.next = pm->modifications;
+ pm->modifications = &me->this;
+}
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/***************************** STANDARD PNG FILES *****************************/
+/* Standard files - write and save standard files. */
+/* There are two basic forms of standard images. Those which attempt to have
+ * all the possible pixel values (not possible for 16bpp images, but a range of
+ * values are produced) and those which have a range of image sizes. The former
+ * are used for testing transforms, in particular gamma correction and bit
+ * reduction and increase. The latter are reserved for testing the behavior of
+ * libpng with respect to 'odd' image sizes - particularly small images where
+ * rows become 1 byte and interlace passes disappear.
+ *
+ * The first, most useful, set are the 'transform' images, the second set of
+ * small images are the 'size' images.
+ *
+ * The transform files are constructed with rows which fit into a 1024 byte row
+ * buffer. This makes allocation easier below. Further regardless of the file
+ * format every row has 128 pixels (giving 1024 bytes for 64bpp formats).
+ *
+ * Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed
+ * and with an ID derived from the colour type, bit depth and interlace type
+ * as above (FILEID). The width (128) and height (variable) are not stored in
+ * the FILEID - instead the fields are set to 0, indicating a transform file.
+ *
+ * The size files ar constructed with rows a maximum of 128 bytes wide, allowing
+ * a maximum width of 16 pixels (for the 64bpp case.) They also have a maximum
+ * height of 16 rows. The width and height are stored in the FILEID and, being
+ * non-zero, indicate a size file.
+ *
+ * Because the PNG filter code is typically the largest CPU consumer within
+ * libpng itself there is a tendency to attempt to optimize it. This results in
+ * special case code which needs to be validated. To cause this to happen the
+ * 'size' images are made to use each possible filter, in so far as this is
+ * possible for smaller images.
+ *
+ * For palette image (colour type 3) multiple transform images are stored with
+ * the same bit depth to allow testing of more colour combinations -
+ * particularly important for testing the gamma code because libpng uses a
+ * different code path for palette images. For size images a single palette is
+ * used.
+ */
+
+/* Make a 'standard' palette. Because there are only 256 entries in a palette
+ * (maximum) this actually makes a random palette in the hope that enough tests
+ * will catch enough errors. (Note that the same palette isn't produced every
+ * time for the same test - it depends on what previous tests have been run -
+ * but a given set of arguments to pngvalid will always produce the same palette
+ * at the same test! This is why pseudo-random number generators are useful for
+ * testing.)
+ *
+ * The store must be open for write when this is called, otherwise an internal
+ * error will occur. This routine contains its own magic number seed, so the
+ * palettes generated don't change if there are intervening errors (changing the
+ * calls to the store_mark seed.)
+ */
+static store_palette_entry *
+make_standard_palette(png_store* ps, int npalette, int do_tRNS)
+{
+ static png_uint_32 palette_seed[2] = { 0x87654321, 9 };
+
+ int i = 0;
+ png_byte values[256][4];
+
+ /* Always put in black and white plus the six primary and secondary colors.
+ */
+ for (; i<8; ++i)
+ {
+ values[i][1] = (png_byte)((i&1) ? 255U : 0U);
+ values[i][2] = (png_byte)((i&2) ? 255U : 0U);
+ values[i][3] = (png_byte)((i&4) ? 255U : 0U);
+ }
+
+ /* Then add 62 grays (one quarter of the remaining 256 slots). */
+ {
+ int j = 0;
+ png_byte random_bytes[4];
+ png_byte need[256];
+
+ need[0] = 0; /*got black*/
+ memset(need+1, 1, (sizeof need)-2); /*need these*/
+ need[255] = 0; /*but not white*/
+
+ while (i<70)
+ {
+ png_byte b;
+
+ if (j==0)
+ {
+ make_four_random_bytes(palette_seed, random_bytes);
+ j = 4;
+ }
+
+ b = random_bytes[--j];
+ if (need[b])
+ {
+ values[i][1] = b;
+ values[i][2] = b;
+ values[i++][3] = b;
+ }
+ }
+ }
+
+ /* Finally add 192 colors at random - don't worry about matches to things we
+ * already have, chance is less than 1/65536. Don't worry about grays,
+ * chance is the same, so we get a duplicate or extra gray less than 1 time
+ * in 170.
+ */
+ for (; i<256; ++i)
+ make_four_random_bytes(palette_seed, values[i]);
+
+ /* Fill in the alpha values in the first byte. Just use all possible values
+ * (0..255) in an apparently random order:
+ */
+ {
+ store_palette_entry *palette;
+ png_byte selector[4];
+
+ make_four_random_bytes(palette_seed, selector);
+
+ if (do_tRNS)
+ for (i=0; i<256; ++i)
+ values[i][0] = (png_byte)(i ^ selector[0]);
+
+ else
+ for (i=0; i<256; ++i)
+ values[i][0] = 255; /* no transparency/tRNS chunk */
+
+ /* 'values' contains 256 ARGB values, but we only need 'npalette'.
+ * 'npalette' will always be a power of 2: 2, 4, 16 or 256. In the low
+ * bit depth cases select colors at random, else it is difficult to have
+ * a set of low bit depth palette test with any chance of a reasonable
+ * range of colors. Do this by randomly permuting values into the low
+ * 'npalette' entries using an XOR mask generated here. This also
+ * permutes the npalette == 256 case in a potentially useful way (there is
+ * no relationship between palette index and the color value therein!)
+ */
+ palette = store_write_palette(ps, npalette);
+
+ for (i=0; i<npalette; ++i)
+ {
+ palette[i].alpha = values[i ^ selector[1]][0];
+ palette[i].red = values[i ^ selector[1]][1];
+ palette[i].green = values[i ^ selector[1]][2];
+ palette[i].blue = values[i ^ selector[1]][3];
+ }
+
+ return palette;
+ }
+}
+
+/* Initialize a standard palette on a write stream. The 'do_tRNS' argument
+ * indicates whether or not to also set the tRNS chunk.
+ */
+/* TODO: the png_structp here can probably be 'const' in the future */
+static void
+init_standard_palette(png_store *ps, png_structp pp, png_infop pi, int npalette,
+ int do_tRNS)
+{
+ store_palette_entry *ppal = make_standard_palette(ps, npalette, do_tRNS);
+
+ {
+ int i;
+ png_color palette[256];
+
+ /* Set all entries to detect overread errors. */
+ for (i=0; i<npalette; ++i)
+ {
+ palette[i].red = ppal[i].red;
+ palette[i].green = ppal[i].green;
+ palette[i].blue = ppal[i].blue;
+ }
+
+ /* Just in case fill in the rest with detectable values: */
+ for (; i<256; ++i)
+ palette[i].red = palette[i].green = palette[i].blue = 42;
+
+ png_set_PLTE(pp, pi, palette, npalette);
+ }
+
+ if (do_tRNS)
+ {
+ int i, j;
+ png_byte tRNS[256];
+
+ /* Set all the entries, but skip trailing opaque entries */
+ for (i=j=0; i<npalette; ++i)
+ if ((tRNS[i] = ppal[i].alpha) < 255)
+ j = i+1;
+
+ /* Fill in the remainder with a detectable value: */
+ for (; i<256; ++i)
+ tRNS[i] = 24;
+
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ if (j > 0)
+ png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+# endif
+ }
+}
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+static void
+set_random_tRNS(png_structp pp, png_infop pi, const png_byte colour_type,
+ const int bit_depth)
+{
+ /* To make this useful the tRNS color needs to match at least one pixel.
+ * Random values are fine for gray, including the 16-bit case where we know
+ * that the test image contains all the gray values. For RGB we need more
+ * method as only 65536 different RGB values are generated.
+ */
+ png_color_16 tRNS;
+ const png_uint_16 mask = (png_uint_16)((1U << bit_depth)-1);
+
+ RANDOMIZE(tRNS);
+
+ if (colour_type & 2/*RGB*/)
+ {
+ if (bit_depth == 8)
+ {
+ tRNS.blue = tRNS.red ^ tRNS.green;
+ tRNS.red &= mask;
+ tRNS.green &= mask;
+ tRNS.blue &= mask;
+ }
+
+ else /* bit_depth == 16 */
+ {
+ tRNS.green = (png_uint_16)(tRNS.red * 257);
+ tRNS.blue = (png_uint_16)(tRNS.green * 17);
+ }
+ }
+
+ else
+ tRNS.gray &= mask;
+
+ png_set_tRNS(pp, pi, NULL, 0, &tRNS);
+}
+#endif
+
+/* The number of passes is related to the interlace type. There was no libpng
+ * API to determine this prior to 1.5, so we need an inquiry function:
+ */
+static int
+npasses_from_interlace_type(png_const_structp pp, int interlace_type)
+{
+ switch (interlace_type)
+ {
+ default:
+ png_error(pp, "invalid interlace type");
+
+ case PNG_INTERLACE_NONE:
+ return 1;
+
+ case PNG_INTERLACE_ADAM7:
+ return PNG_INTERLACE_ADAM7_PASSES;
+ }
+}
+
+static unsigned int
+bit_size(png_const_structp pp, png_byte colour_type, png_byte bit_depth)
+{
+ switch (colour_type)
+ {
+ default: png_error(pp, "invalid color type");
+
+ case 0: return bit_depth;
+
+ case 2: return 3*bit_depth;
+
+ case 3: return bit_depth;
+
+ case 4: return 2*bit_depth;
+
+ case 6: return 4*bit_depth;
+ }
+}
+
+#define TRANSFORM_WIDTH 128U
+#define TRANSFORM_ROWMAX (TRANSFORM_WIDTH*8U)
+#define SIZE_ROWMAX (16*8U) /* 16 pixels, max 8 bytes each - 128 bytes */
+#define STANDARD_ROWMAX TRANSFORM_ROWMAX /* The larger of the two */
+#define SIZE_HEIGHTMAX 16 /* Maximum range of size images */
+
+static size_t
+transform_rowsize(png_const_structp pp, png_byte colour_type,
+ png_byte bit_depth)
+{
+ return (TRANSFORM_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
+}
+
+/* transform_width(pp, colour_type, bit_depth) current returns the same number
+ * every time, so just use a macro:
+ */
+#define transform_width(pp, colour_type, bit_depth) TRANSFORM_WIDTH
+
+static png_uint_32
+transform_height(png_const_structp pp, png_byte colour_type, png_byte bit_depth)
+{
+ switch (bit_size(pp, colour_type, bit_depth))
+ {
+ case 1:
+ case 2:
+ case 4:
+ return 1; /* Total of 128 pixels */
+
+ case 8:
+ return 2; /* Total of 256 pixels/bytes */
+
+ case 16:
+ return 512; /* Total of 65536 pixels */
+
+ case 24:
+ case 32:
+ return 512; /* 65536 pixels */
+
+ case 48:
+ case 64:
+ return 2048;/* 4 x 65536 pixels. */
+# define TRANSFORM_HEIGHTMAX 2048
+
+ default:
+ return 0; /* Error, will be caught later */
+ }
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* The following can only be defined here, now we have the definitions
+ * of the transform image sizes.
+ */
+static png_uint_32
+standard_width(png_const_structp pp, png_uint_32 id)
+{
+ png_uint_32 width = WIDTH_FROM_ID(id);
+ UNUSED(pp)
+
+ if (width == 0)
+ width = transform_width(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+
+ return width;
+}
+
+static png_uint_32
+standard_height(png_const_structp pp, png_uint_32 id)
+{
+ png_uint_32 height = HEIGHT_FROM_ID(id);
+
+ if (height == 0)
+ height = transform_height(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+
+ return height;
+}
+
+static png_uint_32
+standard_rowsize(png_const_structp pp, png_uint_32 id)
+{
+ png_uint_32 width = standard_width(pp, id);
+
+ /* This won't overflow: */
+ width *= bit_size(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+ return (width + 7) / 8;
+}
+#endif /* PNG_READ_SUPPORTED */
+
+static void
+transform_row(png_const_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
+ png_byte colour_type, png_byte bit_depth, png_uint_32 y)
+{
+ png_uint_32 v = y << 7;
+ png_uint_32 i = 0;
+
+ switch (bit_size(pp, colour_type, bit_depth))
+ {
+ case 1:
+ while (i<128/8) buffer[i] = (png_byte)(v & 0xff), v += 17, ++i;
+ return;
+
+ case 2:
+ while (i<128/4) buffer[i] = (png_byte)(v & 0xff), v += 33, ++i;
+ return;
+
+ case 4:
+ while (i<128/2) buffer[i] = (png_byte)(v & 0xff), v += 65, ++i;
+ return;
+
+ case 8:
+ /* 256 bytes total, 128 bytes in each row set as follows: */
+ while (i<128) buffer[i] = (png_byte)(v & 0xff), ++v, ++i;
+ return;
+
+ case 16:
+ /* Generate all 65536 pixel values in order, which includes the 8 bit
+ * GA case as well as the 16 bit G case.
+ */
+ while (i<128)
+ {
+ buffer[2*i] = (png_byte)((v>>8) & 0xff);
+ buffer[2*i+1] = (png_byte)(v & 0xff);
+ ++v;
+ ++i;
+ }
+
+ return;
+
+ case 24:
+ /* 65535 pixels, but rotate the values. */
+ while (i<128)
+ {
+ /* Three bytes per pixel, r, g, b, make b by r^g */
+ buffer[3*i+0] = (png_byte)((v >> 8) & 0xff);
+ buffer[3*i+1] = (png_byte)(v & 0xff);
+ buffer[3*i+2] = (png_byte)(((v >> 8) ^ v) & 0xff);
+ ++v;
+ ++i;
+ }
+
+ return;
+
+ case 32:
+ /* 65535 pixels, r, g, b, a; just replicate */
+ while (i<128)
+ {
+ buffer[4*i+0] = (png_byte)((v >> 8) & 0xff);
+ buffer[4*i+1] = (png_byte)(v & 0xff);
+ buffer[4*i+2] = (png_byte)((v >> 8) & 0xff);
+ buffer[4*i+3] = (png_byte)(v & 0xff);
+ ++v;
+ ++i;
+ }
+
+ return;
+
+ case 48:
+ /* y is maximum 2047, giving 4x65536 pixels, make 'r' increase by 1 at
+ * each pixel, g increase by 257 (0x101) and 'b' by 0x1111:
+ */
+ while (i<128)
+ {
+ png_uint_32 t = v++;
+ buffer[6*i+0] = (png_byte)((t >> 8) & 0xff);
+ buffer[6*i+1] = (png_byte)(t & 0xff);
+ t *= 257;
+ buffer[6*i+2] = (png_byte)((t >> 8) & 0xff);
+ buffer[6*i+3] = (png_byte)(t & 0xff);
+ t *= 17;
+ buffer[6*i+4] = (png_byte)((t >> 8) & 0xff);
+ buffer[6*i+5] = (png_byte)(t & 0xff);
+ ++i;
+ }
+
+ return;
+
+ case 64:
+ /* As above in the 32 bit case. */
+ while (i<128)
+ {
+ png_uint_32 t = v++;
+ buffer[8*i+0] = (png_byte)((t >> 8) & 0xff);
+ buffer[8*i+1] = (png_byte)(t & 0xff);
+ buffer[8*i+4] = (png_byte)((t >> 8) & 0xff);
+ buffer[8*i+5] = (png_byte)(t & 0xff);
+ t *= 257;
+ buffer[8*i+2] = (png_byte)((t >> 8) & 0xff);
+ buffer[8*i+3] = (png_byte)(t & 0xff);
+ buffer[8*i+6] = (png_byte)((t >> 8) & 0xff);
+ buffer[8*i+7] = (png_byte)(t & 0xff);
+ ++i;
+ }
+ return;
+
+ default:
+ break;
+ }
+
+ png_error(pp, "internal error");
+}
+
+/* This is just to do the right cast - could be changed to a function to check
+ * 'bd' but there isn't much point.
+ */
+#define DEPTH(bd) ((png_byte)(1U << (bd)))
+
+/* This is just a helper for compiling on minimal systems with no write
+ * interlacing support. If there is no write interlacing we can't generate test
+ * cases with interlace:
+ */
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+# define INTERLACE_LAST PNG_INTERLACE_LAST
+# define check_interlace_type(type) ((void)(type))
+# define set_write_interlace_handling(pp,type) png_set_interlace_handling(pp)
+# define do_own_interlace 0
+#elif PNG_LIBPNG_VER < 10700
+# define set_write_interlace_handling(pp,type) (1)
+static void
+check_interlace_type(int const interlace_type)
+{
+ /* Prior to 1.7.0 libpng does not support the write of an interlaced image
+ * unless PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the
+ * code here does the pixel interlace itself, so:
+ */
+ if (interlace_type != PNG_INTERLACE_NONE)
+ {
+ /* This is an internal error - --interlace tests should be skipped, not
+ * attempted.
+ */
+ fprintf(stderr, "pngvalid: no interlace support\n");
+ exit(99);
+ }
+}
+# define INTERLACE_LAST (PNG_INTERLACE_NONE+1)
+# define do_own_interlace 0
+#else /* libpng 1.7+ */
+# define set_write_interlace_handling(pp,type)\
+ npasses_from_interlace_type(pp,type)
+# define check_interlace_type(type) ((void)(type))
+# define INTERLACE_LAST PNG_INTERLACE_LAST
+# define do_own_interlace 1
+#endif /* WRITE_INTERLACING tests */
+
+#define CAN_WRITE_INTERLACE\
+ PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED
+
+/* Do the same thing for read interlacing; this controls whether read tests do
+ * their own de-interlace or use libpng.
+ */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+# define do_read_interlace 0
+#else /* no libpng read interlace support */
+# define do_read_interlace 1
+#endif
+/* The following two routines use the PNG interlace support macros from
+ * png.h to interlace or deinterlace rows.
+ */
+static void
+interlace_row(png_bytep buffer, png_const_bytep imageRow,
+ unsigned int pixel_size, png_uint_32 w, int pass, int littleendian)
+{
+ png_uint_32 xin, xout, xstep;
+
+ /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
+ * code is presented this way to make it easier to understand. In practice
+ * consult the code in the libpng source to see other ways of doing this.
+ *
+ * It is OK for buffer and imageRow to be identical, because 'xin' moves
+ * faster than 'xout' and we copy up.
+ */
+ xin = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xout=0; xin<w; xin+=xstep)
+ {
+ pixel_copy(buffer, xout, imageRow, xin, pixel_size, littleendian);
+ ++xout;
+ }
+}
+
+#ifdef PNG_READ_SUPPORTED
+static void
+deinterlace_row(png_bytep buffer, png_const_bytep row,
+ unsigned int pixel_size, png_uint_32 w, int pass, int littleendian)
+{
+ /* The inverse of the above, 'row' is part of row 'y' of the output image,
+ * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
+ * the pixels of row into buffer and return the number written (to allow
+ * this to be checked).
+ */
+ png_uint_32 xin, xout, xstep;
+
+ xout = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xin=0; xout<w; xout+=xstep)
+ {
+ pixel_copy(buffer, xout, row, xin, pixel_size, littleendian);
+ ++xin;
+ }
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Make a standardized image given an image colour type, bit depth and
+ * interlace type. The standard images have a very restricted range of
+ * rows and heights and are used for testing transforms rather than image
+ * layout details. See make_size_images below for a way to make images
+ * that test odd sizes along with the libpng interlace handling.
+ */
+static void
+make_transform_image(png_store* const ps, png_byte const colour_type,
+ png_byte const bit_depth, unsigned int palette_number,
+ int interlace_type, png_const_charp name)
+{
+ context(ps, fault);
+
+ check_interlace_type(interlace_type);
+
+ Try
+ {
+ png_infop pi;
+ png_structp pp = set_store_for_write(ps, &pi, name);
+ png_uint_32 h, w;
+
+ /* In the event of a problem return control to the Catch statement below
+ * to do the clean up - it is not possible to 'return' directly from a Try
+ * block.
+ */
+ if (pp == NULL)
+ Throw ps;
+
+ w = transform_width(pp, colour_type, bit_depth);
+ h = transform_height(pp, colour_type, bit_depth);
+
+ png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#ifdef PNG_TEXT_SUPPORTED
+# if defined(PNG_READ_zTXt_SUPPORTED) && defined(PNG_WRITE_zTXt_SUPPORTED)
+# define TEXT_COMPRESSION PNG_TEXT_COMPRESSION_zTXt
+# else
+# define TEXT_COMPRESSION PNG_TEXT_COMPRESSION_NONE
+# endif
+ {
+ static char key[] = "image name"; /* must be writeable */
+ size_t pos;
+ png_text text;
+ char copy[FILE_NAME_SIZE];
+
+ /* Use a compressed text string to test the correct interaction of text
+ * compression and IDAT compression.
+ */
+ text.compression = TEXT_COMPRESSION;
+ text.key = key;
+ /* Yuck: the text must be writable! */
+ pos = safecat(copy, sizeof copy, 0, ps->wname);
+ text.text = copy;
+ text.text_length = pos;
+ text.itxt_length = 0;
+ text.lang = 0;
+ text.lang_key = 0;
+
+ png_set_text(pp, pi, &text, 1);
+ }
+#endif
+
+ if (colour_type == 3) /* palette */
+ init_standard_palette(ps, pp, pi, 1U << bit_depth, 1/*do tRNS*/);
+
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ else if (palette_number)
+ set_random_tRNS(pp, pi, colour_type, bit_depth);
+# endif
+
+ png_write_info(pp, pi);
+
+ if (png_get_rowbytes(pp, pi) !=
+ transform_rowsize(pp, colour_type, bit_depth))
+ png_error(pp, "transform row size incorrect");
+
+ else
+ {
+ /* Somewhat confusingly this must be called *after* png_write_info
+ * because if it is called before, the information in *pp has not been
+ * updated to reflect the interlaced image.
+ */
+ int npasses = set_write_interlace_handling(pp, interlace_type);
+ int pass;
+
+ if (npasses != npasses_from_interlace_type(pp, interlace_type))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ for (pass=0; pass<npasses; ++pass)
+ {
+ png_uint_32 y;
+
+ /* do_own_interlace is a pre-defined boolean (a #define) which is
+ * set if we have to work out the interlaced rows here.
+ */
+ for (y=0; y<h; ++y)
+ {
+ png_byte buffer[TRANSFORM_ROWMAX];
+
+ transform_row(pp, buffer, colour_type, bit_depth, y);
+
+# if do_own_interlace
+ /* If do_own_interlace *and* the image is interlaced we need a
+ * reduced interlace row; this may be reduced to empty.
+ */
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ {
+ /* The row must not be written if it doesn't exist, notice
+ * that there are two conditions here, either the row isn't
+ * ever in the pass or the row would be but isn't wide
+ * enough to contribute any pixels. In fact the wPass test
+ * can be used to skip the whole y loop in this case.
+ */
+ if (PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+ PNG_PASS_COLS(w, pass) > 0)
+ interlace_row(buffer, buffer,
+ bit_size(pp, colour_type, bit_depth), w, pass,
+ 0/*data always bigendian*/);
+ else
+ continue;
+ }
+# endif /* do_own_interlace */
+
+ png_write_row(pp, buffer);
+ }
+ }
+ }
+
+#ifdef PNG_TEXT_SUPPORTED
+ {
+ static char key[] = "end marker";
+ static char comment[] = "end";
+ png_text text;
+
+ /* Use a compressed text string to test the correct interaction of text
+ * compression and IDAT compression.
+ */
+ text.compression = TEXT_COMPRESSION;
+ text.key = key;
+ text.text = comment;
+ text.text_length = (sizeof comment)-1;
+ text.itxt_length = 0;
+ text.lang = 0;
+ text.lang_key = 0;
+
+ png_set_text(pp, pi, &text, 1);
+ }
+#endif
+
+ png_write_end(pp, pi);
+
+ /* And store this under the appropriate id, then clean up. */
+ store_storefile(ps, FILEID(colour_type, bit_depth, palette_number,
+ interlace_type, 0, 0, 0));
+
+ store_write_reset(ps);
+ }
+
+ Catch(fault)
+ {
+ /* Use the png_store returned by the exception. This may help the compiler
+ * because 'ps' is not used in this branch of the setjmp. Note that fault
+ * and ps will always be the same value.
+ */
+ store_write_reset(fault);
+ }
+}
+
+static void
+make_transform_images(png_modifier *pm)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ unsigned int palette_number = 0;
+
+ /* This is in case of errors. */
+ safecat(pm->this.test, sizeof pm->this.test, 0, "make standard images");
+
+ /* Use next_format to enumerate all the combinations we test, including
+ * generating multiple low bit depth palette images. Non-A images (palette
+ * and direct) are created with and without tRNS chunks.
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number, 1, 1))
+ {
+ int interlace_type;
+
+ for (interlace_type = PNG_INTERLACE_NONE;
+ interlace_type < INTERLACE_LAST; ++interlace_type)
+ {
+ char name[FILE_NAME_SIZE];
+
+ standard_name(name, sizeof name, 0, colour_type, bit_depth,
+ palette_number, interlace_type, 0, 0, do_own_interlace);
+ make_transform_image(&pm->this, colour_type, bit_depth, palette_number,
+ interlace_type, name);
+ }
+ }
+}
+
+/* Build a single row for the 'size' test images; this fills in only the
+ * first bit_width bits of the sample row.
+ */
+static void
+size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
+{
+ /* height is in the range 1 to 16, so: */
+ y = ((y & 1) << 7) + ((y & 2) << 6) + ((y & 4) << 5) + ((y & 8) << 4);
+ /* the following ensures bits are set in small images: */
+ y ^= 0xA5;
+
+ while (bit_width >= 8)
+ *buffer++ = (png_byte)y++, bit_width -= 8;
+
+ /* There may be up to 7 remaining bits, these go in the most significant
+ * bits of the byte.
+ */
+ if (bit_width > 0)
+ {
+ png_uint_32 mask = (1U<<(8-bit_width))-1;
+ *buffer = (png_byte)((*buffer & mask) | (y & ~mask));
+ }
+}
+
+static void
+make_size_image(png_store* const ps, png_byte const colour_type,
+ png_byte const bit_depth, int const interlace_type,
+ png_uint_32 const w, png_uint_32 const h,
+ int const do_interlace)
+{
+ context(ps, fault);
+
+ check_interlace_type(interlace_type);
+
+ Try
+ {
+ png_infop pi;
+ png_structp pp;
+ unsigned int pixel_size;
+
+ /* Make a name and get an appropriate id for the store: */
+ char name[FILE_NAME_SIZE];
+ const png_uint_32 id = FILEID(colour_type, bit_depth, 0/*palette*/,
+ interlace_type, w, h, do_interlace);
+
+ standard_name_from_id(name, sizeof name, 0, id);
+ pp = set_store_for_write(ps, &pi, name);
+
+ /* In the event of a problem return control to the Catch statement below
+ * to do the clean up - it is not possible to 'return' directly from a Try
+ * block.
+ */
+ if (pp == NULL)
+ Throw ps;
+
+ png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#ifdef PNG_TEXT_SUPPORTED
+ {
+ static char key[] = "image name"; /* must be writeable */
+ size_t pos;
+ png_text text;
+ char copy[FILE_NAME_SIZE];
+
+ /* Use a compressed text string to test the correct interaction of text
+ * compression and IDAT compression.
+ */
+ text.compression = TEXT_COMPRESSION;
+ text.key = key;
+ /* Yuck: the text must be writable! */
+ pos = safecat(copy, sizeof copy, 0, ps->wname);
+ text.text = copy;
+ text.text_length = pos;
+ text.itxt_length = 0;
+ text.lang = 0;
+ text.lang_key = 0;
+
+ png_set_text(pp, pi, &text, 1);
+ }
+#endif
+
+ if (colour_type == 3) /* palette */
+ init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
+
+ png_write_info(pp, pi);
+
+ /* Calculate the bit size, divide by 8 to get the byte size - this won't
+ * overflow because we know the w values are all small enough even for
+ * a system where 'unsigned int' is only 16 bits.
+ */
+ pixel_size = bit_size(pp, colour_type, bit_depth);
+ if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
+ png_error(pp, "size row size incorrect");
+
+ else
+ {
+ int npasses = npasses_from_interlace_type(pp, interlace_type);
+ png_uint_32 y;
+ int pass;
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ int nfilter = PNG_FILTER_VALUE_LAST;
+# endif
+ png_byte image[16][SIZE_ROWMAX];
+
+ /* To help consistent error detection make the parts of this buffer
+ * that aren't set below all '1':
+ */
+ memset(image, 0xff, sizeof image);
+
+ if (!do_interlace &&
+ npasses != set_write_interlace_handling(pp, interlace_type))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ /* Prepare the whole image first to avoid making it 7 times: */
+ for (y=0; y<h; ++y)
+ size_row(image[y], w * pixel_size, y);
+
+ for (pass=0; pass<npasses; ++pass)
+ {
+ /* The following two are for checking the macros: */
+ const png_uint_32 wPass = PNG_PASS_COLS(w, pass);
+
+ /* If do_interlace is set we don't call png_write_row for every
+ * row because some of them are empty. In fact, for a 1x1 image,
+ * most of them are empty!
+ */
+ for (y=0; y<h; ++y)
+ {
+ png_const_bytep row = image[y];
+ png_byte tempRow[SIZE_ROWMAX];
+
+ /* If do_interlace *and* the image is interlaced we
+ * need a reduced interlace row; this may be reduced
+ * to empty.
+ */
+ if (do_interlace && interlace_type == PNG_INTERLACE_ADAM7)
+ {
+ /* The row must not be written if it doesn't exist, notice
+ * that there are two conditions here, either the row isn't
+ * ever in the pass or the row would be but isn't wide
+ * enough to contribute any pixels. In fact the wPass test
+ * can be used to skip the whole y loop in this case.
+ */
+ if (PNG_ROW_IN_INTERLACE_PASS(y, pass) && wPass > 0)
+ {
+ /* Set to all 1's for error detection (libpng tends to
+ * set unset things to 0).
+ */
+ memset(tempRow, 0xff, sizeof tempRow);
+ interlace_row(tempRow, row, pixel_size, w, pass,
+ 0/*data always bigendian*/);
+ row = tempRow;
+ }
+ else
+ continue;
+ }
+
+# ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* Only get to here if the row has some pixels in it, set the
+ * filters to 'all' for the very first row and thereafter to a
+ * single filter. It isn't well documented, but png_set_filter
+ * does accept a filter number (per the spec) as well as a bit
+ * mask.
+ *
+ * The apparent wackiness of decrementing nfilter rather than
+ * incrementing is so that Paeth gets used in all images bigger
+ * than 1 row - it's the tricky one.
+ */
+ png_set_filter(pp, 0/*method*/,
+ nfilter >= PNG_FILTER_VALUE_LAST ? PNG_ALL_FILTERS : nfilter);
+
+ if (nfilter-- == 0)
+ nfilter = PNG_FILTER_VALUE_LAST-1;
+# endif
+
+ png_write_row(pp, row);
+ }
+ }
+ }
+
+#ifdef PNG_TEXT_SUPPORTED
+ {
+ static char key[] = "end marker";
+ static char comment[] = "end";
+ png_text text;
+
+ /* Use a compressed text string to test the correct interaction of text
+ * compression and IDAT compression.
+ */
+ text.compression = TEXT_COMPRESSION;
+ text.key = key;
+ text.text = comment;
+ text.text_length = (sizeof comment)-1;
+ text.itxt_length = 0;
+ text.lang = 0;
+ text.lang_key = 0;
+
+ png_set_text(pp, pi, &text, 1);
+ }
+#endif
+
+ png_write_end(pp, pi);
+
+ /* And store this under the appropriate id, then clean up. */
+ store_storefile(ps, id);
+
+ store_write_reset(ps);
+ }
+
+ Catch(fault)
+ {
+ /* Use the png_store returned by the exception. This may help the compiler
+ * because 'ps' is not used in this branch of the setjmp. Note that fault
+ * and ps will always be the same value.
+ */
+ store_write_reset(fault);
+ }
+}
+
+static void
+make_size(png_store* const ps, png_byte const colour_type, int bdlo,
+ int const bdhi)
+{
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ png_uint_32 width;
+
+ for (width = 1; width <= 16; ++width)
+ {
+ png_uint_32 height;
+
+ for (height = 1; height <= 16; ++height)
+ {
+ /* The four combinations of DIY interlace and interlace or not -
+ * no interlace + DIY should be identical to no interlace with
+ * libpng doing it.
+ */
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
+ width, height, 0);
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
+ width, height, 1);
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+ width, height, 0);
+# endif
+# if CAN_WRITE_INTERLACE
+ /* 1.7.0 removes the hack that prevented app write of an interlaced
+ * image if WRITE_INTERLACE was not supported
+ */
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+ width, height, 1);
+# endif
+ }
+ }
+ }
+}
+
+static void
+make_size_images(png_store *ps)
+{
+ /* This is in case of errors. */
+ safecat(ps->test, sizeof ps->test, 0, "make size images");
+
+ /* Arguments are colour_type, low bit depth, high bit depth
+ */
+ make_size(ps, 0, 0, WRITE_BDHI);
+ make_size(ps, 2, 3, WRITE_BDHI);
+ make_size(ps, 3, 0, 3 /*palette: max 8 bits*/);
+ make_size(ps, 4, 3, WRITE_BDHI);
+ make_size(ps, 6, 3, WRITE_BDHI);
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Return a row based on image id and 'y' for checking: */
+static void
+standard_row(png_const_structp pp, png_byte std[STANDARD_ROWMAX],
+ png_uint_32 id, png_uint_32 y)
+{
+ if (WIDTH_FROM_ID(id) == 0)
+ transform_row(pp, std, COL_FROM_ID(id), DEPTH_FROM_ID(id), y);
+ else
+ size_row(std, WIDTH_FROM_ID(id) * bit_size(pp, COL_FROM_ID(id),
+ DEPTH_FROM_ID(id)), y);
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* Tests - individual test cases */
+/* Like 'make_standard' but errors are deliberately introduced into the calls
+ * to ensure that they get detected - it should not be possible to write an
+ * invalid image with libpng!
+ */
+/* TODO: the 'set' functions can probably all be made to take a
+ * png_const_structp rather than a modifiable one.
+ */
+#ifdef PNG_WARNINGS_SUPPORTED
+static void
+sBIT0_error_fn(png_structp pp, png_infop pi)
+{
+ /* 0 is invalid... */
+ png_color_8 bad;
+ bad.red = bad.green = bad.blue = bad.gray = bad.alpha = 0;
+ png_set_sBIT(pp, pi, &bad);
+}
+
+static void
+sBIT_error_fn(png_structp pp, png_infop pi)
+{
+ png_byte bit_depth;
+ png_color_8 bad;
+
+ if (png_get_color_type(pp, pi) == PNG_COLOR_TYPE_PALETTE)
+ bit_depth = 8;
+
+ else
+ bit_depth = png_get_bit_depth(pp, pi);
+
+ /* Now we know the bit depth we can easily generate an invalid sBIT entry */
+ bad.red = bad.green = bad.blue = bad.gray = bad.alpha =
+ (png_byte)(bit_depth+1);
+ png_set_sBIT(pp, pi, &bad);
+}
+
+static const struct
+{
+ void (*fn)(png_structp, png_infop);
+ const char *msg;
+ unsigned int warning :1; /* the error is a warning... */
+} error_test[] =
+ {
+ /* no warnings makes these errors undetectable prior to 1.7.0 */
+ { sBIT0_error_fn, "sBIT(0): failed to detect error",
+ PNG_LIBPNG_VER < 10700 },
+
+ { sBIT_error_fn, "sBIT(too big): failed to detect error",
+ PNG_LIBPNG_VER < 10700 },
+ };
+
+static void
+make_error(png_store* const ps, png_byte const colour_type,
+ png_byte bit_depth, int interlace_type, int test, png_const_charp name)
+{
+ context(ps, fault);
+
+ check_interlace_type(interlace_type);
+
+ Try
+ {
+ png_infop pi;
+ const png_structp pp = set_store_for_write(ps, &pi, name);
+ png_uint_32 w, h;
+ gnu_volatile(pp)
+
+ if (pp == NULL)
+ Throw ps;
+
+ w = transform_width(pp, colour_type, bit_depth);
+ gnu_volatile(w)
+ h = transform_height(pp, colour_type, bit_depth);
+ gnu_volatile(h)
+ png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (colour_type == 3) /* palette */
+ init_standard_palette(ps, pp, pi, 1U << bit_depth, 0/*do tRNS*/);
+
+ /* Time for a few errors; these are in various optional chunks, the
+ * standard tests test the standard chunks pretty well.
+ */
+# define exception__prev exception_prev_1
+# define exception__env exception_env_1
+ Try
+ {
+ gnu_volatile(exception__prev)
+
+ /* Expect this to throw: */
+ ps->expect_error = !error_test[test].warning;
+ ps->expect_warning = error_test[test].warning;
+ ps->saw_warning = 0;
+ error_test[test].fn(pp, pi);
+
+ /* Normally the error is only detected here: */
+ png_write_info(pp, pi);
+
+ /* And handle the case where it was only a warning: */
+ if (ps->expect_warning && ps->saw_warning)
+ Throw ps;
+
+ /* If we get here there is a problem, we have success - no error or
+ * no warning - when we shouldn't have success. Log an error.
+ */
+ store_log(ps, pp, error_test[test].msg, 1 /*error*/);
+ }
+
+ Catch (fault)
+ { /* expected exit */
+ }
+#undef exception__prev
+#undef exception__env
+
+ /* And clear these flags */
+ ps->expect_error = 0;
+ ps->expect_warning = 0;
+
+ /* Now write the whole image, just to make sure that the detected, or
+ * undetected, errro has not created problems inside libpng.
+ */
+ if (png_get_rowbytes(pp, pi) !=
+ transform_rowsize(pp, colour_type, bit_depth))
+ png_error(pp, "row size incorrect");
+
+ else
+ {
+ int npasses = set_write_interlace_handling(pp, interlace_type);
+ int pass;
+
+ if (npasses != npasses_from_interlace_type(pp, interlace_type))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ for (pass=0; pass<npasses; ++pass)
+ {
+ png_uint_32 y;
+
+ for (y=0; y<h; ++y)
+ {
+ png_byte buffer[TRANSFORM_ROWMAX];
+
+ transform_row(pp, buffer, colour_type, bit_depth, y);
+
+# if do_own_interlace
+ /* If do_own_interlace *and* the image is interlaced we need a
+ * reduced interlace row; this may be reduced to empty.
+ */
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ {
+ /* The row must not be written if it doesn't exist, notice
+ * that there are two conditions here, either the row isn't
+ * ever in the pass or the row would be but isn't wide
+ * enough to contribute any pixels. In fact the wPass test
+ * can be used to skip the whole y loop in this case.
+ */
+ if (PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+ PNG_PASS_COLS(w, pass) > 0)
+ interlace_row(buffer, buffer,
+ bit_size(pp, colour_type, bit_depth), w, pass,
+ 0/*data always bigendian*/);
+ else
+ continue;
+ }
+# endif /* do_own_interlace */
+
+ png_write_row(pp, buffer);
+ }
+ }
+ }
+
+ png_write_end(pp, pi);
+
+ /* The following deletes the file that was just written. */
+ store_write_reset(ps);
+ }
+
+ Catch(fault)
+ {
+ store_write_reset(fault);
+ }
+}
+
+static int
+make_errors(png_modifier* const pm, png_byte const colour_type,
+ int bdlo, int const bdhi)
+{
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ int interlace_type;
+
+ for (interlace_type = PNG_INTERLACE_NONE;
+ interlace_type < INTERLACE_LAST; ++interlace_type)
+ {
+ unsigned int test;
+ char name[FILE_NAME_SIZE];
+
+ standard_name(name, sizeof name, 0, colour_type, 1<<bdlo, 0,
+ interlace_type, 0, 0, do_own_interlace);
+
+ for (test=0; test<ARRAY_SIZE(error_test); ++test)
+ {
+ make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
+ test, name);
+
+ if (fail(pm))
+ return 0;
+ }
+ }
+ }
+
+ return 1; /* keep going */
+}
+#endif /* PNG_WARNINGS_SUPPORTED */
+
+static void
+perform_error_test(png_modifier *pm)
+{
+#ifdef PNG_WARNINGS_SUPPORTED /* else there are no cases that work! */
+ /* Need to do this here because we just write in this test. */
+ safecat(pm->this.test, sizeof pm->this.test, 0, "error test");
+
+ if (!make_errors(pm, 0, 0, WRITE_BDHI))
+ return;
+
+ if (!make_errors(pm, 2, 3, WRITE_BDHI))
+ return;
+
+ if (!make_errors(pm, 3, 0, 3))
+ return;
+
+ if (!make_errors(pm, 4, 3, WRITE_BDHI))
+ return;
+
+ if (!make_errors(pm, 6, 3, WRITE_BDHI))
+ return;
+#else
+ UNUSED(pm)
+#endif
+}
+
+/* This is just to validate the internal PNG formatting code - if this fails
+ * then the warning messages the library outputs will probably be garbage.
+ */
+static void
+perform_formatting_test(png_store *ps)
+{
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+ /* The handle into the formatting code is the RFC1123 support; this test does
+ * nothing if that is compiled out.
+ */
+ context(ps, fault);
+
+ Try
+ {
+ png_const_charp correct = "29 Aug 2079 13:53:60 +0000";
+ png_const_charp result;
+# if PNG_LIBPNG_VER >= 10600
+ char timestring[29];
+# endif
+ png_structp pp;
+ png_time pt;
+
+ pp = set_store_for_write(ps, NULL, "libpng formatting test");
+
+ if (pp == NULL)
+ Throw ps;
+
+
+ /* Arbitrary settings: */
+ pt.year = 2079;
+ pt.month = 8;
+ pt.day = 29;
+ pt.hour = 13;
+ pt.minute = 53;
+ pt.second = 60; /* a leap second */
+
+# if PNG_LIBPNG_VER < 10600
+ result = png_convert_to_rfc1123(pp, &pt);
+# else
+ if (png_convert_to_rfc1123_buffer(timestring, &pt))
+ result = timestring;
+
+ else
+ result = NULL;
+# endif
+
+ if (result == NULL)
+ png_error(pp, "png_convert_to_rfc1123 failed");
+
+ if (strcmp(result, correct) != 0)
+ {
+ size_t pos = 0;
+ char msg[128];
+
+ pos = safecat(msg, sizeof msg, pos, "png_convert_to_rfc1123(");
+ pos = safecat(msg, sizeof msg, pos, correct);
+ pos = safecat(msg, sizeof msg, pos, ") returned: '");
+ pos = safecat(msg, sizeof msg, pos, result);
+ pos = safecat(msg, sizeof msg, pos, "'");
+
+ png_error(pp, msg);
+ }
+
+ store_write_reset(ps);
+ }
+
+ Catch(fault)
+ {
+ store_write_reset(fault);
+ }
+#else
+ UNUSED(ps)
+#endif
+}
+
+#ifdef PNG_READ_SUPPORTED
+/* Because we want to use the same code in both the progressive reader and the
+ * sequential reader it is necessary to deal with the fact that the progressive
+ * reader callbacks only have one parameter (png_get_progressive_ptr()), so this
+ * must contain all the test parameters and all the local variables directly
+ * accessible to the sequential reader implementation.
+ *
+ * The technique adopted is to reinvent part of what Dijkstra termed a
+ * 'display'; an array of pointers to the stack frames of enclosing functions so
+ * that a nested function definition can access the local (C auto) variables of
+ * the functions that contain its definition. In fact C provides the first
+ * pointer (the local variables - the stack frame pointer) and the last (the
+ * global variables - the BCPL global vector typically implemented as global
+ * addresses), this code requires one more pointer to make the display - the
+ * local variables (and function call parameters) of the function that actually
+ * invokes either the progressive or sequential reader.
+ *
+ * Perhaps confusingly this technique is confounded with classes - the
+ * 'standard_display' defined here is sub-classed as the 'gamma_display' below.
+ * A gamma_display is a standard_display, taking advantage of the ANSI-C
+ * requirement that the pointer to the first member of a structure must be the
+ * same as the pointer to the structure. This allows us to reuse standard_
+ * functions in the gamma test code; something that could not be done with
+ * nested functions!
+ */
+typedef struct standard_display
+{
+ png_store* ps; /* Test parameters (passed to the function) */
+ png_byte colour_type;
+ png_byte bit_depth;
+ png_byte red_sBIT; /* Input data sBIT values. */
+ png_byte green_sBIT;
+ png_byte blue_sBIT;
+ png_byte alpha_sBIT;
+ png_byte interlace_type;
+ png_byte filler; /* Output has a filler */
+ png_uint_32 id; /* Calculated file ID */
+ png_uint_32 w; /* Width of image */
+ png_uint_32 h; /* Height of image */
+ int npasses; /* Number of interlaced passes */
+ png_uint_32 pixel_size; /* Width of one pixel in bits */
+ png_uint_32 bit_width; /* Width of output row in bits */
+ size_t cbRow; /* Bytes in a row of the output image */
+ int do_interlace; /* Do interlacing internally */
+ int littleendian; /* App (row) data is little endian */
+ int is_transparent; /* Transparency information was present. */
+ int has_tRNS; /* color type GRAY or RGB with a tRNS chunk. */
+ int speed; /* Doing a speed test */
+ int use_update_info;/* Call update_info, not start_image */
+ struct
+ {
+ png_uint_16 red;
+ png_uint_16 green;
+ png_uint_16 blue;
+ } transparent; /* The transparent color, if set. */
+ int npalette; /* Number of entries in the palette. */
+ store_palette
+ palette;
+} standard_display;
+
+static void
+standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
+ int do_interlace, int use_update_info)
+{
+ memset(dp, 0, sizeof *dp);
+
+ dp->ps = ps;
+ dp->colour_type = COL_FROM_ID(id);
+ dp->bit_depth = DEPTH_FROM_ID(id);
+ if (dp->bit_depth < 1 || dp->bit_depth > 16)
+ internal_error(ps, "internal: bad bit depth");
+ if (dp->colour_type == 3)
+ dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT = 8;
+ else
+ dp->red_sBIT = dp->blue_sBIT = dp->green_sBIT = dp->alpha_sBIT =
+ dp->bit_depth;
+ dp->interlace_type = INTERLACE_FROM_ID(id);
+ check_interlace_type(dp->interlace_type);
+ dp->id = id;
+ /* All the rest are filled in after the read_info: */
+ dp->w = 0;
+ dp->h = 0;
+ dp->npasses = 0;
+ dp->pixel_size = 0;
+ dp->bit_width = 0;
+ dp->cbRow = 0;
+ dp->do_interlace = do_interlace;
+ dp->littleendian = 0;
+ dp->is_transparent = 0;
+ dp->speed = ps->speed;
+ dp->use_update_info = use_update_info;
+ dp->npalette = 0;
+ /* Preset the transparent color to black: */
+ memset(&dp->transparent, 0, sizeof dp->transparent);
+ /* Preset the palette to full intensity/opaque througout: */
+ memset(dp->palette, 0xff, sizeof dp->palette);
+}
+
+/* Initialize the palette fields - this must be done later because the palette
+ * comes from the particular png_store_file that is selected.
+ */
+static void
+standard_palette_init(standard_display *dp)
+{
+ store_palette_entry *palette = store_current_palette(dp->ps, &dp->npalette);
+
+ /* The remaining entries remain white/opaque. */
+ if (dp->npalette > 0)
+ {
+ int i = dp->npalette;
+ memcpy(dp->palette, palette, i * sizeof *palette);
+
+ /* Check for a non-opaque palette entry: */
+ while (--i >= 0)
+ if (palette[i].alpha < 255)
+ break;
+
+# ifdef __GNUC__
+ /* GCC can't handle the more obviously optimizable version. */
+ if (i >= 0)
+ dp->is_transparent = 1;
+ else
+ dp->is_transparent = 0;
+# else
+ dp->is_transparent = (i >= 0);
+# endif
+ }
+}
+
+/* Utility to read the palette from the PNG file and convert it into
+ * store_palette format. This returns 1 if there is any transparency in the
+ * palette (it does not check for a transparent colour in the non-palette case.)
+ */
+static int
+read_palette(store_palette palette, int *npalette, png_const_structp pp,
+ png_infop pi)
+{
+ png_colorp pal;
+ png_bytep trans_alpha;
+ int num;
+
+ pal = 0;
+ *npalette = -1;
+
+ if (png_get_PLTE(pp, pi, &pal, npalette) & PNG_INFO_PLTE)
+ {
+ int i = *npalette;
+
+ if (i <= 0 || i > 256)
+ png_error(pp, "validate: invalid PLTE count");
+
+ while (--i >= 0)
+ {
+ palette[i].red = pal[i].red;
+ palette[i].green = pal[i].green;
+ palette[i].blue = pal[i].blue;
+ }
+
+ /* Mark the remainder of the entries with a flag value (other than
+ * white/opaque which is the flag value stored above.)
+ */
+ memset(palette + *npalette, 126, (256-*npalette) * sizeof *palette);
+ }
+
+ else /* !png_get_PLTE */
+ {
+ if (*npalette != (-1))
+ png_error(pp, "validate: invalid PLTE result");
+ /* But there is no palette, so record this: */
+ *npalette = 0;
+ memset(palette, 113, sizeof (store_palette));
+ }
+
+ trans_alpha = 0;
+ num = 2; /* force error below */
+ if ((png_get_tRNS(pp, pi, &trans_alpha, &num, 0) & PNG_INFO_tRNS) != 0 &&
+ (trans_alpha != NULL || num != 1/*returns 1 for a transparent color*/) &&
+ /* Oops, if a palette tRNS gets expanded png_read_update_info (at least so
+ * far as 1.5.4) does not remove the trans_alpha pointer, only num_trans,
+ * so in the above call we get a success, we get a pointer (who knows what
+ * to) and we get num_trans == 0:
+ */
+ !(trans_alpha != NULL && num == 0)) /* TODO: fix this in libpng. */
+ {
+ int i;
+
+ /* Any of these are crash-worthy - given the implementation of
+ * png_get_tRNS up to 1.5 an app won't crash if it just checks the
+ * result above and fails to check that the variables it passed have
+ * actually been filled in! Note that if the app were to pass the
+ * last, png_color_16p, variable too it couldn't rely on this.
+ */
+ if (trans_alpha == NULL || num <= 0 || num > 256 || num > *npalette)
+ png_error(pp, "validate: unexpected png_get_tRNS (palette) result");
+
+ for (i=0; i<num; ++i)
+ palette[i].alpha = trans_alpha[i];
+
+ for (num=*npalette; i<num; ++i)
+ palette[i].alpha = 255;
+
+ for (; i<256; ++i)
+ palette[i].alpha = 33; /* flag value */
+
+ return 1; /* transparency */
+ }
+
+ else
+ {
+ /* No palette transparency - just set the alpha channel to opaque. */
+ int i;
+
+ for (i=0, num=*npalette; i<num; ++i)
+ palette[i].alpha = 255;
+
+ for (; i<256; ++i)
+ palette[i].alpha = 55; /* flag value */
+
+ return 0; /* no transparency */
+ }
+}
+
+/* Utility to validate the palette if it should not have changed (the
+ * non-transform case).
+ */
+static void
+standard_palette_validate(standard_display *dp, png_const_structp pp,
+ png_infop pi)
+{
+ int npalette;
+ store_palette palette;
+
+ if (read_palette(palette, &npalette, pp, pi) != dp->is_transparent)
+ png_error(pp, "validate: palette transparency changed");
+
+ if (npalette != dp->npalette)
+ {
+ size_t pos = 0;
+ char msg[64];
+
+ pos = safecat(msg, sizeof msg, pos, "validate: palette size changed: ");
+ pos = safecatn(msg, sizeof msg, pos, dp->npalette);
+ pos = safecat(msg, sizeof msg, pos, " -> ");
+ pos = safecatn(msg, sizeof msg, pos, npalette);
+ png_error(pp, msg);
+ }
+
+ {
+ int i = npalette; /* npalette is aliased */
+
+ while (--i >= 0)
+ if (palette[i].red != dp->palette[i].red ||
+ palette[i].green != dp->palette[i].green ||
+ palette[i].blue != dp->palette[i].blue ||
+ palette[i].alpha != dp->palette[i].alpha)
+ png_error(pp, "validate: PLTE or tRNS chunk changed");
+ }
+}
+
+/* By passing a 'standard_display' the progressive callbacks can be used
+ * directly by the sequential code, the functions suffixed "_imp" are the
+ * implementations, the functions without the suffix are the callbacks.
+ *
+ * The code for the info callback is split into two because this callback calls
+ * png_read_update_info or png_start_read_image and what gets called depends on
+ * whether the info needs updating (we want to test both calls in pngvalid.)
+ */
+static void
+standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
+{
+ if (png_get_bit_depth(pp, pi) != dp->bit_depth)
+ png_error(pp, "validate: bit depth changed");
+
+ if (png_get_color_type(pp, pi) != dp->colour_type)
+ png_error(pp, "validate: color type changed");
+
+ if (png_get_filter_type(pp, pi) != PNG_FILTER_TYPE_BASE)
+ png_error(pp, "validate: filter type changed");
+
+ if (png_get_interlace_type(pp, pi) != dp->interlace_type)
+ png_error(pp, "validate: interlacing changed");
+
+ if (png_get_compression_type(pp, pi) != PNG_COMPRESSION_TYPE_BASE)
+ png_error(pp, "validate: compression type changed");
+
+ dp->w = png_get_image_width(pp, pi);
+
+ if (dp->w != standard_width(pp, dp->id))
+ png_error(pp, "validate: image width changed");
+
+ dp->h = png_get_image_height(pp, pi);
+
+ if (dp->h != standard_height(pp, dp->id))
+ png_error(pp, "validate: image height changed");
+
+ /* Record (but don't check at present) the input sBIT according to the colour
+ * type information.
+ */
+ {
+ png_color_8p sBIT = 0;
+
+ if (png_get_sBIT(pp, pi, &sBIT) & PNG_INFO_sBIT)
+ {
+ int sBIT_invalid = 0;
+
+ if (sBIT == 0)
+ png_error(pp, "validate: unexpected png_get_sBIT result");
+
+ if (dp->colour_type & PNG_COLOR_MASK_COLOR)
+ {
+ if (sBIT->red == 0 || sBIT->red > dp->bit_depth)
+ sBIT_invalid = 1;
+ else
+ dp->red_sBIT = sBIT->red;
+
+ if (sBIT->green == 0 || sBIT->green > dp->bit_depth)
+ sBIT_invalid = 1;
+ else
+ dp->green_sBIT = sBIT->green;
+
+ if (sBIT->blue == 0 || sBIT->blue > dp->bit_depth)
+ sBIT_invalid = 1;
+ else
+ dp->blue_sBIT = sBIT->blue;
+ }
+
+ else /* !COLOR */
+ {
+ if (sBIT->gray == 0 || sBIT->gray > dp->bit_depth)
+ sBIT_invalid = 1;
+ else
+ dp->blue_sBIT = dp->green_sBIT = dp->red_sBIT = sBIT->gray;
+ }
+
+ /* All 8 bits in tRNS for a palette image are significant - see the
+ * spec.
+ */
+ if (dp->colour_type & PNG_COLOR_MASK_ALPHA)
+ {
+ if (sBIT->alpha == 0 || sBIT->alpha > dp->bit_depth)
+ sBIT_invalid = 1;
+ else
+ dp->alpha_sBIT = sBIT->alpha;
+ }
+
+ if (sBIT_invalid)
+ png_error(pp, "validate: sBIT value out of range");
+ }
+ }
+
+ /* Important: this is validating the value *before* any transforms have been
+ * put in place. It doesn't matter for the standard tests, where there are
+ * no transforms, but it does for other tests where rowbytes may change after
+ * png_read_update_info.
+ */
+ if (png_get_rowbytes(pp, pi) != standard_rowsize(pp, dp->id))
+ png_error(pp, "validate: row size changed");
+
+ /* Validate the colour type 3 palette (this can be present on other color
+ * types.)
+ */
+ standard_palette_validate(dp, pp, pi);
+
+ /* In any case always check for a tranparent color (notice that the
+ * colour type 3 case must not give a successful return on the get_tRNS call
+ * with these arguments!)
+ */
+ {
+ png_color_16p trans_color = 0;
+
+ if (png_get_tRNS(pp, pi, 0, 0, &trans_color) & PNG_INFO_tRNS)
+ {
+ if (trans_color == 0)
+ png_error(pp, "validate: unexpected png_get_tRNS (color) result");
+
+ switch (dp->colour_type)
+ {
+ case 0:
+ dp->transparent.red = dp->transparent.green = dp->transparent.blue =
+ trans_color->gray;
+ dp->has_tRNS = 1;
+ break;
+
+ case 2:
+ dp->transparent.red = trans_color->red;
+ dp->transparent.green = trans_color->green;
+ dp->transparent.blue = trans_color->blue;
+ dp->has_tRNS = 1;
+ break;
+
+ case 3:
+ /* Not expected because it should result in the array case
+ * above.
+ */
+ png_error(pp, "validate: unexpected png_get_tRNS result");
+ break;
+
+ default:
+ png_error(pp, "validate: invalid tRNS chunk with alpha image");
+ }
+ }
+ }
+
+ /* Read the number of passes - expected to match the value used when
+ * creating the image (interlaced or not). This has the side effect of
+ * turning on interlace handling (if do_interlace is not set.)
+ */
+ dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
+ if (!dp->do_interlace)
+ {
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (dp->npasses != png_set_interlace_handling(pp))
+ png_error(pp, "validate: file changed interlace type");
+# else /* !READ_INTERLACING */
+ /* This should never happen: the relevant tests (!do_interlace) should
+ * not be run.
+ */
+ if (dp->npasses > 1)
+ png_error(pp, "validate: no libpng interlace support");
+# endif /* !READ_INTERLACING */
+ }
+
+ /* Caller calls png_read_update_info or png_start_read_image now, then calls
+ * part2.
+ */
+}
+
+/* This must be called *after* the png_read_update_info call to get the correct
+ * 'rowbytes' value, otherwise png_get_rowbytes will refer to the untransformed
+ * image.
+ */
+static void
+standard_info_part2(standard_display *dp, png_const_structp pp,
+ png_const_infop pi, int nImages)
+{
+ /* Record cbRow now that it can be found. */
+ {
+ png_byte ct = png_get_color_type(pp, pi);
+ png_byte bd = png_get_bit_depth(pp, pi);
+
+ if (bd >= 8 && (ct == PNG_COLOR_TYPE_RGB || ct == PNG_COLOR_TYPE_GRAY) &&
+ dp->filler)
+ ct |= 4; /* handle filler as faked alpha channel */
+
+ dp->pixel_size = bit_size(pp, ct, bd);
+ }
+ dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
+ dp->cbRow = png_get_rowbytes(pp, pi);
+
+ /* Validate the rowbytes here again. */
+ if (dp->cbRow != (dp->bit_width+7)/8)
+ png_error(pp, "bad png_get_rowbytes calculation");
+
+ /* Then ensure there is enough space for the output image(s). */
+ store_ensure_image(dp->ps, pp, nImages, dp->cbRow, dp->h);
+}
+
+static void
+standard_info_imp(standard_display *dp, png_structp pp, png_infop pi,
+ int nImages)
+{
+ /* Note that the validation routine has the side effect of turning on
+ * interlace handling in the subsequent code.
+ */
+ standard_info_part1(dp, pp, pi);
+
+ /* And the info callback has to call this (or png_read_update_info - see
+ * below in the png_modifier code for that variant.
+ */
+ if (dp->use_update_info)
+ {
+ /* For debugging the effect of multiple calls: */
+ int i = dp->use_update_info;
+ while (i-- > 0)
+ png_read_update_info(pp, pi);
+ }
+
+ else
+ png_start_read_image(pp);
+
+ /* Validate the height, width and rowbytes plus ensure that sufficient buffer
+ * exists for decoding the image.
+ */
+ standard_info_part2(dp, pp, pi, nImages);
+}
+
+static void PNGCBAPI
+standard_info(png_structp pp, png_infop pi)
+{
+ standard_display *dp = voidcast(standard_display*,
+ png_get_progressive_ptr(pp));
+
+ /* Call with nImages==1 because the progressive reader can only produce one
+ * image.
+ */
+ standard_info_imp(dp, pp, pi, 1 /*only one image*/);
+}
+
+static void PNGCBAPI
+progressive_row(png_structp ppIn, png_bytep new_row, png_uint_32 y, int pass)
+{
+ png_const_structp pp = ppIn;
+ const standard_display *dp = voidcast(standard_display*,
+ png_get_progressive_ptr(pp));
+
+ /* When handling interlacing some rows will be absent in each pass, the
+ * callback still gets called, but with a NULL pointer. This is checked
+ * in the 'else' clause below. We need our own 'cbRow', but we can't call
+ * png_get_rowbytes because we got no info structure.
+ */
+ if (new_row != NULL)
+ {
+ png_bytep row;
+
+ /* In the case where the reader doesn't do the interlace it gives
+ * us the y in the sub-image:
+ */
+ if (dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7)
+ {
+#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
+ /* Use this opportunity to validate the png 'current' APIs: */
+ if (y != png_get_current_row_number(pp))
+ png_error(pp, "png_get_current_row_number is broken");
+
+ if (pass != png_get_current_pass_number(pp))
+ png_error(pp, "png_get_current_pass_number is broken");
+#endif /* USER_TRANSFORM_INFO */
+
+ y = PNG_ROW_FROM_PASS_ROW(y, pass);
+ }
+
+ /* Validate this just in case. */
+ if (y >= dp->h)
+ png_error(pp, "invalid y to progressive row callback");
+
+ row = store_image_row(dp->ps, pp, 0, y);
+
+ /* Combine the new row into the old: */
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (dp->do_interlace)
+#endif /* READ_INTERLACING */
+ {
+ if (dp->interlace_type == PNG_INTERLACE_ADAM7)
+ deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass,
+ dp->littleendian);
+ else
+ row_copy(row, new_row, dp->pixel_size * dp->w, dp->littleendian);
+ }
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ else
+ png_progressive_combine_row(pp, row, new_row);
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+ }
+
+ else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
+ PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+ PNG_PASS_COLS(dp->w, pass) > 0)
+ png_error(pp, "missing row in progressive de-interlacing");
+}
+
+static void
+sequential_row(standard_display *dp, png_structp pp, png_infop pi,
+ const int iImage, const int iDisplay)
+{
+ const int npasses = dp->npasses;
+ const int do_interlace = dp->do_interlace &&
+ dp->interlace_type == PNG_INTERLACE_ADAM7;
+ const png_uint_32 height = standard_height(pp, dp->id);
+ const png_uint_32 width = standard_width(pp, dp->id);
+ const png_store* ps = dp->ps;
+ int pass;
+
+ for (pass=0; pass<npasses; ++pass)
+ {
+ png_uint_32 y;
+ png_uint_32 wPass = PNG_PASS_COLS(width, pass);
+
+ for (y=0; y<height; ++y)
+ {
+ if (do_interlace)
+ {
+ /* wPass may be zero or this row may not be in this pass.
+ * png_read_row must not be called in either case.
+ */
+ if (wPass > 0 && PNG_ROW_IN_INTERLACE_PASS(y, pass))
+ {
+ /* Read the row into a pair of temporary buffers, then do the
+ * merge here into the output rows.
+ */
+ png_byte row[STANDARD_ROWMAX], display[STANDARD_ROWMAX];
+
+ /* The following aids (to some extent) error detection - we can
+ * see where png_read_row wrote. Use opposite values in row and
+ * display to make this easier. Don't use 0xff (which is used in
+ * the image write code to fill unused bits) or 0 (which is a
+ * likely value to overwrite unused bits with).
+ */
+ memset(row, 0xc5, sizeof row);
+ memset(display, 0x5c, sizeof display);
+
+ png_read_row(pp, row, display);
+
+ if (iImage >= 0)
+ deinterlace_row(store_image_row(ps, pp, iImage, y), row,
+ dp->pixel_size, dp->w, pass, dp->littleendian);
+
+ if (iDisplay >= 0)
+ deinterlace_row(store_image_row(ps, pp, iDisplay, y), display,
+ dp->pixel_size, dp->w, pass, dp->littleendian);
+ }
+ }
+ else
+ png_read_row(pp,
+ iImage >= 0 ? store_image_row(ps, pp, iImage, y) : NULL,
+ iDisplay >= 0 ? store_image_row(ps, pp, iDisplay, y) : NULL);
+ }
+ }
+
+ /* And finish the read operation (only really necessary if the caller wants
+ * to find additional data in png_info from chunks after the last IDAT.)
+ */
+ png_read_end(pp, pi);
+}
+
+#ifdef PNG_TEXT_SUPPORTED
+static void
+standard_check_text(png_const_structp pp, png_const_textp tp,
+ png_const_charp keyword, png_const_charp text)
+{
+ char msg[1024];
+ size_t pos = safecat(msg, sizeof msg, 0, "text: ");
+ size_t ok;
+
+ pos = safecat(msg, sizeof msg, pos, keyword);
+ pos = safecat(msg, sizeof msg, pos, ": ");
+ ok = pos;
+
+ if (tp->compression != TEXT_COMPRESSION)
+ {
+ char buf[64];
+
+ sprintf(buf, "compression [%d->%d], ", TEXT_COMPRESSION,
+ tp->compression);
+ pos = safecat(msg, sizeof msg, pos, buf);
+ }
+
+ if (tp->key == NULL || strcmp(tp->key, keyword) != 0)
+ {
+ pos = safecat(msg, sizeof msg, pos, "keyword \"");
+ if (tp->key != NULL)
+ {
+ pos = safecat(msg, sizeof msg, pos, tp->key);
+ pos = safecat(msg, sizeof msg, pos, "\", ");
+ }
+
+ else
+ pos = safecat(msg, sizeof msg, pos, "null, ");
+ }
+
+ if (tp->text == NULL)
+ pos = safecat(msg, sizeof msg, pos, "text lost, ");
+
+ else
+ {
+ if (tp->text_length != strlen(text))
+ {
+ char buf[64];
+ sprintf(buf, "text length changed[%lu->%lu], ",
+ (unsigned long)strlen(text), (unsigned long)tp->text_length);
+ pos = safecat(msg, sizeof msg, pos, buf);
+ }
+
+ if (strcmp(tp->text, text) != 0)
+ {
+ pos = safecat(msg, sizeof msg, pos, "text becomes \"");
+ pos = safecat(msg, sizeof msg, pos, tp->text);
+ pos = safecat(msg, sizeof msg, pos, "\" (was \"");
+ pos = safecat(msg, sizeof msg, pos, text);
+ pos = safecat(msg, sizeof msg, pos, "\"), ");
+ }
+ }
+
+ if (tp->itxt_length != 0)
+ pos = safecat(msg, sizeof msg, pos, "iTXt length set, ");
+
+ if (tp->lang != NULL)
+ {
+ pos = safecat(msg, sizeof msg, pos, "iTXt language \"");
+ pos = safecat(msg, sizeof msg, pos, tp->lang);
+ pos = safecat(msg, sizeof msg, pos, "\", ");
+ }
+
+ if (tp->lang_key != NULL)
+ {
+ pos = safecat(msg, sizeof msg, pos, "iTXt keyword \"");
+ pos = safecat(msg, sizeof msg, pos, tp->lang_key);
+ pos = safecat(msg, sizeof msg, pos, "\", ");
+ }
+
+ if (pos > ok)
+ {
+ msg[pos-2] = '\0'; /* Remove the ", " at the end */
+ png_error(pp, msg);
+ }
+}
+
+static void
+standard_text_validate(standard_display *dp, png_const_structp pp,
+ png_infop pi, int check_end)
+{
+ png_textp tp = NULL;
+ png_uint_32 num_text = png_get_text(pp, pi, &tp, NULL);
+
+ if (num_text == 2 && tp != NULL)
+ {
+ standard_check_text(pp, tp, "image name", dp->ps->current->name);
+
+ /* This exists because prior to 1.5.18 the progressive reader left the
+ * png_struct z_stream unreset at the end of the image, so subsequent
+ * attempts to use it simply returns Z_STREAM_END.
+ */
+ if (check_end)
+ standard_check_text(pp, tp+1, "end marker", "end");
+ }
+
+ else
+ {
+ char msg[64];
+
+ sprintf(msg, "expected two text items, got %lu",
+ (unsigned long)num_text);
+ png_error(pp, msg);
+ }
+}
+#else
+# define standard_text_validate(dp,pp,pi,check_end) ((void)0)
+#endif
+
+static void
+standard_row_validate(standard_display *dp, png_const_structp pp,
+ int iImage, int iDisplay, png_uint_32 y)
+{
+ int where;
+ png_byte std[STANDARD_ROWMAX];
+
+ /* The row must be pre-initialized to the magic number here for the size
+ * tests to pass:
+ */
+ memset(std, 178, sizeof std);
+ standard_row(pp, std, dp->id, y);
+
+ /* At the end both the 'row' and 'display' arrays should end up identical.
+ * In earlier passes 'row' will be partially filled in, with only the pixels
+ * that have been read so far, but 'display' will have those pixels
+ * replicated to fill the unread pixels while reading an interlaced image.
+ */
+ if (iImage >= 0 &&
+ (where = pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
+ dp->bit_width)) != 0)
+ {
+ char msg[64];
+ sprintf(msg, "PNG image row[%lu][%d] changed from %.2x to %.2x",
+ (unsigned long)y, where-1, std[where-1],
+ store_image_row(dp->ps, pp, iImage, y)[where-1]);
+ png_error(pp, msg);
+ }
+
+ if (iDisplay >= 0 &&
+ (where = pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y),
+ dp->bit_width)) != 0)
+ {
+ char msg[64];
+ sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x",
+ (unsigned long)y, where-1, std[where-1],
+ store_image_row(dp->ps, pp, iDisplay, y)[where-1]);
+ png_error(pp, msg);
+ }
+}
+
+static void
+standard_image_validate(standard_display *dp, png_const_structp pp, int iImage,
+ int iDisplay)
+{
+ png_uint_32 y;
+
+ if (iImage >= 0)
+ store_image_check(dp->ps, pp, iImage);
+
+ if (iDisplay >= 0)
+ store_image_check(dp->ps, pp, iDisplay);
+
+ for (y=0; y<dp->h; ++y)
+ standard_row_validate(dp, pp, iImage, iDisplay, y);
+
+ /* This avoids false positives if the validation code is never called! */
+ dp->ps->validated = 1;
+}
+
+static void PNGCBAPI
+standard_end(png_structp ppIn, png_infop pi)
+{
+ png_const_structp pp = ppIn;
+ standard_display *dp = voidcast(standard_display*,
+ png_get_progressive_ptr(pp));
+
+ UNUSED(pi)
+
+ /* Validate the image - progressive reading only produces one variant for
+ * interlaced images.
+ */
+ standard_text_validate(dp, pp, pi,
+ PNG_LIBPNG_VER >= 10518/*check_end: see comments above*/);
+ standard_image_validate(dp, pp, 0, -1);
+}
+
+/* A single test run checking the standard image to ensure it is not damaged. */
+static void
+standard_test(png_store* const psIn, png_uint_32 const id,
+ int do_interlace, int use_update_info)
+{
+ standard_display d;
+ context(psIn, fault);
+
+ /* Set up the display (stack frame) variables from the arguments to the
+ * function and initialize the locals that are filled in later.
+ */
+ standard_display_init(&d, psIn, id, do_interlace, use_update_info);
+
+ /* Everything is protected by a Try/Catch. The functions called also
+ * typically have local Try/Catch blocks.
+ */
+ Try
+ {
+ png_structp pp;
+ png_infop pi;
+
+ /* Get a png_struct for reading the image. This will throw an error if it
+ * fails, so we don't need to check the result.
+ */
+ pp = set_store_for_read(d.ps, &pi, d.id,
+ d.do_interlace ? (d.ps->progressive ?
+ "pngvalid progressive deinterlacer" :
+ "pngvalid sequential deinterlacer") : (d.ps->progressive ?
+ "progressive reader" : "sequential reader"));
+
+ /* Initialize the palette correctly from the png_store_file. */
+ standard_palette_init(&d);
+
+ /* Introduce the correct read function. */
+ if (d.ps->progressive)
+ {
+ png_set_progressive_read_fn(pp, &d, standard_info, progressive_row,
+ standard_end);
+
+ /* Now feed data into the reader until we reach the end: */
+ store_progressive_read(d.ps, pp, pi);
+ }
+ else
+ {
+ /* Note that this takes the store, not the display. */
+ png_set_read_fn(pp, d.ps, store_read);
+
+ /* Check the header values: */
+ png_read_info(pp, pi);
+
+ /* The code tests both versions of the images that the sequential
+ * reader can produce.
+ */
+ standard_info_imp(&d, pp, pi, 2 /*images*/);
+
+ /* Need the total bytes in the image below; we can't get to this point
+ * unless the PNG file values have been checked against the expected
+ * values.
+ */
+ {
+ sequential_row(&d, pp, pi, 0, 1);
+
+ /* After the last pass loop over the rows again to check that the
+ * image is correct.
+ */
+ if (!d.speed)
+ {
+ standard_text_validate(&d, pp, pi, 1/*check_end*/);
+ standard_image_validate(&d, pp, 0, 1);
+ }
+ else
+ d.ps->validated = 1;
+ }
+ }
+
+ /* Check for validation. */
+ if (!d.ps->validated)
+ png_error(pp, "image read failed silently");
+
+ /* Successful completion. */
+ }
+
+ Catch(fault)
+ d.ps = fault; /* make sure this hasn't been clobbered. */
+
+ /* In either case clean up the store. */
+ store_read_reset(d.ps);
+}
+
+static int
+test_standard(png_modifier* const pm, png_byte const colour_type,
+ int bdlo, int const bdhi)
+{
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ int interlace_type;
+
+ for (interlace_type = PNG_INTERLACE_NONE;
+ interlace_type < INTERLACE_LAST; ++interlace_type)
+ {
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ interlace_type, 0, 0, 0), do_read_interlace, pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+ }
+ }
+
+ return 1; /* keep going */
+}
+
+static void
+perform_standard_test(png_modifier *pm)
+{
+ /* Test each colour type over the valid range of bit depths (expressed as
+ * log2(bit_depth) in turn, stop as soon as any error is detected.
+ */
+ if (!test_standard(pm, 0, 0, READ_BDHI))
+ return;
+
+ if (!test_standard(pm, 2, 3, READ_BDHI))
+ return;
+
+ if (!test_standard(pm, 3, 0, 3))
+ return;
+
+ if (!test_standard(pm, 4, 3, READ_BDHI))
+ return;
+
+ if (!test_standard(pm, 6, 3, READ_BDHI))
+ return;
+}
+
+
+/********************************** SIZE TESTS ********************************/
+static int
+test_size(png_modifier* const pm, png_byte const colour_type,
+ int bdlo, int const bdhi)
+{
+ /* Run the tests on each combination.
+ *
+ * NOTE: on my 32 bit x86 each of the following blocks takes
+ * a total of 3.5 seconds if done across every combo of bit depth
+ * width and height. This is a waste of time in practice, hence the
+ * hinc and winc stuff:
+ */
+ static const png_byte hinc[] = {1, 3, 11, 1, 5};
+ static const png_byte winc[] = {1, 9, 5, 7, 1};
+ const int save_bdlo = bdlo;
+
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ png_uint_32 h, w;
+
+ for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
+ {
+ /* First test all the 'size' images against the sequential
+ * reader using libpng to deinterlace (where required.) This
+ * validates the write side of libpng. There are four possibilities
+ * to validate.
+ */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+
+ /* Now validate the interlaced read side - do_interlace true,
+ * in the progressive case this does actually make a difference
+ * to the code used in the non-interlaced case too.
+ */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+
+# if CAN_WRITE_INTERLACE
+ /* Validate the pngvalid code itself: */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 1), 1/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+# endif
+ }
+ }
+
+ /* Now do the tests of libpng interlace handling, after we have made sure
+ * that the pngvalid version works:
+ */
+ for (bdlo = save_bdlo; bdlo <= bdhi; ++bdlo)
+ {
+ png_uint_32 h, w;
+
+ for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
+ {
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* Test with pngvalid generated interlaced images first; we have
+ * already verify these are ok (unless pngvalid has self-consistent
+ * read/write errors, which is unlikely), so this detects errors in the
+ * read side first:
+ */
+# if CAN_WRITE_INTERLACE
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+# endif
+# endif /* READ_INTERLACING */
+
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Test the libpng write side against the pngvalid read side: */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+# endif
+
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+# ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Test both together: */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/,
+ pm->use_update_info);
+
+ if (fail(pm))
+ return 0;
+# endif
+# endif /* READ_INTERLACING */
+ }
+ }
+
+ return 1; /* keep going */
+}
+
+static void
+perform_size_test(png_modifier *pm)
+{
+ /* Test each colour type over the valid range of bit depths (expressed as
+ * log2(bit_depth) in turn, stop as soon as any error is detected.
+ */
+ if (!test_size(pm, 0, 0, READ_BDHI))
+ return;
+
+ if (!test_size(pm, 2, 3, READ_BDHI))
+ return;
+
+ /* For the moment don't do the palette test - it's a waste of time when
+ * compared to the grayscale test.
+ */
+#if 0
+ if (!test_size(pm, 3, 0, 3))
+ return;
+#endif
+
+ if (!test_size(pm, 4, 3, READ_BDHI))
+ return;
+
+ if (!test_size(pm, 6, 3, READ_BDHI))
+ return;
+}
+
+
+/******************************* TRANSFORM TESTS ******************************/
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* A set of tests to validate libpng image transforms. The possibilities here
+ * are legion because the transforms can be combined in a combinatorial
+ * fashion. To deal with this some measure of restraint is required, otherwise
+ * the tests would take forever.
+ */
+typedef struct image_pixel
+{
+ /* A local (pngvalid) representation of a PNG pixel, in all its
+ * various forms.
+ */
+ unsigned int red, green, blue, alpha; /* For non-palette images. */
+ unsigned int palette_index; /* For a palette image. */
+ png_byte colour_type; /* As in the spec. */
+ png_byte bit_depth; /* Defines bit size in row */
+ png_byte sample_depth; /* Scale of samples */
+ unsigned int have_tRNS :1; /* tRNS chunk may need processing */
+ unsigned int swap_rgb :1; /* RGB swapped to BGR */
+ unsigned int alpha_first :1; /* Alpha at start, not end */
+ unsigned int alpha_inverted :1; /* Alpha channel inverted */
+ unsigned int mono_inverted :1; /* Gray channel inverted */
+ unsigned int swap16 :1; /* Byte swap 16-bit components */
+ unsigned int littleendian :1; /* High bits on right */
+ unsigned int sig_bits :1; /* Pixel shifted (sig bits only) */
+
+ /* For checking the code calculates double precision floating point values
+ * along with an error value, accumulated from the transforms. Because an
+ * sBIT setting allows larger error bounds (indeed, by the spec, apparently
+ * up to just less than +/-1 in the scaled value) the *lowest* sBIT for each
+ * channel is stored. This sBIT value is folded in to the stored error value
+ * at the end of the application of the transforms to the pixel.
+ *
+ * If sig_bits is set above the red, green, blue and alpha values have been
+ * scaled so they only contain the significant bits of the component values.
+ */
+ double redf, greenf, bluef, alphaf;
+ double rede, greene, bluee, alphae;
+ png_byte red_sBIT, green_sBIT, blue_sBIT, alpha_sBIT;
+} image_pixel;
+
+/* Shared utility function, see below. */
+static void
+image_pixel_setf(image_pixel *this, unsigned int rMax, unsigned int gMax,
+ unsigned int bMax, unsigned int aMax)
+{
+ this->redf = this->red / (double)rMax;
+ this->greenf = this->green / (double)gMax;
+ this->bluef = this->blue / (double)bMax;
+ this->alphaf = this->alpha / (double)aMax;
+
+ if (this->red < rMax)
+ this->rede = this->redf * DBL_EPSILON;
+ else
+ this->rede = 0;
+ if (this->green < gMax)
+ this->greene = this->greenf * DBL_EPSILON;
+ else
+ this->greene = 0;
+ if (this->blue < bMax)
+ this->bluee = this->bluef * DBL_EPSILON;
+ else
+ this->bluee = 0;
+ if (this->alpha < aMax)
+ this->alphae = this->alphaf * DBL_EPSILON;
+ else
+ this->alphae = 0;
+}
+
+/* Initialize the structure for the next pixel - call this before doing any
+ * transforms and call it for each pixel since all the fields may need to be
+ * reset.
+ */
+static void
+image_pixel_init(image_pixel *this, png_const_bytep row, png_byte colour_type,
+ png_byte bit_depth, png_uint_32 x, store_palette palette,
+ const image_pixel *format /*from pngvalid transform of input*/)
+{
+ const png_byte sample_depth = (png_byte)(colour_type ==
+ PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
+ const unsigned int max = (1U<<sample_depth)-1;
+ const int swap16 = (format != 0 && format->swap16);
+ const int littleendian = (format != 0 && format->littleendian);
+ const int sig_bits = (format != 0 && format->sig_bits);
+
+ /* Initially just set everything to the same number and the alpha to opaque.
+ * Note that this currently assumes a simple palette where entry x has colour
+ * rgb(x,x,x)!
+ */
+ this->palette_index = this->red = this->green = this->blue =
+ sample(row, colour_type, bit_depth, x, 0, swap16, littleendian);
+ this->alpha = max;
+ this->red_sBIT = this->green_sBIT = this->blue_sBIT = this->alpha_sBIT =
+ sample_depth;
+
+ /* Then override as appropriate: */
+ if (colour_type == 3) /* palette */
+ {
+ /* This permits the caller to default to the sample value. */
+ if (palette != 0)
+ {
+ const unsigned int i = this->palette_index;
+
+ this->red = palette[i].red;
+ this->green = palette[i].green;
+ this->blue = palette[i].blue;
+ this->alpha = palette[i].alpha;
+ }
+ }
+
+ else /* not palette */
+ {
+ unsigned int i = 0;
+
+ if ((colour_type & 4) != 0 && format != 0 && format->alpha_first)
+ {
+ this->alpha = this->red;
+ /* This handles the gray case for 'AG' pixels */
+ this->palette_index = this->red = this->green = this->blue =
+ sample(row, colour_type, bit_depth, x, 1, swap16, littleendian);
+ i = 1;
+ }
+
+ if (colour_type & 2)
+ {
+ /* Green is second for both BGR and RGB: */
+ this->green = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+
+ if (format != 0 && format->swap_rgb) /* BGR */
+ this->red = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+ else
+ this->blue = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+ }
+
+ else /* grayscale */ if (format != 0 && format->mono_inverted)
+ this->red = this->green = this->blue = this->red ^ max;
+
+ if ((colour_type & 4) != 0) /* alpha */
+ {
+ if (format == 0 || !format->alpha_first)
+ this->alpha = sample(row, colour_type, bit_depth, x, ++i, swap16,
+ littleendian);
+
+ if (format != 0 && format->alpha_inverted)
+ this->alpha ^= max;
+ }
+ }
+
+ /* Calculate the scaled values, these are simply the values divided by
+ * 'max' and the error is initialized to the double precision epsilon value
+ * from the header file.
+ */
+ image_pixel_setf(this,
+ sig_bits ? (1U << format->red_sBIT)-1 : max,
+ sig_bits ? (1U << format->green_sBIT)-1 : max,
+ sig_bits ? (1U << format->blue_sBIT)-1 : max,
+ sig_bits ? (1U << format->alpha_sBIT)-1 : max);
+
+ /* Store the input information for use in the transforms - these will
+ * modify the information.
+ */
+ this->colour_type = colour_type;
+ this->bit_depth = bit_depth;
+ this->sample_depth = sample_depth;
+ this->have_tRNS = 0;
+ this->swap_rgb = 0;
+ this->alpha_first = 0;
+ this->alpha_inverted = 0;
+ this->mono_inverted = 0;
+ this->swap16 = 0;
+ this->littleendian = 0;
+ this->sig_bits = 0;
+}
+
+#if defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_GRAY_TO_RGB_SUPPORTED\
+ || defined PNG_READ_EXPAND_SUPPORTED || defined PNG_READ_EXPAND_16_SUPPORTED\
+ || defined PNG_READ_BACKGROUND_SUPPORTED
+/* Convert a palette image to an rgb image. This necessarily converts the tRNS
+ * chunk at the same time, because the tRNS will be in palette form. The way
+ * palette validation works means that the original palette is never updated,
+ * instead the image_pixel value from the row contains the RGB of the
+ * corresponding palette entry and *this* is updated. Consequently this routine
+ * only needs to change the colour type information.
+ */
+static void
+image_pixel_convert_PLTE(image_pixel *this)
+{
+ if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (this->have_tRNS)
+ {
+ this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ this->have_tRNS = 0;
+ }
+ else
+ this->colour_type = PNG_COLOR_TYPE_RGB;
+
+ /* The bit depth of the row changes at this point too (notice that this is
+ * the row format, not the sample depth, which is separate.)
+ */
+ this->bit_depth = 8;
+ }
+}
+
+/* Add an alpha channel; this will import the tRNS information because tRNS is
+ * not valid in an alpha image. The bit depth will invariably be set to at
+ * least 8 prior to 1.7.0. Palette images will be converted to alpha (using
+ * the above API). With png_set_background the alpha channel is never expanded
+ * but this routine is used by pngvalid to simplify code; 'for_background'
+ * records this.
+ */
+static void
+image_pixel_add_alpha(image_pixel *this, const standard_display *display,
+ int for_background)
+{
+ if (this->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(this);
+
+ if ((this->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
+ {
+ if (this->colour_type == PNG_COLOR_TYPE_GRAY)
+ {
+# if PNG_LIBPNG_VER < 10700
+ if (!for_background && this->bit_depth < 8)
+ this->bit_depth = this->sample_depth = 8;
+# endif
+
+ if (this->have_tRNS)
+ {
+ /* After 1.7 the expansion of bit depth only happens if there is a
+ * tRNS chunk to expand at this point.
+ */
+# if PNG_LIBPNG_VER >= 10700
+ if (!for_background && this->bit_depth < 8)
+ this->bit_depth = this->sample_depth = 8;
+# endif
+
+ this->have_tRNS = 0;
+
+ /* Check the input, original, channel value here against the
+ * original tRNS gray chunk valie.
+ */
+ if (this->red == display->transparent.red)
+ this->alphaf = 0;
+ else
+ this->alphaf = 1;
+ }
+ else
+ this->alphaf = 1;
+
+ this->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+ else if (this->colour_type == PNG_COLOR_TYPE_RGB)
+ {
+ if (this->have_tRNS)
+ {
+ this->have_tRNS = 0;
+
+ /* Again, check the exact input values, not the current transformed
+ * value!
+ */
+ if (this->red == display->transparent.red &&
+ this->green == display->transparent.green &&
+ this->blue == display->transparent.blue)
+ this->alphaf = 0;
+ else
+ this->alphaf = 1;
+ }
+ else
+ this->alphaf = 1;
+
+ this->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+
+ /* The error in the alpha is zero and the sBIT value comes from the
+ * original sBIT data (actually it will always be the original bit depth).
+ */
+ this->alphae = 0;
+ this->alpha_sBIT = display->alpha_sBIT;
+ }
+}
+#endif /* transforms that need image_pixel_add_alpha */
+
+struct transform_display;
+typedef struct image_transform
+{
+ /* The name of this transform: a string. */
+ const char *name;
+
+ /* Each transform can be disabled from the command line: */
+ int enable;
+
+ /* The global list of transforms; read only. */
+ struct image_transform *const list;
+
+ /* The global count of the number of times this transform has been set on an
+ * image.
+ */
+ unsigned int global_use;
+
+ /* The local count of the number of times this transform has been set. */
+ unsigned int local_use;
+
+ /* The next transform in the list, each transform must call its own next
+ * transform after it has processed the pixel successfully.
+ */
+ const struct image_transform *next;
+
+ /* A single transform for the image, expressed as a series of function
+ * callbacks and some space for values.
+ *
+ * First a callback to add any required modifications to the png_modifier;
+ * this gets called just before the modifier is set up for read.
+ */
+ void (*ini)(const struct image_transform *this,
+ struct transform_display *that);
+
+ /* And a callback to set the transform on the current png_read_struct:
+ */
+ void (*set)(const struct image_transform *this,
+ struct transform_display *that, png_structp pp, png_infop pi);
+
+ /* Then a transform that takes an input pixel in one PNG format or another
+ * and modifies it by a pngvalid implementation of the transform (thus
+ * duplicating the libpng intent without, we hope, duplicating the bugs
+ * in the libpng implementation!) The png_structp is solely to allow error
+ * reporting via png_error and png_warning.
+ */
+ void (*mod)(const struct image_transform *this, image_pixel *that,
+ png_const_structp pp, const struct transform_display *display);
+
+ /* Add this transform to the list and return true if the transform is
+ * meaningful for this colour type and bit depth - if false then the
+ * transform should have no effect on the image so there's not a lot of
+ * point running it.
+ */
+ int (*add)(struct image_transform *this,
+ const struct image_transform **that, png_byte colour_type,
+ png_byte bit_depth);
+} image_transform;
+
+typedef struct transform_display
+{
+ standard_display this;
+
+ /* Parameters */
+ png_modifier* pm;
+ const image_transform* transform_list;
+ unsigned int max_gamma_8;
+
+ /* Local variables */
+ png_byte output_colour_type;
+ png_byte output_bit_depth;
+ png_byte unpacked;
+
+ /* Modifications (not necessarily used.) */
+ gama_modification gama_mod;
+ chrm_modification chrm_mod;
+ srgb_modification srgb_mod;
+} transform_display;
+
+/* Set sRGB, cHRM and gAMA transforms as required by the current encoding. */
+static void
+transform_set_encoding(transform_display *this)
+{
+ /* Set up the png_modifier '_current' fields then use these to determine how
+ * to add appropriate chunks.
+ */
+ png_modifier *pm = this->pm;
+
+ modifier_set_encoding(pm);
+
+ if (modifier_color_encoding_is_set(pm))
+ {
+ if (modifier_color_encoding_is_sRGB(pm))
+ srgb_modification_init(&this->srgb_mod, pm, PNG_sRGB_INTENT_ABSOLUTE);
+
+ else
+ {
+ /* Set gAMA and cHRM separately. */
+ gama_modification_init(&this->gama_mod, pm, pm->current_gamma);
+
+ if (pm->current_encoding != 0)
+ chrm_modification_init(&this->chrm_mod, pm, pm->current_encoding);
+ }
+ }
+}
+
+/* Three functions to end the list: */
+static void
+image_transform_ini_end(const image_transform *this,
+ transform_display *that)
+{
+ UNUSED(this)
+ UNUSED(that)
+}
+
+static void
+image_transform_set_end(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ UNUSED(this)
+ UNUSED(that)
+ UNUSED(pp)
+ UNUSED(pi)
+}
+
+/* At the end of the list recalculate the output image pixel value from the
+ * double precision values set up by the preceding 'mod' calls:
+ */
+static unsigned int
+sample_scale(double sample_value, unsigned int scale)
+{
+ sample_value = floor(sample_value * scale + .5);
+
+ /* Return NaN as 0: */
+ if (!(sample_value > 0))
+ sample_value = 0;
+ else if (sample_value > scale)
+ sample_value = scale;
+
+ return (unsigned int)sample_value;
+}
+
+static void
+image_transform_mod_end(const image_transform *this, image_pixel *that,
+ png_const_structp pp, const transform_display *display)
+{
+ const unsigned int scale = (1U<<that->sample_depth)-1;
+ const int sig_bits = that->sig_bits;
+
+ UNUSED(this)
+ UNUSED(pp)
+ UNUSED(display)
+
+ /* At the end recalculate the digitized red green and blue values according
+ * to the current sample_depth of the pixel.
+ *
+ * The sample value is simply scaled to the maximum, checking for over
+ * and underflow (which can both happen for some image transforms,
+ * including simple size scaling, though libpng doesn't do that at present.
+ */
+ that->red = sample_scale(that->redf, scale);
+
+ /* This is a bit bogus; really the above calculation should use the red_sBIT
+ * value, not sample_depth, but because libpng does png_set_shift by just
+ * shifting the bits we get errors if we don't do it the same way.
+ */
+ if (sig_bits && that->red_sBIT < that->sample_depth)
+ that->red >>= that->sample_depth - that->red_sBIT;
+
+ /* The error value is increased, at the end, according to the lowest sBIT
+ * value seen. Common sense tells us that the intermediate integer
+ * representations are no more accurate than +/- 0.5 in the integral values,
+ * the sBIT allows the implementation to be worse than this. In addition the
+ * PNG specification actually permits any error within the range (-1..+1),
+ * but that is ignored here. Instead the final digitized value is compared,
+ * below to the digitized value of the error limits - this has the net effect
+ * of allowing (almost) +/-1 in the output value. It's difficult to see how
+ * any algorithm that digitizes intermediate results can be more accurate.
+ */
+ that->rede += 1./(2*((1U<<that->red_sBIT)-1));
+
+ if (that->colour_type & PNG_COLOR_MASK_COLOR)
+ {
+ that->green = sample_scale(that->greenf, scale);
+ if (sig_bits && that->green_sBIT < that->sample_depth)
+ that->green >>= that->sample_depth - that->green_sBIT;
+
+ that->blue = sample_scale(that->bluef, scale);
+ if (sig_bits && that->blue_sBIT < that->sample_depth)
+ that->blue >>= that->sample_depth - that->blue_sBIT;
+
+ that->greene += 1./(2*((1U<<that->green_sBIT)-1));
+ that->bluee += 1./(2*((1U<<that->blue_sBIT)-1));
+ }
+ else
+ {
+ that->blue = that->green = that->red;
+ that->bluef = that->greenf = that->redf;
+ that->bluee = that->greene = that->rede;
+ }
+
+ if ((that->colour_type & PNG_COLOR_MASK_ALPHA) ||
+ that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ that->alpha = sample_scale(that->alphaf, scale);
+ that->alphae += 1./(2*((1U<<that->alpha_sBIT)-1));
+ }
+ else
+ {
+ that->alpha = scale; /* opaque */
+ that->alphaf = 1; /* Override this. */
+ that->alphae = 0; /* It's exact ;-) */
+ }
+
+ if (sig_bits && that->alpha_sBIT < that->sample_depth)
+ that->alpha >>= that->sample_depth - that->alpha_sBIT;
+}
+
+/* Static 'end' structure: */
+static image_transform image_transform_end =
+{
+ "(end)", /* name */
+ 1, /* enable */
+ 0, /* list */
+ 0, /* global_use */
+ 0, /* local_use */
+ 0, /* next */
+ image_transform_ini_end,
+ image_transform_set_end,
+ image_transform_mod_end,
+ 0 /* never called, I want it to crash if it is! */
+};
+
+/* Reader callbacks and implementations, where they differ from the standard
+ * ones.
+ */
+static void
+transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id,
+ const image_transform *transform_list)
+{
+ memset(dp, 0, sizeof *dp);
+
+ /* Standard fields */
+ standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
+ pm->use_update_info);
+
+ /* Parameter fields */
+ dp->pm = pm;
+ dp->transform_list = transform_list;
+ dp->max_gamma_8 = 16;
+
+ /* Local variable fields */
+ dp->output_colour_type = 255; /* invalid */
+ dp->output_bit_depth = 255; /* invalid */
+ dp->unpacked = 0; /* not unpacked */
+}
+
+static void
+transform_info_imp(transform_display *dp, png_structp pp, png_infop pi)
+{
+ /* Reuse the standard stuff as appropriate. */
+ standard_info_part1(&dp->this, pp, pi);
+
+ /* Now set the list of transforms. */
+ dp->transform_list->set(dp->transform_list, dp, pp, pi);
+
+ /* Update the info structure for these transforms: */
+ {
+ int i = dp->this.use_update_info;
+ /* Always do one call, even if use_update_info is 0. */
+ do
+ png_read_update_info(pp, pi);
+ while (--i > 0);
+ }
+
+ /* And get the output information into the standard_display */
+ standard_info_part2(&dp->this, pp, pi, 1/*images*/);
+
+ /* Plus the extra stuff we need for the transform tests: */
+ dp->output_colour_type = png_get_color_type(pp, pi);
+ dp->output_bit_depth = png_get_bit_depth(pp, pi);
+
+ /* If png_set_filler is in action then fake the output color type to include
+ * an alpha channel where appropriate.
+ */
+ if (dp->output_bit_depth >= 8 &&
+ (dp->output_colour_type == PNG_COLOR_TYPE_RGB ||
+ dp->output_colour_type == PNG_COLOR_TYPE_GRAY) && dp->this.filler)
+ dp->output_colour_type |= 4;
+
+ /* Validate the combination of colour type and bit depth that we are getting
+ * out of libpng; the semantics of something not in the PNG spec are, at
+ * best, unclear.
+ */
+ switch (dp->output_colour_type)
+ {
+ case PNG_COLOR_TYPE_PALETTE:
+ if (dp->output_bit_depth > 8) goto error;
+ /*FALL THROUGH*/
+ case PNG_COLOR_TYPE_GRAY:
+ if (dp->output_bit_depth == 1 || dp->output_bit_depth == 2 ||
+ dp->output_bit_depth == 4)
+ break;
+ /*FALL THROUGH*/
+ default:
+ if (dp->output_bit_depth == 8 || dp->output_bit_depth == 16)
+ break;
+ /*FALL THROUGH*/
+ error:
+ {
+ char message[128];
+ size_t pos;
+
+ pos = safecat(message, sizeof message, 0,
+ "invalid final bit depth: colour type(");
+ pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
+ pos = safecat(message, sizeof message, pos, ") with bit depth: ");
+ pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+
+ png_error(pp, message);
+ }
+ }
+
+ /* Use a test pixel to check that the output agrees with what we expect -
+ * this avoids running the whole test if the output is unexpected. This also
+ * checks for internal errors.
+ */
+ {
+ image_pixel test_pixel;
+
+ memset(&test_pixel, 0, sizeof test_pixel);
+ test_pixel.colour_type = dp->this.colour_type; /* input */
+ test_pixel.bit_depth = dp->this.bit_depth;
+ if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
+ test_pixel.sample_depth = 8;
+ else
+ test_pixel.sample_depth = test_pixel.bit_depth;
+ /* Don't need sBIT here, but it must be set to non-zero to avoid
+ * arithmetic overflows.
+ */
+ test_pixel.have_tRNS = dp->this.is_transparent != 0;
+ test_pixel.red_sBIT = test_pixel.green_sBIT = test_pixel.blue_sBIT =
+ test_pixel.alpha_sBIT = test_pixel.sample_depth;
+
+ dp->transform_list->mod(dp->transform_list, &test_pixel, pp, dp);
+
+ if (test_pixel.colour_type != dp->output_colour_type)
+ {
+ char message[128];
+ size_t pos = safecat(message, sizeof message, 0, "colour type ");
+
+ pos = safecatn(message, sizeof message, pos, dp->output_colour_type);
+ pos = safecat(message, sizeof message, pos, " expected ");
+ pos = safecatn(message, sizeof message, pos, test_pixel.colour_type);
+
+ png_error(pp, message);
+ }
+
+ if (test_pixel.bit_depth != dp->output_bit_depth)
+ {
+ char message[128];
+ size_t pos = safecat(message, sizeof message, 0, "bit depth ");
+
+ pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+ pos = safecat(message, sizeof message, pos, " expected ");
+ pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+ png_error(pp, message);
+ }
+
+ /* If both bit depth and colour type are correct check the sample depth.
+ */
+ if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE &&
+ test_pixel.sample_depth != 8) /* oops - internal error! */
+ png_error(pp, "pngvalid: internal: palette sample depth not 8");
+ else if (dp->unpacked && test_pixel.bit_depth != 8)
+ png_error(pp, "pngvalid: internal: bad unpacked pixel depth");
+ else if (!dp->unpacked && test_pixel.colour_type != PNG_COLOR_TYPE_PALETTE
+ && test_pixel.bit_depth != test_pixel.sample_depth)
+ {
+ char message[128];
+ size_t pos = safecat(message, sizeof message, 0,
+ "internal: sample depth ");
+
+ /* Because unless something has set 'unpacked' or the image is palette
+ * mapped we expect the transform to keep sample depth and bit depth
+ * the same.
+ */
+ pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
+ pos = safecat(message, sizeof message, pos, " expected ");
+ pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+ png_error(pp, message);
+ }
+ else if (test_pixel.bit_depth != dp->output_bit_depth)
+ {
+ /* This could be a libpng error too; libpng has not produced what we
+ * expect for the output bit depth.
+ */
+ char message[128];
+ size_t pos = safecat(message, sizeof message, 0,
+ "internal: bit depth ");
+
+ pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+ pos = safecat(message, sizeof message, pos, " expected ");
+ pos = safecatn(message, sizeof message, pos, test_pixel.bit_depth);
+
+ png_error(pp, message);
+ }
+ }
+}
+
+static void PNGCBAPI
+transform_info(png_structp pp, png_infop pi)
+{
+ transform_info_imp(voidcast(transform_display*, png_get_progressive_ptr(pp)),
+ pp, pi);
+}
+
+static void
+transform_range_check(png_const_structp pp, unsigned int r, unsigned int g,
+ unsigned int b, unsigned int a, unsigned int in_digitized, double in,
+ unsigned int out, png_byte sample_depth, double err, double limit,
+ const char *name, double digitization_error)
+{
+ /* Compare the scaled, digitzed, values of our local calculation (in+-err)
+ * with the digitized values libpng produced; 'sample_depth' is the actual
+ * digitization depth of the libpng output colors (the bit depth except for
+ * palette images where it is always 8.) The check on 'err' is to detect
+ * internal errors in pngvalid itself.
+ */
+ unsigned int max = (1U<<sample_depth)-1;
+ double in_min = ceil((in-err)*max - digitization_error);
+ double in_max = floor((in+err)*max + digitization_error);
+ if (err > limit || !(out >= in_min && out <= in_max))
+ {
+ char message[256];
+ size_t pos;
+
+ pos = safecat(message, sizeof message, 0, name);
+ pos = safecat(message, sizeof message, pos, " output value error: rgba(");
+ pos = safecatn(message, sizeof message, pos, r);
+ pos = safecat(message, sizeof message, pos, ",");
+ pos = safecatn(message, sizeof message, pos, g);
+ pos = safecat(message, sizeof message, pos, ",");
+ pos = safecatn(message, sizeof message, pos, b);
+ pos = safecat(message, sizeof message, pos, ",");
+ pos = safecatn(message, sizeof message, pos, a);
+ pos = safecat(message, sizeof message, pos, "): ");
+ pos = safecatn(message, sizeof message, pos, out);
+ pos = safecat(message, sizeof message, pos, " expected: ");
+ pos = safecatn(message, sizeof message, pos, in_digitized);
+ pos = safecat(message, sizeof message, pos, " (");
+ pos = safecatd(message, sizeof message, pos, (in-err)*max, 3);
+ pos = safecat(message, sizeof message, pos, "..");
+ pos = safecatd(message, sizeof message, pos, (in+err)*max, 3);
+ pos = safecat(message, sizeof message, pos, ")");
+
+ png_error(pp, message);
+ }
+}
+
+static void
+transform_image_validate(transform_display *dp, png_const_structp pp,
+ png_infop pi)
+{
+ /* Constants for the loop below: */
+ const png_store* const ps = dp->this.ps;
+ const png_byte in_ct = dp->this.colour_type;
+ const png_byte in_bd = dp->this.bit_depth;
+ const png_uint_32 w = dp->this.w;
+ const png_uint_32 h = dp->this.h;
+ const png_byte out_ct = dp->output_colour_type;
+ const png_byte out_bd = dp->output_bit_depth;
+ const png_byte sample_depth = (png_byte)(out_ct ==
+ PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
+ const png_byte red_sBIT = dp->this.red_sBIT;
+ const png_byte green_sBIT = dp->this.green_sBIT;
+ const png_byte blue_sBIT = dp->this.blue_sBIT;
+ const png_byte alpha_sBIT = dp->this.alpha_sBIT;
+ const int have_tRNS = dp->this.is_transparent;
+ double digitization_error;
+
+ store_palette out_palette;
+ png_uint_32 y;
+
+ UNUSED(pi)
+
+ /* Check for row overwrite errors */
+ store_image_check(dp->this.ps, pp, 0);
+
+ /* Read the palette corresponding to the output if the output colour type
+ * indicates a palette, othewise set out_palette to garbage.
+ */
+ if (out_ct == PNG_COLOR_TYPE_PALETTE)
+ {
+ /* Validate that the palette count itself has not changed - this is not
+ * expected.
+ */
+ int npalette = (-1);
+
+ (void)read_palette(out_palette, &npalette, pp, pi);
+ if (npalette != dp->this.npalette)
+ png_error(pp, "unexpected change in palette size");
+
+ digitization_error = .5;
+ }
+ else
+ {
+ png_byte in_sample_depth;
+
+ memset(out_palette, 0x5e, sizeof out_palette);
+
+ /* use-input-precision means assume that if the input has 8 bit (or less)
+ * samples and the output has 16 bit samples the calculations will be done
+ * with 8 bit precision, not 16.
+ */
+ if (in_ct == PNG_COLOR_TYPE_PALETTE || in_bd < 16)
+ in_sample_depth = 8;
+ else
+ in_sample_depth = in_bd;
+
+ if (sample_depth != 16 || in_sample_depth > 8 ||
+ !dp->pm->calculations_use_input_precision)
+ digitization_error = .5;
+
+ /* Else calculations are at 8 bit precision, and the output actually
+ * consists of scaled 8-bit values, so scale .5 in 8 bits to the 16 bits:
+ */
+ else
+ digitization_error = .5 * 257;
+ }
+
+ for (y=0; y<h; ++y)
+ {
+ png_const_bytep const pRow = store_image_row(ps, pp, 0, y);
+ png_uint_32 x;
+
+ /* The original, standard, row pre-transforms. */
+ png_byte std[STANDARD_ROWMAX];
+
+ transform_row(pp, std, in_ct, in_bd, y);
+
+ /* Go through each original pixel transforming it and comparing with what
+ * libpng did to the same pixel.
+ */
+ for (x=0; x<w; ++x)
+ {
+ image_pixel in_pixel, out_pixel;
+ unsigned int r, g, b, a;
+
+ /* Find out what we think the pixel should be: */
+ image_pixel_init(&in_pixel, std, in_ct, in_bd, x, dp->this.palette,
+ NULL);
+
+ in_pixel.red_sBIT = red_sBIT;
+ in_pixel.green_sBIT = green_sBIT;
+ in_pixel.blue_sBIT = blue_sBIT;
+ in_pixel.alpha_sBIT = alpha_sBIT;
+ in_pixel.have_tRNS = have_tRNS != 0;
+
+ /* For error detection, below. */
+ r = in_pixel.red;
+ g = in_pixel.green;
+ b = in_pixel.blue;
+ a = in_pixel.alpha;
+
+ /* This applies the transforms to the input data, including output
+ * format operations which must be used when reading the output
+ * pixel that libpng produces.
+ */
+ dp->transform_list->mod(dp->transform_list, &in_pixel, pp, dp);
+
+ /* Read the output pixel and compare it to what we got, we don't
+ * use the error field here, so no need to update sBIT. in_pixel
+ * says whether we expect libpng to change the output format.
+ */
+ image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette,
+ &in_pixel);
+
+ /* We don't expect changes to the index here even if the bit depth is
+ * changed.
+ */
+ if (in_ct == PNG_COLOR_TYPE_PALETTE &&
+ out_ct == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (in_pixel.palette_index != out_pixel.palette_index)
+ png_error(pp, "unexpected transformed palette index");
+ }
+
+ /* Check the colours for palette images too - in fact the palette could
+ * be separately verified itself in most cases.
+ */
+ if (in_pixel.red != out_pixel.red)
+ transform_range_check(pp, r, g, b, a, in_pixel.red, in_pixel.redf,
+ out_pixel.red, sample_depth, in_pixel.rede,
+ dp->pm->limit + 1./(2*((1U<<in_pixel.red_sBIT)-1)), "red/gray",
+ digitization_error);
+
+ if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
+ in_pixel.green != out_pixel.green)
+ transform_range_check(pp, r, g, b, a, in_pixel.green,
+ in_pixel.greenf, out_pixel.green, sample_depth, in_pixel.greene,
+ dp->pm->limit + 1./(2*((1U<<in_pixel.green_sBIT)-1)), "green",
+ digitization_error);
+
+ if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 &&
+ in_pixel.blue != out_pixel.blue)
+ transform_range_check(pp, r, g, b, a, in_pixel.blue, in_pixel.bluef,
+ out_pixel.blue, sample_depth, in_pixel.bluee,
+ dp->pm->limit + 1./(2*((1U<<in_pixel.blue_sBIT)-1)), "blue",
+ digitization_error);
+
+ if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0 &&
+ in_pixel.alpha != out_pixel.alpha)
+ transform_range_check(pp, r, g, b, a, in_pixel.alpha,
+ in_pixel.alphaf, out_pixel.alpha, sample_depth, in_pixel.alphae,
+ dp->pm->limit + 1./(2*((1U<<in_pixel.alpha_sBIT)-1)), "alpha",
+ digitization_error);
+ } /* pixel (x) loop */
+ } /* row (y) loop */
+
+ /* Record that something was actually checked to avoid a false positive. */
+ dp->this.ps->validated = 1;
+}
+
+static void PNGCBAPI
+transform_end(png_structp ppIn, png_infop pi)
+{
+ png_const_structp pp = ppIn;
+ transform_display *dp = voidcast(transform_display*,
+ png_get_progressive_ptr(pp));
+
+ if (!dp->this.speed)
+ transform_image_validate(dp, pp, pi);
+ else
+ dp->this.ps->validated = 1;
+}
+
+/* A single test run. */
+static void
+transform_test(png_modifier *pmIn, const png_uint_32 idIn,
+ const image_transform* transform_listIn, const char * const name)
+{
+ transform_display d;
+ context(&pmIn->this, fault);
+
+ transform_display_init(&d, pmIn, idIn, transform_listIn);
+
+ Try
+ {
+ size_t pos = 0;
+ png_structp pp;
+ png_infop pi;
+ char full_name[256];
+
+ /* Make sure the encoding fields are correct and enter the required
+ * modifications.
+ */
+ transform_set_encoding(&d);
+
+ /* Add any modifications required by the transform list. */
+ d.transform_list->ini(d.transform_list, &d);
+
+ /* Add the color space information, if any, to the name. */
+ pos = safecat(full_name, sizeof full_name, pos, name);
+ pos = safecat_current_encoding(full_name, sizeof full_name, pos, d.pm);
+
+ /* Get a png_struct for reading the image. */
+ pp = set_modifier_for_read(d.pm, &pi, d.this.id, full_name);
+ standard_palette_init(&d.this);
+
+# if 0
+ /* Logging (debugging only) */
+ {
+ char buffer[256];
+
+ (void)store_message(&d.pm->this, pp, buffer, sizeof buffer, 0,
+ "running test");
+
+ fprintf(stderr, "%s\n", buffer);
+ }
+# endif
+
+ /* Introduce the correct read function. */
+ if (d.pm->this.progressive)
+ {
+ /* Share the row function with the standard implementation. */
+ png_set_progressive_read_fn(pp, &d, transform_info, progressive_row,
+ transform_end);
+
+ /* Now feed data into the reader until we reach the end: */
+ modifier_progressive_read(d.pm, pp, pi);
+ }
+ else
+ {
+ /* modifier_read expects a png_modifier* */
+ png_set_read_fn(pp, d.pm, modifier_read);
+
+ /* Check the header values: */
+ png_read_info(pp, pi);
+
+ /* Process the 'info' requirements. Only one image is generated */
+ transform_info_imp(&d, pp, pi);
+
+ sequential_row(&d.this, pp, pi, -1, 0);
+
+ if (!d.this.speed)
+ transform_image_validate(&d, pp, pi);
+ else
+ d.this.ps->validated = 1;
+ }
+
+ modifier_reset(d.pm);
+ }
+
+ Catch(fault)
+ {
+ modifier_reset(voidcast(png_modifier*,(void*)fault));
+ }
+}
+
+/* The transforms: */
+#define ITSTRUCT(name) image_transform_##name
+#define ITDATA(name) image_transform_data_##name
+#define image_transform_ini image_transform_default_ini
+#define IT(name)\
+static image_transform ITSTRUCT(name) =\
+{\
+ #name,\
+ 1, /*enable*/\
+ &PT, /*list*/\
+ 0, /*global_use*/\
+ 0, /*local_use*/\
+ 0, /*next*/\
+ image_transform_ini,\
+ image_transform_png_set_##name##_set,\
+ image_transform_png_set_##name##_mod,\
+ image_transform_png_set_##name##_add\
+}
+#define PT ITSTRUCT(end) /* stores the previous transform */
+
+/* To save code: */
+extern void image_transform_default_ini(const image_transform *this,
+ transform_display *that); /* silence GCC warnings */
+
+void /* private, but almost always needed */
+image_transform_default_ini(const image_transform *this,
+ transform_display *that)
+{
+ this->next->ini(this->next, that);
+}
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+static int
+image_transform_default_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return 1;
+}
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_palette_to_rgb */
+static void
+image_transform_png_set_palette_to_rgb_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_palette_to_rgb(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_palette_to_rgb_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_palette_to_rgb_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return colour_type == PNG_COLOR_TYPE_PALETTE;
+}
+
+IT(palette_to_rgb);
+#undef PT
+#define PT ITSTRUCT(palette_to_rgb)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_tRNS_to_alpha */
+static void
+image_transform_png_set_tRNS_to_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_tRNS_to_alpha(pp);
+
+ /* If there was a tRNS chunk that would get expanded and add an alpha
+ * channel is_transparent must be updated:
+ */
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_tRNS_to_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+#if PNG_LIBPNG_VER < 10700
+ /* LIBPNG BUG: this always forces palette images to RGB. */
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+#endif
+
+ /* This effectively does an 'expand' only if there is some transparency to
+ * convert to an alpha channel.
+ */
+ if (that->have_tRNS)
+# if PNG_LIBPNG_VER >= 10700
+ if (that->colour_type != PNG_COLOR_TYPE_PALETTE &&
+ (that->colour_type & PNG_COLOR_MASK_ALPHA) == 0)
+# endif
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+#if PNG_LIBPNG_VER < 10700
+ /* LIBPNG BUG: otherwise libpng still expands to 8 bits! */
+ else
+ {
+ if (that->bit_depth < 8)
+ that->bit_depth =8;
+ if (that->sample_depth < 8)
+ that->sample_depth = 8;
+ }
+#endif
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_tRNS_to_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* We don't know yet whether there will be a tRNS chunk, but we know that
+ * this transformation should do nothing if there already is an alpha
+ * channel. In addition, after the bug fix in 1.7.0, there is no longer
+ * any action on a palette image.
+ */
+ return
+# if PNG_LIBPNG_VER >= 10700
+ colour_type != PNG_COLOR_TYPE_PALETTE &&
+# endif
+ (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
+}
+
+IT(tRNS_to_alpha);
+#undef PT
+#define PT ITSTRUCT(tRNS_to_alpha)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+/* png_set_gray_to_rgb */
+static void
+image_transform_png_set_gray_to_rgb_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_gray_to_rgb(pp);
+ /* NOTE: this doesn't result in tRNS expansion. */
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_gray_to_rgb_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* NOTE: we can actually pend the tRNS processing at this point because we
+ * can correctly recognize the original pixel value even though we have
+ * mapped the one gray channel to the three RGB ones, but in fact libpng
+ * doesn't do this, so we don't either.
+ */
+ if ((that->colour_type & PNG_COLOR_MASK_COLOR) == 0 && that->have_tRNS)
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+ /* Simply expand the bit depth and alter the colour type as required. */
+ if (that->colour_type == PNG_COLOR_TYPE_GRAY)
+ {
+ /* RGB images have a bit depth at least equal to '8' */
+ if (that->bit_depth < 8)
+ that->sample_depth = that->bit_depth = 8;
+
+ /* And just changing the colour type works here because the green and blue
+ * channels are being maintained in lock-step with the red/gray:
+ */
+ that->colour_type = PNG_COLOR_TYPE_RGB;
+ }
+
+ else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_gray_to_rgb_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return (colour_type & PNG_COLOR_MASK_COLOR) == 0;
+}
+
+IT(gray_to_rgb);
+#undef PT
+#define PT ITSTRUCT(gray_to_rgb)
+#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_expand */
+static void
+image_transform_png_set_expand_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_expand(pp);
+
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* The general expand case depends on what the colour type is: */
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+ else if (that->bit_depth < 8) /* grayscale */
+ that->sample_depth = that->bit_depth = 8;
+
+ if (that->have_tRNS)
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* 'expand' should do nothing for RGBA or GA input - no tRNS and the bit
+ * depth is at least 8 already.
+ */
+ return (colour_type & PNG_COLOR_MASK_ALPHA) == 0;
+}
+
+IT(expand);
+#undef PT
+#define PT ITSTRUCT(expand)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_expand_gray_1_2_4_to_8
+ * Pre 1.7.0 LIBPNG BUG: this just does an 'expand'
+ */
+static void
+image_transform_png_set_expand_gray_1_2_4_to_8_set(
+ const image_transform *this, transform_display *that, png_structp pp,
+ png_infop pi)
+{
+ png_set_expand_gray_1_2_4_to_8(pp);
+ /* NOTE: don't expect this to expand tRNS */
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_gray_1_2_4_to_8_mod(
+ const image_transform *this, image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+#if PNG_LIBPNG_VER < 10700
+ image_transform_png_set_expand_mod(this, that, pp, display);
+#else
+ /* Only expand grayscale of bit depth less than 8: */
+ if (that->colour_type == PNG_COLOR_TYPE_GRAY &&
+ that->bit_depth < 8)
+ that->sample_depth = that->bit_depth = 8;
+
+ this->next->mod(this->next, that, pp, display);
+#endif /* 1.7 or later */
+}
+
+static int
+image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+#if PNG_LIBPNG_VER < 10700
+ return image_transform_png_set_expand_add(this, that, colour_type,
+ bit_depth);
+#else
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* This should do nothing unless the color type is gray and the bit depth is
+ * less than 8:
+ */
+ return colour_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8;
+#endif /* 1.7 or later */
+}
+
+IT(expand_gray_1_2_4_to_8);
+#undef PT
+#define PT ITSTRUCT(expand_gray_1_2_4_to_8)
+#endif /* PNG_READ_EXPAND_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* png_set_expand_16 */
+static void
+image_transform_png_set_expand_16_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_expand_16(pp);
+
+ /* NOTE: prior to 1.7 libpng does SET_EXPAND as well, so tRNS is expanded. */
+# if PNG_LIBPNG_VER < 10700
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+# endif
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_16_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* Expect expand_16 to expand everything to 16 bits as a result of also
+ * causing 'expand' to happen.
+ */
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+
+ if (that->have_tRNS)
+ image_pixel_add_alpha(that, &display->this, 0/*!for background*/);
+
+ if (that->bit_depth < 16)
+ that->sample_depth = that->bit_depth = 16;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_16_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ /* expand_16 does something unless the bit depth is already 16. */
+ return bit_depth < 16;
+}
+
+IT(expand_16);
+#undef PT
+#define PT ITSTRUCT(expand_16)
+#endif /* PNG_READ_EXPAND_16_SUPPORTED */
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED /* API added in 1.5.4 */
+/* png_set_scale_16 */
+static void
+image_transform_png_set_scale_16_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_scale_16(pp);
+# if PNG_LIBPNG_VER < 10700
+ /* libpng will limit the gamma table size: */
+ that->max_gamma_8 = PNG_MAX_GAMMA_8;
+# endif
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_scale_16_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth == 16)
+ {
+ that->sample_depth = that->bit_depth = 8;
+ if (that->red_sBIT > 8) that->red_sBIT = 8;
+ if (that->green_sBIT > 8) that->green_sBIT = 8;
+ if (that->blue_sBIT > 8) that->blue_sBIT = 8;
+ if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_scale_16_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ return bit_depth > 8;
+}
+
+IT(scale_16);
+#undef PT
+#define PT ITSTRUCT(scale_16)
+#endif /* PNG_READ_SCALE_16_TO_8_SUPPORTED (1.5.4 on) */
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED /* the default before 1.5.4 */
+/* png_set_strip_16 */
+static void
+image_transform_png_set_strip_16_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_strip_16(pp);
+# if PNG_LIBPNG_VER < 10700
+ /* libpng will limit the gamma table size: */
+ that->max_gamma_8 = PNG_MAX_GAMMA_8;
+# endif
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_strip_16_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth == 16)
+ {
+ that->sample_depth = that->bit_depth = 8;
+ if (that->red_sBIT > 8) that->red_sBIT = 8;
+ if (that->green_sBIT > 8) that->green_sBIT = 8;
+ if (that->blue_sBIT > 8) that->blue_sBIT = 8;
+ if (that->alpha_sBIT > 8) that->alpha_sBIT = 8;
+
+ /* Prior to 1.5.4 png_set_strip_16 would use an 'accurate' method if this
+ * configuration option is set. From 1.5.4 the flag is never set and the
+ * 'scale' API (above) must be used.
+ */
+# ifdef PNG_READ_ACCURATE_SCALE_SUPPORTED
+# if PNG_LIBPNG_VER >= 10504
+# error PNG_READ_ACCURATE_SCALE should not be set
+# endif
+
+ /* The strip 16 algorithm drops the low 8 bits rather than calculating
+ * 1/257, so we need to adjust the permitted errors appropriately:
+ * Notice that this is only relevant prior to the addition of the
+ * png_set_scale_16 API in 1.5.4 (but 1.5.4+ always defines the above!)
+ */
+ {
+ const double d = (255-128.5)/65535;
+ that->rede += d;
+ that->greene += d;
+ that->bluee += d;
+ that->alphae += d;
+ }
+# endif
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_strip_16_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ return bit_depth > 8;
+}
+
+IT(strip_16);
+#undef PT
+#define PT ITSTRUCT(strip_16)
+#endif /* PNG_READ_16_TO_8_SUPPORTED */
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+/* png_set_strip_alpha */
+static void
+image_transform_png_set_strip_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_strip_alpha(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_strip_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_GRAY;
+ else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_RGB;
+
+ that->have_tRNS = 0;
+ that->alphaf = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_strip_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return (colour_type & PNG_COLOR_MASK_ALPHA) != 0;
+}
+
+IT(strip_alpha);
+#undef PT
+#define PT ITSTRUCT(strip_alpha)
+#endif /* PNG_READ_STRIP_ALPHA_SUPPORTED */
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* png_set_rgb_to_gray(png_structp, int err_action, double red, double green)
+ * png_set_rgb_to_gray_fixed(png_structp, int err_action, png_fixed_point red,
+ * png_fixed_point green)
+ * png_get_rgb_to_gray_status
+ *
+ * The 'default' test here uses values known to be used inside libpng prior to
+ * 1.7.0:
+ *
+ * red: 6968
+ * green: 23434
+ * blue: 2366
+ *
+ * These values are being retained for compatibility, along with the somewhat
+ * broken truncation calculation in the fast-and-inaccurate code path. Older
+ * versions of libpng will fail the accuracy tests below because they use the
+ * truncation algorithm everywhere.
+ */
+#define data ITDATA(rgb_to_gray)
+static struct
+{
+ double gamma; /* File gamma to use in processing */
+
+ /* The following are the parameters for png_set_rgb_to_gray: */
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ double red_to_set;
+ double green_to_set;
+# else
+ png_fixed_point red_to_set;
+ png_fixed_point green_to_set;
+# endif
+
+ /* The actual coefficients: */
+ double red_coefficient;
+ double green_coefficient;
+ double blue_coefficient;
+
+ /* Set if the coeefficients have been overridden. */
+ int coefficients_overridden;
+} data;
+
+#undef image_transform_ini
+#define image_transform_ini image_transform_png_set_rgb_to_gray_ini
+static void
+image_transform_png_set_rgb_to_gray_ini(const image_transform *this,
+ transform_display *that)
+{
+ png_modifier *pm = that->pm;
+ const color_encoding *e = pm->current_encoding;
+
+ UNUSED(this)
+
+ /* Since we check the encoding this flag must be set: */
+ pm->test_uses_encoding = 1;
+
+ /* If 'e' is not NULL chromaticity information is present and either a cHRM
+ * or an sRGB chunk will be inserted.
+ */
+ if (e != 0)
+ {
+ /* Coefficients come from the encoding, but may need to be normalized to a
+ * white point Y of 1.0
+ */
+ const double whiteY = e->red.Y + e->green.Y + e->blue.Y;
+
+ data.red_coefficient = e->red.Y;
+ data.green_coefficient = e->green.Y;
+ data.blue_coefficient = e->blue.Y;
+
+ if (whiteY != 1)
+ {
+ data.red_coefficient /= whiteY;
+ data.green_coefficient /= whiteY;
+ data.blue_coefficient /= whiteY;
+ }
+ }
+
+ else
+ {
+ /* The default (built in) coeffcients, as above: */
+# if PNG_LIBPNG_VER < 10700
+ data.red_coefficient = 6968 / 32768.;
+ data.green_coefficient = 23434 / 32768.;
+ data.blue_coefficient = 2366 / 32768.;
+# else
+ data.red_coefficient = .2126;
+ data.green_coefficient = .7152;
+ data.blue_coefficient = .0722;
+# endif
+ }
+
+ data.gamma = pm->current_gamma;
+
+ /* If not set then the calculations assume linear encoding (implicitly): */
+ if (data.gamma == 0)
+ data.gamma = 1;
+
+ /* The arguments to png_set_rgb_to_gray can override the coefficients implied
+ * by the color space encoding. If doing exhaustive checks do the override
+ * in each case, otherwise do it randomly.
+ */
+ if (pm->test_exhaustive)
+ {
+ /* First time in coefficients_overridden is 0, the following sets it to 1,
+ * so repeat if it is set. If a test fails this may mean we subsequently
+ * skip a non-override test, ignore that.
+ */
+ data.coefficients_overridden = !data.coefficients_overridden;
+ pm->repeat = data.coefficients_overridden != 0;
+ }
+
+ else
+ data.coefficients_overridden = random_choice();
+
+ if (data.coefficients_overridden)
+ {
+ /* These values override the color encoding defaults, simply use random
+ * numbers.
+ */
+ png_uint_32 ru;
+ double total;
+
+ RANDOMIZE(ru);
+ data.green_coefficient = total = (ru & 0xffff) / 65535.;
+ ru >>= 16;
+ data.red_coefficient = (1 - total) * (ru & 0xffff) / 65535.;
+ total += data.red_coefficient;
+ data.blue_coefficient = 1 - total;
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ data.red_to_set = data.red_coefficient;
+ data.green_to_set = data.green_coefficient;
+# else
+ data.red_to_set = fix(data.red_coefficient);
+ data.green_to_set = fix(data.green_coefficient);
+# endif
+
+ /* The following just changes the error messages: */
+ pm->encoding_ignored = 1;
+ }
+
+ else
+ {
+ data.red_to_set = -1;
+ data.green_to_set = -1;
+ }
+
+ /* Adjust the error limit in the png_modifier because of the larger errors
+ * produced in the digitization during the gamma handling.
+ */
+ if (data.gamma != 1) /* Use gamma tables */
+ {
+ if (that->this.bit_depth == 16 || pm->assume_16_bit_calculations)
+ {
+ /* The computations have the form:
+ *
+ * r * rc + g * gc + b * bc
+ *
+ * Each component of which is +/-1/65535 from the gamma_to_1 table
+ * lookup, resulting in a base error of +/-6. The gamma_from_1
+ * conversion adds another +/-2 in the 16-bit case and
+ * +/-(1<<(15-PNG_MAX_GAMMA_8)) in the 8-bit case.
+ */
+# if PNG_LIBPNG_VER < 10700
+ if (that->this.bit_depth < 16)
+ that->max_gamma_8 = PNG_MAX_GAMMA_8;
+# endif
+ that->pm->limit += pow(
+ (that->this.bit_depth == 16 || that->max_gamma_8 > 14 ?
+ 8. :
+ 6. + (1<<(15-that->max_gamma_8))
+ )/65535, data.gamma);
+ }
+
+ else
+ {
+ /* Rounding to 8 bits in the linear space causes massive errors which
+ * will trigger the error check in transform_range_check. Fix that
+ * here by taking the gamma encoding into account.
+ *
+ * When DIGITIZE is set because a pre-1.7 version of libpng is being
+ * tested allow a bigger slack.
+ *
+ * NOTE: this magic number was determined by experiment to be about
+ * 1.263. There's no great merit to the value below, however it only
+ * affects the limit used for checking for internal calculation errors,
+ * not the actual limit imposed by pngvalid on the output errors.
+ */
+ that->pm->limit += pow(
+# if DIGITIZE
+ 1.3
+# else
+ 1.0
+# endif
+ /255, data.gamma);
+ }
+ }
+
+ else
+ {
+ /* With no gamma correction a large error comes from the truncation of the
+ * calculation in the 8 bit case, allow for that here.
+ */
+ if (that->this.bit_depth != 16 && !pm->assume_16_bit_calculations)
+ that->pm->limit += 4E-3;
+ }
+}
+
+static void
+image_transform_png_set_rgb_to_gray_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ const int error_action = 1; /* no error, no defines in png.h */
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_rgb_to_gray(pp, error_action, data.red_to_set, data.green_to_set);
+# else
+ png_set_rgb_to_gray_fixed(pp, error_action, data.red_to_set,
+ data.green_to_set);
+# endif
+
+# ifdef PNG_READ_cHRM_SUPPORTED
+ if (that->pm->current_encoding != 0)
+ {
+ /* We have an encoding so a cHRM chunk may have been set; if so then
+ * check that the libpng APIs give the correct (X,Y,Z) values within
+ * some margin of error for the round trip through the chromaticity
+ * form.
+ */
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+# define API_function png_get_cHRM_XYZ
+# define API_form "FP"
+# define API_type double
+# define API_cvt(x) (x)
+# else
+# define API_function png_get_cHRM_XYZ_fixed
+# define API_form "fixed"
+# define API_type png_fixed_point
+# define API_cvt(x) ((double)(x)/PNG_FP_1)
+# endif
+
+ API_type rX, gX, bX;
+ API_type rY, gY, bY;
+ API_type rZ, gZ, bZ;
+
+ if ((API_function(pp, pi, &rX, &rY, &rZ, &gX, &gY, &gZ, &bX, &bY, &bZ)
+ & PNG_INFO_cHRM) != 0)
+ {
+ double maxe;
+ const char *el;
+ color_encoding e, o;
+
+ /* Expect libpng to return a normalized result, but the original
+ * color space encoding may not be normalized.
+ */
+ modifier_current_encoding(that->pm, &o);
+ normalize_color_encoding(&o);
+
+ /* Sanity check the pngvalid code - the coefficients should match
+ * the normalized Y values of the encoding unless they were
+ * overridden.
+ */
+ if (data.red_to_set == -1 && data.green_to_set == -1 &&
+ (fabs(o.red.Y - data.red_coefficient) > DBL_EPSILON ||
+ fabs(o.green.Y - data.green_coefficient) > DBL_EPSILON ||
+ fabs(o.blue.Y - data.blue_coefficient) > DBL_EPSILON))
+ png_error(pp, "internal pngvalid cHRM coefficient error");
+
+ /* Generate a colour space encoding. */
+ e.gamma = o.gamma; /* not used */
+ e.red.X = API_cvt(rX);
+ e.red.Y = API_cvt(rY);
+ e.red.Z = API_cvt(rZ);
+ e.green.X = API_cvt(gX);
+ e.green.Y = API_cvt(gY);
+ e.green.Z = API_cvt(gZ);
+ e.blue.X = API_cvt(bX);
+ e.blue.Y = API_cvt(bY);
+ e.blue.Z = API_cvt(bZ);
+
+ /* This should match the original one from the png_modifier, within
+ * the range permitted by the libpng fixed point representation.
+ */
+ maxe = 0;
+ el = "-"; /* Set to element name with error */
+
+# define CHECK(col,x)\
+ {\
+ double err = fabs(o.col.x - e.col.x);\
+ if (err > maxe)\
+ {\
+ maxe = err;\
+ el = #col "(" #x ")";\
+ }\
+ }
+
+ CHECK(red,X)
+ CHECK(red,Y)
+ CHECK(red,Z)
+ CHECK(green,X)
+ CHECK(green,Y)
+ CHECK(green,Z)
+ CHECK(blue,X)
+ CHECK(blue,Y)
+ CHECK(blue,Z)
+
+ /* Here in both fixed and floating cases to check the values read
+ * from the cHRm chunk. PNG uses fixed point in the cHRM chunk, so
+ * we can't expect better than +/-.5E-5 on the result, allow 1E-5.
+ */
+ if (maxe >= 1E-5)
+ {
+ size_t pos = 0;
+ char buffer[256];
+
+ pos = safecat(buffer, sizeof buffer, pos, API_form);
+ pos = safecat(buffer, sizeof buffer, pos, " cHRM ");
+ pos = safecat(buffer, sizeof buffer, pos, el);
+ pos = safecat(buffer, sizeof buffer, pos, " error: ");
+ pos = safecatd(buffer, sizeof buffer, pos, maxe, 7);
+ pos = safecat(buffer, sizeof buffer, pos, " ");
+ /* Print the color space without the gamma value: */
+ pos = safecat_color_encoding(buffer, sizeof buffer, pos, &o, 0);
+ pos = safecat(buffer, sizeof buffer, pos, " -> ");
+ pos = safecat_color_encoding(buffer, sizeof buffer, pos, &e, 0);
+
+ png_error(pp, buffer);
+ }
+ }
+ }
+# endif /* READ_cHRM */
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_rgb_to_gray_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
+ {
+ double gray, err;
+
+# if PNG_LIBPNG_VER < 10700
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+# endif
+
+ /* Image now has RGB channels... */
+# if DIGITIZE
+ {
+ const png_modifier *pm = display->pm;
+ const unsigned int sample_depth = that->sample_depth;
+ const unsigned int calc_depth = (pm->assume_16_bit_calculations ? 16 :
+ sample_depth);
+ const unsigned int gamma_depth =
+ (sample_depth == 16 ?
+ display->max_gamma_8 :
+ (pm->assume_16_bit_calculations ?
+ display->max_gamma_8 :
+ sample_depth));
+ int isgray;
+ double r, g, b;
+ double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi;
+
+ /* Do this using interval arithmetic, otherwise it is too difficult to
+ * handle the errors correctly.
+ *
+ * To handle the gamma correction work out the upper and lower bounds
+ * of the digitized value. Assume rounding here - normally the values
+ * will be identical after this operation if there is only one
+ * transform, feel free to delete the png_error checks on this below in
+ * the future (this is just me trying to ensure it works!)
+ *
+ * Interval arithmetic is exact, but to implement it it must be
+ * possible to control the floating point implementation rounding mode.
+ * This cannot be done in ANSI-C, so instead I reduce the 'lo' values
+ * by DBL_EPSILON and increase the 'hi' values by the same.
+ */
+# define DD(v,d,r) (digitize(v*(1-DBL_EPSILON), d, r) * (1-DBL_EPSILON))
+# define DU(v,d,r) (digitize(v*(1+DBL_EPSILON), d, r) * (1+DBL_EPSILON))
+
+ r = rlo = rhi = that->redf;
+ rlo -= that->rede;
+ rlo = DD(rlo, calc_depth, 1/*round*/);
+ rhi += that->rede;
+ rhi = DU(rhi, calc_depth, 1/*round*/);
+
+ g = glo = ghi = that->greenf;
+ glo -= that->greene;
+ glo = DD(glo, calc_depth, 1/*round*/);
+ ghi += that->greene;
+ ghi = DU(ghi, calc_depth, 1/*round*/);
+
+ b = blo = bhi = that->bluef;
+ blo -= that->bluee;
+ blo = DD(blo, calc_depth, 1/*round*/);
+ bhi += that->bluee;
+ bhi = DU(bhi, calc_depth, 1/*round*/);
+
+ isgray = r==g && g==b;
+
+ if (data.gamma != 1)
+ {
+ const double power = 1/data.gamma;
+ const double abse = .5/(sample_depth == 16 ? 65535 : 255);
+
+ /* If a gamma calculation is done it is done using lookup tables of
+ * precision gamma_depth, so the already digitized value above may
+ * need to be further digitized here.
+ */
+ if (gamma_depth != calc_depth)
+ {
+ rlo = DD(rlo, gamma_depth, 0/*truncate*/);
+ rhi = DU(rhi, gamma_depth, 0/*truncate*/);
+ glo = DD(glo, gamma_depth, 0/*truncate*/);
+ ghi = DU(ghi, gamma_depth, 0/*truncate*/);
+ blo = DD(blo, gamma_depth, 0/*truncate*/);
+ bhi = DU(bhi, gamma_depth, 0/*truncate*/);
+ }
+
+ /* 'abse' is the error in the gamma table calculation itself. */
+ r = pow(r, power);
+ rlo = DD(pow(rlo, power)-abse, calc_depth, 1);
+ rhi = DU(pow(rhi, power)+abse, calc_depth, 1);
+
+ g = pow(g, power);
+ glo = DD(pow(glo, power)-abse, calc_depth, 1);
+ ghi = DU(pow(ghi, power)+abse, calc_depth, 1);
+
+ b = pow(b, power);
+ blo = DD(pow(blo, power)-abse, calc_depth, 1);
+ bhi = DU(pow(bhi, power)+abse, calc_depth, 1);
+ }
+
+ /* Now calculate the actual gray values. Although the error in the
+ * coefficients depends on whether they were specified on the command
+ * line (in which case truncation to 15 bits happened) or not (rounding
+ * was used) the maxium error in an individual coefficient is always
+ * 2/32768, because even in the rounding case the requirement that
+ * coefficients add up to 32768 can cause a larger rounding error.
+ *
+ * The only time when rounding doesn't occur in 1.5.5 and later is when
+ * the non-gamma code path is used for less than 16 bit data.
+ */
+ gray = r * data.red_coefficient + g * data.green_coefficient +
+ b * data.blue_coefficient;
+
+ {
+ const int do_round = data.gamma != 1 || calc_depth == 16;
+ const double ce = 2. / 32768;
+
+ graylo = DD(rlo * (data.red_coefficient-ce) +
+ glo * (data.green_coefficient-ce) +
+ blo * (data.blue_coefficient-ce), calc_depth, do_round);
+ if (graylo > gray) /* always accept the right answer */
+ graylo = gray;
+
+ grayhi = DU(rhi * (data.red_coefficient+ce) +
+ ghi * (data.green_coefficient+ce) +
+ bhi * (data.blue_coefficient+ce), calc_depth, do_round);
+ if (grayhi < gray)
+ grayhi = gray;
+ }
+
+ /* And invert the gamma. */
+ if (data.gamma != 1)
+ {
+ const double power = data.gamma;
+
+ /* And this happens yet again, shifting the values once more. */
+ if (gamma_depth != sample_depth)
+ {
+ rlo = DD(rlo, gamma_depth, 0/*truncate*/);
+ rhi = DU(rhi, gamma_depth, 0/*truncate*/);
+ glo = DD(glo, gamma_depth, 0/*truncate*/);
+ ghi = DU(ghi, gamma_depth, 0/*truncate*/);
+ blo = DD(blo, gamma_depth, 0/*truncate*/);
+ bhi = DU(bhi, gamma_depth, 0/*truncate*/);
+ }
+
+ gray = pow(gray, power);
+ graylo = DD(pow(graylo, power), sample_depth, 1);
+ grayhi = DU(pow(grayhi, power), sample_depth, 1);
+ }
+
+# undef DD
+# undef DU
+
+ /* Now the error can be calculated.
+ *
+ * If r==g==b because there is no overall gamma correction libpng
+ * currently preserves the original value.
+ */
+ if (isgray)
+ err = (that->rede + that->greene + that->bluee)/3;
+
+ else
+ {
+ err = fabs(grayhi-gray);
+ if (fabs(gray - graylo) > err)
+ err = fabs(graylo-gray);
+
+ /* Check that this worked: */
+ if (err > pm->limit)
+ {
+ size_t pos = 0;
+ char buffer[128];
+
+ pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error ");
+ pos = safecatd(buffer, sizeof buffer, pos, err, 6);
+ pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
+ pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
+ png_error(pp, buffer);
+ }
+ }
+ }
+# else /* DIGITIZE */
+ {
+ double r = that->redf;
+ double re = that->rede;
+ double g = that->greenf;
+ double ge = that->greene;
+ double b = that->bluef;
+ double be = that->bluee;
+
+# if PNG_LIBPNG_VER < 10700
+ /* The true gray case involves no math in earlier versions (not
+ * true, there was some if gamma correction was happening too.)
+ */
+ if (r == g && r == b)
+ {
+ gray = r;
+ err = re;
+ if (err < ge) err = ge;
+ if (err < be) err = be;
+ }
+
+ else
+# endif /* before 1.7 */
+ if (data.gamma == 1)
+ {
+ /* There is no need to do the conversions to and from linear space,
+ * so the calculation should be a lot more accurate. There is a
+ * built in error in the coefficients because they only have 15 bits
+ * and are adjusted to make sure they add up to 32768. This
+ * involves a integer calculation with truncation of the form:
+ *
+ * ((int)(coefficient * 100000) * 32768)/100000
+ *
+ * This is done to the red and green coefficients (the ones
+ * provided to the API) then blue is calculated from them so the
+ * result adds up to 32768. In the worst case this can result in
+ * a -1 error in red and green and a +2 error in blue. Consequently
+ * the worst case in the calculation below is 2/32768 error.
+ *
+ * TODO: consider fixing this in libpng by rounding the calculation
+ * limiting the error to 1/32768.
+ *
+ * Handling this by adding 2/32768 here avoids needing to increase
+ * the global error limits to take this into account.)
+ */
+ gray = r * data.red_coefficient + g * data.green_coefficient +
+ b * data.blue_coefficient;
+ err = re * data.red_coefficient + ge * data.green_coefficient +
+ be * data.blue_coefficient + 2./32768 + gray * 5 * DBL_EPSILON;
+ }
+
+ else
+ {
+ /* The calculation happens in linear space, and this produces much
+ * wider errors in the encoded space. These are handled here by
+ * factoring the errors in to the calculation. There are two table
+ * lookups in the calculation and each introduces a quantization
+ * error defined by the table size.
+ */
+ const png_modifier *pm = display->pm;
+ double in_qe = (that->sample_depth > 8 ? .5/65535 : .5/255);
+ double out_qe = (that->sample_depth > 8 ? .5/65535 :
+ (pm->assume_16_bit_calculations ? .5/(1<<display->max_gamma_8) :
+ .5/255));
+ double rhi, ghi, bhi, grayhi;
+ double g1 = 1/data.gamma;
+
+ rhi = r + re + in_qe; if (rhi > 1) rhi = 1;
+ r -= re + in_qe; if (r < 0) r = 0;
+ ghi = g + ge + in_qe; if (ghi > 1) ghi = 1;
+ g -= ge + in_qe; if (g < 0) g = 0;
+ bhi = b + be + in_qe; if (bhi > 1) bhi = 1;
+ b -= be + in_qe; if (b < 0) b = 0;
+
+ r = pow(r, g1)*(1-DBL_EPSILON); rhi = pow(rhi, g1)*(1+DBL_EPSILON);
+ g = pow(g, g1)*(1-DBL_EPSILON); ghi = pow(ghi, g1)*(1+DBL_EPSILON);
+ b = pow(b, g1)*(1-DBL_EPSILON); bhi = pow(bhi, g1)*(1+DBL_EPSILON);
+
+ /* Work out the lower and upper bounds for the gray value in the
+ * encoded space, then work out an average and error. Remove the
+ * previously added input quantization error at this point.
+ */
+ gray = r * data.red_coefficient + g * data.green_coefficient +
+ b * data.blue_coefficient - 2./32768 - out_qe;
+ if (gray <= 0)
+ gray = 0;
+ else
+ {
+ gray *= (1 - 6 * DBL_EPSILON);
+ gray = pow(gray, data.gamma) * (1-DBL_EPSILON);
+ }
+
+ grayhi = rhi * data.red_coefficient + ghi * data.green_coefficient +
+ bhi * data.blue_coefficient + 2./32768 + out_qe;
+ grayhi *= (1 + 6 * DBL_EPSILON);
+ if (grayhi >= 1)
+ grayhi = 1;
+ else
+ grayhi = pow(grayhi, data.gamma) * (1+DBL_EPSILON);
+
+ err = (grayhi - gray) / 2;
+ gray = (grayhi + gray) / 2;
+
+ if (err <= in_qe)
+ err = gray * DBL_EPSILON;
+
+ else
+ err -= in_qe;
+
+ /* Validate that the error is within limits (this has caused
+ * problems before, it's much easier to detect them here.)
+ */
+ if (err > pm->limit)
+ {
+ size_t pos = 0;
+ char buffer[128];
+
+ pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error ");
+ pos = safecatd(buffer, sizeof buffer, pos, err, 6);
+ pos = safecat(buffer, sizeof buffer, pos, " exceeds limit ");
+ pos = safecatd(buffer, sizeof buffer, pos, pm->limit, 6);
+ png_error(pp, buffer);
+ }
+ }
+ }
+# endif /* !DIGITIZE */
+
+ that->bluef = that->greenf = that->redf = gray;
+ that->bluee = that->greene = that->rede = err;
+
+ /* The sBIT is the minium of the three colour channel sBITs. */
+ if (that->red_sBIT > that->green_sBIT)
+ that->red_sBIT = that->green_sBIT;
+ if (that->red_sBIT > that->blue_sBIT)
+ that->red_sBIT = that->blue_sBIT;
+ that->blue_sBIT = that->green_sBIT = that->red_sBIT;
+
+ /* And remove the colour bit in the type: */
+ if (that->colour_type == PNG_COLOR_TYPE_RGB)
+ that->colour_type = PNG_COLOR_TYPE_GRAY;
+ else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_rgb_to_gray_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return (colour_type & PNG_COLOR_MASK_COLOR) != 0;
+}
+
+#undef data
+IT(rgb_to_gray);
+#undef PT
+#define PT ITSTRUCT(rgb_to_gray)
+#undef image_transform_ini
+#define image_transform_ini image_transform_default_ini
+#endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* png_set_background(png_structp, png_const_color_16p background_color,
+ * int background_gamma_code, int need_expand, double background_gamma)
+ * png_set_background_fixed(png_structp, png_const_color_16p background_color,
+ * int background_gamma_code, int need_expand,
+ * png_fixed_point background_gamma)
+ *
+ * This ignores the gamma (at present.)
+*/
+#define data ITDATA(background)
+static image_pixel data;
+
+static void
+image_transform_png_set_background_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_byte colour_type, bit_depth;
+ png_byte random_bytes[8]; /* 8 bytes - 64 bits - the biggest pixel */
+ int expand;
+ png_color_16 back;
+
+ /* We need a background colour, because we don't know exactly what transforms
+ * have been set we have to supply the colour in the original file format and
+ * so we need to know what that is! The background colour is stored in the
+ * transform_display.
+ */
+ RANDOMIZE(random_bytes);
+
+ /* Read the random value, for colour type 3 the background colour is actually
+ * expressed as a 24bit rgb, not an index.
+ */
+ colour_type = that->this.colour_type;
+ if (colour_type == 3)
+ {
+ colour_type = PNG_COLOR_TYPE_RGB;
+ bit_depth = 8;
+ expand = 0; /* passing in an RGB not a pixel index */
+ }
+
+ else
+ {
+ if (that->this.has_tRNS)
+ that->this.is_transparent = 1;
+
+ bit_depth = that->this.bit_depth;
+ expand = 1;
+ }
+
+ image_pixel_init(&data, random_bytes, colour_type,
+ bit_depth, 0/*x*/, 0/*unused: palette*/, NULL/*format*/);
+
+ /* Extract the background colour from this image_pixel, but make sure the
+ * unused fields of 'back' are garbage.
+ */
+ RANDOMIZE(back);
+
+ if (colour_type & PNG_COLOR_MASK_COLOR)
+ {
+ back.red = (png_uint_16)data.red;
+ back.green = (png_uint_16)data.green;
+ back.blue = (png_uint_16)data.blue;
+ }
+
+ else
+ back.gray = (png_uint_16)data.red;
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
+# else
+ png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE, expand, 0);
+# endif
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_background_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* Check for tRNS first: */
+ if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
+ image_pixel_add_alpha(that, &display->this, 1/*for background*/);
+
+ /* This is only necessary if the alpha value is less than 1. */
+ if (that->alphaf < 1)
+ {
+ /* Now we do the background calculation without any gamma correction. */
+ if (that->alphaf <= 0)
+ {
+ that->redf = data.redf;
+ that->greenf = data.greenf;
+ that->bluef = data.bluef;
+
+ that->rede = data.rede;
+ that->greene = data.greene;
+ that->bluee = data.bluee;
+
+ that->red_sBIT= data.red_sBIT;
+ that->green_sBIT= data.green_sBIT;
+ that->blue_sBIT= data.blue_sBIT;
+ }
+
+ else /* 0 < alpha < 1 */
+ {
+ double alf = 1 - that->alphaf;
+
+ that->redf = that->redf * that->alphaf + data.redf * alf;
+ that->rede = that->rede * that->alphaf + data.rede * alf +
+ DBL_EPSILON;
+ that->greenf = that->greenf * that->alphaf + data.greenf * alf;
+ that->greene = that->greene * that->alphaf + data.greene * alf +
+ DBL_EPSILON;
+ that->bluef = that->bluef * that->alphaf + data.bluef * alf;
+ that->bluee = that->bluee * that->alphaf + data.bluee * alf +
+ DBL_EPSILON;
+ }
+
+ /* Remove the alpha type and set the alpha (not in that order.) */
+ that->alphaf = 1;
+ that->alphae = 0;
+ }
+
+ if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_RGB;
+ else if (that->colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ that->colour_type = PNG_COLOR_TYPE_GRAY;
+ /* PNG_COLOR_TYPE_PALETTE is not changed */
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+#define image_transform_png_set_background_add image_transform_default_add
+
+#undef data
+IT(background);
+#undef PT
+#define PT ITSTRUCT(background)
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
+ * int maximum_colors, png_const_uint_16p histogram, int full_quantize)
+ *
+ * Very difficult to validate this!
+ */
+/*NOTE: TBD NYI */
+
+/* The data layout transforms are handled by swapping our own channel data,
+ * necessarily these need to happen at the end of the transform list because the
+ * semantic of the channels changes after these are executed. Some of these,
+ * like set_shift and set_packing, can't be done at present because they change
+ * the layout of the data at the sub-sample level so sample() won't get the
+ * right answer.
+ */
+/* png_set_invert_alpha */
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+/* Invert the alpha channel
+ *
+ * png_set_invert_alpha(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_invert_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_invert_alpha(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_invert_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type & 4)
+ that->alpha_inverted = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_invert_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* Only has an effect on pixels with alpha: */
+ return (colour_type & 4) != 0;
+}
+
+IT(invert_alpha);
+#undef PT
+#define PT ITSTRUCT(invert_alpha)
+
+#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */
+
+/* png_set_bgr */
+#ifdef PNG_READ_BGR_SUPPORTED
+/* Swap R,G,B channels to order B,G,R.
+ *
+ * png_set_bgr(png_structrp png_ptr)
+ *
+ * This only has an effect on RGB and RGBA pixels.
+ */
+static void
+image_transform_png_set_bgr_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_bgr(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_bgr_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type == PNG_COLOR_TYPE_RGB ||
+ that->colour_type == PNG_COLOR_TYPE_RGBA)
+ that->swap_rgb = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_bgr_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return colour_type == PNG_COLOR_TYPE_RGB ||
+ colour_type == PNG_COLOR_TYPE_RGBA;
+}
+
+IT(bgr);
+#undef PT
+#define PT ITSTRUCT(bgr)
+
+#endif /* PNG_READ_BGR_SUPPORTED */
+
+/* png_set_swap_alpha */
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+/* Put the alpha channel first.
+ *
+ * png_set_swap_alpha(png_structrp png_ptr)
+ *
+ * This only has an effect on GA and RGBA pixels.
+ */
+static void
+image_transform_png_set_swap_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_swap_alpha(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_swap_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type == PNG_COLOR_TYPE_GA ||
+ that->colour_type == PNG_COLOR_TYPE_RGBA)
+ that->alpha_first = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_swap_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return colour_type == PNG_COLOR_TYPE_GA ||
+ colour_type == PNG_COLOR_TYPE_RGBA;
+}
+
+IT(swap_alpha);
+#undef PT
+#define PT ITSTRUCT(swap_alpha)
+
+#endif /* PNG_READ_SWAP_ALPHA_SUPPORTED */
+
+/* png_set_swap */
+#ifdef PNG_READ_SWAP_SUPPORTED
+/* Byte swap 16-bit components.
+ *
+ * png_set_swap(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_swap_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_swap(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_swap_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth == 16)
+ that->swap16 = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_swap_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ return bit_depth == 16;
+}
+
+IT(swap);
+#undef PT
+#define PT ITSTRUCT(swap)
+
+#endif /* PNG_READ_SWAP_SUPPORTED */
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images.
+ *
+ * png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags));
+ *
+ * Flags:
+ *
+ * PNG_FILLER_BEFORE
+ * PNG_FILLER_AFTER
+ */
+#define data ITDATA(filler)
+static struct
+{
+ png_uint_32 filler;
+ int flags;
+} data;
+
+static void
+image_transform_png_set_filler_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ /* Need a random choice for 'before' and 'after' as well as for the
+ * filler. The 'filler' value has all 32 bits set, but only bit_depth
+ * will be used. At this point we don't know bit_depth.
+ */
+ RANDOMIZE(data.filler);
+ data.flags = random_choice();
+
+ png_set_filler(pp, data.filler, data.flags);
+
+ /* The standard display handling stuff also needs to know that
+ * there is a filler, so set that here.
+ */
+ that->this.filler = 1;
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_filler_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth >= 8 &&
+ (that->colour_type == PNG_COLOR_TYPE_RGB ||
+ that->colour_type == PNG_COLOR_TYPE_GRAY))
+ {
+ const unsigned int max = (1U << that->bit_depth)-1;
+ that->alpha = data.filler & max;
+ that->alphaf = ((double)that->alpha) / max;
+ that->alphae = 0;
+
+ /* The filler has been stored in the alpha channel, we must record
+ * that this has been done for the checking later on, the color
+ * type is faked to have an alpha channel, but libpng won't report
+ * this; the app has to know the extra channel is there and this
+ * was recording in standard_display::filler above.
+ */
+ that->colour_type |= 4; /* alpha added */
+ that->alpha_first = data.flags == PNG_FILLER_BEFORE;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_filler_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB ||
+ colour_type == PNG_COLOR_TYPE_GRAY);
+}
+
+#undef data
+IT(filler);
+#undef PT
+#define PT ITSTRUCT(filler)
+
+/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+#define data ITDATA(add_alpha)
+static struct
+{
+ png_uint_32 filler;
+ int flags;
+} data;
+
+static void
+image_transform_png_set_add_alpha_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ /* Need a random choice for 'before' and 'after' as well as for the
+ * filler. The 'filler' value has all 32 bits set, but only bit_depth
+ * will be used. At this point we don't know bit_depth.
+ */
+ RANDOMIZE(data.filler);
+ data.flags = random_choice();
+
+ png_set_add_alpha(pp, data.filler, data.flags);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_add_alpha_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth >= 8 &&
+ (that->colour_type == PNG_COLOR_TYPE_RGB ||
+ that->colour_type == PNG_COLOR_TYPE_GRAY))
+ {
+ const unsigned int max = (1U << that->bit_depth)-1;
+ that->alpha = data.filler & max;
+ that->alphaf = ((double)that->alpha) / max;
+ that->alphae = 0;
+
+ that->colour_type |= 4; /* alpha added */
+ that->alpha_first = data.flags == PNG_FILLER_BEFORE;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_add_alpha_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ return bit_depth >= 8 && (colour_type == PNG_COLOR_TYPE_RGB ||
+ colour_type == PNG_COLOR_TYPE_GRAY);
+}
+
+#undef data
+IT(add_alpha);
+#undef PT
+#define PT ITSTRUCT(add_alpha)
+
+#endif /* PNG_READ_FILLER_SUPPORTED */
+
+/* png_set_packing */
+#ifdef PNG_READ_PACK_SUPPORTED
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files.
+ *
+ * png_set_packing(png_structrp png_ptr)
+ *
+ * This should only affect grayscale and palette images with less than 8 bits
+ * per pixel.
+ */
+static void
+image_transform_png_set_packing_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_packing(pp);
+ that->unpacked = 1;
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_packing_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* The general expand case depends on what the colour type is,
+ * low bit-depth pixel values are unpacked into bytes without
+ * scaling, so sample_depth is not changed.
+ */
+ if (that->bit_depth < 8) /* grayscale or palette */
+ that->bit_depth = 8;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_packing_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ /* Nothing should happen unless the bit depth is less than 8: */
+ return bit_depth < 8;
+}
+
+IT(packing);
+#undef PT
+#define PT ITSTRUCT(packing)
+
+#endif /* PNG_READ_PACK_SUPPORTED */
+
+/* png_set_packswap */
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+/* Swap pixels packed into bytes; reverses the order on screen so that
+ * the high order bits correspond to the rightmost pixels.
+ *
+ * png_set_packswap(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_packswap_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_packswap(pp);
+ that->this.littleendian = 1;
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_packswap_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->bit_depth < 8)
+ that->littleendian = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_packswap_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ return bit_depth < 8;
+}
+
+IT(packswap);
+#undef PT
+#define PT ITSTRUCT(packswap)
+
+#endif /* PNG_READ_PACKSWAP_SUPPORTED */
+
+
+/* png_set_invert_mono */
+#ifdef PNG_READ_INVERT_MONO_SUPPORTED
+/* Invert the gray channel
+ *
+ * png_set_invert_mono(png_structrp png_ptr)
+ */
+static void
+image_transform_png_set_invert_mono_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_invert_mono(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_invert_mono_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ if (that->colour_type & 4)
+ that->mono_inverted = 1;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_invert_mono_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ /* Only has an effect on pixels with no colour: */
+ return (colour_type & 2) == 0;
+}
+
+IT(invert_mono);
+#undef PT
+#define PT ITSTRUCT(invert_mono)
+
+#endif /* PNG_READ_INVERT_MONO_SUPPORTED */
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+/* png_set_shift(png_structp, png_const_color_8p true_bits)
+ *
+ * The output pixels will be shifted by the given true_bits
+ * values.
+ */
+#define data ITDATA(shift)
+static png_color_8 data;
+
+static void
+image_transform_png_set_shift_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ /* Get a random set of shifts. The shifts need to do something
+ * to test the transform, so they are limited to the bit depth
+ * of the input image. Notice that in the following the 'gray'
+ * field is randomized independently. This acts as a check that
+ * libpng does use the correct field.
+ */
+ const unsigned int depth = that->this.bit_depth;
+
+ data.red = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.green = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.blue = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.gray = (png_byte)/*SAFE*/(random_mod(depth)+1);
+ data.alpha = (png_byte)/*SAFE*/(random_mod(depth)+1);
+
+ png_set_shift(pp, &data);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_shift_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ /* Copy the correct values into the sBIT fields, libpng does not do
+ * anything to palette data:
+ */
+ if (that->colour_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ that->sig_bits = 1;
+
+ /* The sBIT fields are reset to the values previously sent to
+ * png_set_shift according to the colour type.
+ * does.
+ */
+ if (that->colour_type & 2) /* RGB channels */
+ {
+ that->red_sBIT = data.red;
+ that->green_sBIT = data.green;
+ that->blue_sBIT = data.blue;
+ }
+
+ else /* One grey channel */
+ that->red_sBIT = that->green_sBIT = that->blue_sBIT = data.gray;
+
+ that->alpha_sBIT = data.alpha;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_shift_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(bit_depth)
+
+ this->next = *that;
+ *that = this;
+
+ return colour_type != PNG_COLOR_TYPE_PALETTE;
+}
+
+IT(shift);
+#undef PT
+#define PT ITSTRUCT(shift)
+
+#endif /* PNG_READ_SHIFT_SUPPORTED */
+
+#ifdef THIS_IS_THE_PROFORMA
+static void
+image_transform_png_set_@_set(const image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_@(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_@_mod(const image_transform *this,
+ image_pixel *that, png_const_structp pp,
+ const transform_display *display)
+{
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_@_add(image_transform *this,
+ const image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ return 1;
+}
+
+IT(@);
+#endif
+
+
+/* This may just be 'end' if all the transforms are disabled! */
+static image_transform *const image_transform_first = &PT;
+
+static void
+transform_enable(const char *name)
+{
+ /* Everything starts out enabled, so if we see an 'enable' disabled
+ * everything else the first time round.
+ */
+ static int all_disabled = 0;
+ int found_it = 0;
+ image_transform *list = image_transform_first;
+
+ while (list != &image_transform_end)
+ {
+ if (strcmp(list->name, name) == 0)
+ {
+ list->enable = 1;
+ found_it = 1;
+ }
+ else if (!all_disabled)
+ list->enable = 0;
+
+ list = list->list;
+ }
+
+ all_disabled = 1;
+
+ if (!found_it)
+ {
+ fprintf(stderr, "pngvalid: --transform-enable=%s: unknown transform\n",
+ name);
+ exit(99);
+ }
+}
+
+static void
+transform_disable(const char *name)
+{
+ image_transform *list = image_transform_first;
+
+ while (list != &image_transform_end)
+ {
+ if (strcmp(list->name, name) == 0)
+ {
+ list->enable = 0;
+ return;
+ }
+
+ list = list->list;
+ }
+
+ fprintf(stderr, "pngvalid: --transform-disable=%s: unknown transform\n",
+ name);
+ exit(99);
+}
+
+static void
+image_transform_reset_count(void)
+{
+ image_transform *next = image_transform_first;
+ int count = 0;
+
+ while (next != &image_transform_end)
+ {
+ next->local_use = 0;
+ next->next = 0;
+ next = next->list;
+ ++count;
+ }
+
+ /* This can only happen if we every have more than 32 transforms (excluding
+ * the end) in the list.
+ */
+ if (count > 32) abort();
+}
+
+static int
+image_transform_test_counter(png_uint_32 counter, unsigned int max)
+{
+ /* Test the list to see if there is any point contining, given a current
+ * counter and a 'max' value.
+ */
+ image_transform *next = image_transform_first;
+
+ while (next != &image_transform_end)
+ {
+ /* For max 0 or 1 continue until the counter overflows: */
+ counter >>= 1;
+
+ /* Continue if any entry hasn't reacked the max. */
+ if (max > 1 && next->local_use < max)
+ return 1;
+ next = next->list;
+ }
+
+ return max <= 1 && counter == 0;
+}
+
+static png_uint_32
+image_transform_add(const image_transform **this, unsigned int max,
+ png_uint_32 counter, char *name, size_t sizeof_name, size_t *pos,
+ png_byte colour_type, png_byte bit_depth)
+{
+ for (;;) /* until we manage to add something */
+ {
+ png_uint_32 mask;
+ image_transform *list;
+
+ /* Find the next counter value, if the counter is zero this is the start
+ * of the list. This routine always returns the current counter (not the
+ * next) so it returns 0 at the end and expects 0 at the beginning.
+ */
+ if (counter == 0) /* first time */
+ {
+ image_transform_reset_count();
+ if (max <= 1)
+ counter = 1;
+ else
+ counter = random_32();
+ }
+ else /* advance the counter */
+ {
+ switch (max)
+ {
+ case 0: ++counter; break;
+ case 1: counter <<= 1; break;
+ default: counter = random_32(); break;
+ }
+ }
+
+ /* Now add all these items, if possible */
+ *this = &image_transform_end;
+ list = image_transform_first;
+ mask = 1;
+
+ /* Go through the whole list adding anything that the counter selects: */
+ while (list != &image_transform_end)
+ {
+ if ((counter & mask) != 0 && list->enable &&
+ (max == 0 || list->local_use < max))
+ {
+ /* Candidate to add: */
+ if (list->add(list, this, colour_type, bit_depth) || max == 0)
+ {
+ /* Added, so add to the name too. */
+ *pos = safecat(name, sizeof_name, *pos, " +");
+ *pos = safecat(name, sizeof_name, *pos, list->name);
+ }
+
+ else
+ {
+ /* Not useful and max>0, so remove it from *this: */
+ *this = list->next;
+ list->next = 0;
+
+ /* And, since we know it isn't useful, stop it being added again
+ * in this run:
+ */
+ list->local_use = max;
+ }
+ }
+
+ mask <<= 1;
+ list = list->list;
+ }
+
+ /* Now if anything was added we have something to do. */
+ if (*this != &image_transform_end)
+ return counter;
+
+ /* Nothing added, but was there anything in there to add? */
+ if (!image_transform_test_counter(counter, max))
+ return 0;
+ }
+}
+
+static void
+perform_transform_test(png_modifier *pm)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ unsigned int palette_number = 0;
+
+ while (next_format(&colour_type, &bit_depth, &palette_number, pm->test_lbg,
+ pm->test_tRNS))
+ {
+ png_uint_32 counter = 0;
+ size_t base_pos;
+ char name[64];
+
+ base_pos = safecat(name, sizeof name, 0, "transform:");
+
+ for (;;)
+ {
+ size_t pos = base_pos;
+ const image_transform *list = 0;
+
+ /* 'max' is currently hardwired to '1'; this should be settable on the
+ * command line.
+ */
+ counter = image_transform_add(&list, 1/*max*/, counter,
+ name, sizeof name, &pos, colour_type, bit_depth);
+
+ if (counter == 0)
+ break;
+
+ /* The command line can change this to checking interlaced images. */
+ do
+ {
+ pm->repeat = 0;
+ transform_test(pm, FILEID(colour_type, bit_depth, palette_number,
+ pm->interlace_type, 0, 0, 0), list, name);
+
+ if (fail(pm))
+ return;
+ }
+ while (pm->repeat);
+ }
+ }
+}
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/********************************* GAMMA TESTS ********************************/
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Reader callbacks and implementations, where they differ from the standard
+ * ones.
+ */
+typedef struct gamma_display
+{
+ standard_display this;
+
+ /* Parameters */
+ png_modifier* pm;
+ double file_gamma;
+ double screen_gamma;
+ double background_gamma;
+ png_byte sbit;
+ int threshold_test;
+ int use_input_precision;
+ int scale16;
+ int expand16;
+ int do_background;
+ png_color_16 background_color;
+
+ /* Local variables */
+ double maxerrout;
+ double maxerrpc;
+ double maxerrabs;
+} gamma_display;
+
+#define ALPHA_MODE_OFFSET 4
+
+static void
+gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
+ double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
+ int use_input_precision, int scale16, int expand16,
+ int do_background, const png_color_16 *pointer_to_the_background_color,
+ double background_gamma)
+{
+ /* Standard fields */
+ standard_display_init(&dp->this, &pm->this, id, do_read_interlace,
+ pm->use_update_info);
+
+ /* Parameter fields */
+ dp->pm = pm;
+ dp->file_gamma = file_gamma;
+ dp->screen_gamma = screen_gamma;
+ dp->background_gamma = background_gamma;
+ dp->sbit = sbit;
+ dp->threshold_test = threshold_test;
+ dp->use_input_precision = use_input_precision;
+ dp->scale16 = scale16;
+ dp->expand16 = expand16;
+ dp->do_background = do_background;
+ if (do_background && pointer_to_the_background_color != 0)
+ dp->background_color = *pointer_to_the_background_color;
+ else
+ memset(&dp->background_color, 0, sizeof dp->background_color);
+
+ /* Local variable fields */
+ dp->maxerrout = dp->maxerrpc = dp->maxerrabs = 0;
+}
+
+static void
+gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi)
+{
+ /* Reuse the standard stuff as appropriate. */
+ standard_info_part1(&dp->this, pp, pi);
+
+ /* If requested strip 16 to 8 bits - this is handled automagically below
+ * because the output bit depth is read from the library. Note that there
+ * are interactions with sBIT but, internally, libpng makes sbit at most
+ * PNG_MAX_GAMMA_8 prior to 1.7 when doing the following.
+ */
+ if (dp->scale16)
+# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ png_set_scale_16(pp);
+# else
+ /* The following works both in 1.5.4 and earlier versions: */
+# ifdef PNG_READ_16_TO_8_SUPPORTED
+ png_set_strip_16(pp);
+# else
+ png_error(pp, "scale16 (16 to 8 bit conversion) not supported");
+# endif
+# endif
+
+ if (dp->expand16)
+# ifdef PNG_READ_EXPAND_16_SUPPORTED
+ png_set_expand_16(pp);
+# else
+ png_error(pp, "expand16 (8 to 16 bit conversion) not supported");
+# endif
+
+ if (dp->do_background >= ALPHA_MODE_OFFSET)
+ {
+# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ {
+ /* This tests the alpha mode handling, if supported. */
+ int mode = dp->do_background - ALPHA_MODE_OFFSET;
+
+ /* The gamma value is the output gamma, and is in the standard,
+ * non-inverted, represenation. It provides a default for the PNG file
+ * gamma, but since the file has a gAMA chunk this does not matter.
+ */
+ const double sg = dp->screen_gamma;
+# ifndef PNG_FLOATING_POINT_SUPPORTED
+ const png_fixed_point g = fix(sg);
+# endif
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_alpha_mode(pp, mode, sg);
+# else
+ png_set_alpha_mode_fixed(pp, mode, g);
+# endif
+
+ /* However, for the standard Porter-Duff algorithm the output defaults
+ * to be linear, so if the test requires non-linear output it must be
+ * corrected here.
+ */
+ if (mode == PNG_ALPHA_STANDARD && sg != 1)
+ {
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_gamma(pp, sg, dp->file_gamma);
+# else
+ png_fixed_point f = fix(dp->file_gamma);
+ png_set_gamma_fixed(pp, g, f);
+# endif
+ }
+ }
+# else
+ png_error(pp, "alpha mode handling not supported");
+# endif
+ }
+
+ else
+ {
+ /* Set up gamma processing. */
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_gamma(pp, dp->screen_gamma, dp->file_gamma);
+# else
+ {
+ png_fixed_point s = fix(dp->screen_gamma);
+ png_fixed_point f = fix(dp->file_gamma);
+ png_set_gamma_fixed(pp, s, f);
+ }
+# endif
+
+ if (dp->do_background)
+ {
+# ifdef PNG_READ_BACKGROUND_SUPPORTED
+ /* NOTE: this assumes the caller provided the correct background gamma!
+ */
+ const double bg = dp->background_gamma;
+# ifndef PNG_FLOATING_POINT_SUPPORTED
+ const png_fixed_point g = fix(bg);
+# endif
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_background(pp, &dp->background_color, dp->do_background,
+ 0/*need_expand*/, bg);
+# else
+ png_set_background_fixed(pp, &dp->background_color,
+ dp->do_background, 0/*need_expand*/, g);
+# endif
+# else
+ png_error(pp, "png_set_background not supported");
+# endif
+ }
+ }
+
+ {
+ int i = dp->this.use_update_info;
+ /* Always do one call, even if use_update_info is 0. */
+ do
+ png_read_update_info(pp, pi);
+ while (--i > 0);
+ }
+
+ /* Now we may get a different cbRow: */
+ standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
+}
+
+static void PNGCBAPI
+gamma_info(png_structp pp, png_infop pi)
+{
+ gamma_info_imp(voidcast(gamma_display*, png_get_progressive_ptr(pp)), pp,
+ pi);
+}
+
+/* Validate a single component value - the routine gets the input and output
+ * sample values as unscaled PNG component values along with a cache of all the
+ * information required to validate the values.
+ */
+typedef struct validate_info
+{
+ png_const_structp pp;
+ gamma_display *dp;
+ png_byte sbit;
+ int use_input_precision;
+ int do_background;
+ int scale16;
+ unsigned int sbit_max;
+ unsigned int isbit_shift;
+ unsigned int outmax;
+
+ double gamma_correction; /* Overall correction required. */
+ double file_inverse; /* Inverse of file gamma. */
+ double screen_gamma;
+ double screen_inverse; /* Inverse of screen gamma. */
+
+ double background_red; /* Linear background value, red or gray. */
+ double background_green;
+ double background_blue;
+
+ double maxabs;
+ double maxpc;
+ double maxcalc;
+ double maxout;
+ double maxout_total; /* Total including quantization error */
+ double outlog;
+ int outquant;
+}
+validate_info;
+
+static void
+init_validate_info(validate_info *vi, gamma_display *dp, png_const_structp pp,
+ int in_depth, int out_depth)
+{
+ const unsigned int outmax = (1U<<out_depth)-1;
+
+ vi->pp = pp;
+ vi->dp = dp;
+
+ if (dp->sbit > 0 && dp->sbit < in_depth)
+ {
+ vi->sbit = dp->sbit;
+ vi->isbit_shift = in_depth - dp->sbit;
+ }
+
+ else
+ {
+ vi->sbit = (png_byte)in_depth;
+ vi->isbit_shift = 0;
+ }
+
+ vi->sbit_max = (1U << vi->sbit)-1;
+
+ /* This mimics the libpng threshold test, '0' is used to prevent gamma
+ * correction in the validation test.
+ */
+ vi->screen_gamma = dp->screen_gamma;
+ if (fabs(vi->screen_gamma-1) < PNG_GAMMA_THRESHOLD)
+ vi->screen_gamma = vi->screen_inverse = 0;
+ else
+ vi->screen_inverse = 1/vi->screen_gamma;
+
+ vi->use_input_precision = dp->use_input_precision;
+ vi->outmax = outmax;
+ vi->maxabs = abserr(dp->pm, in_depth, out_depth);
+ vi->maxpc = pcerr(dp->pm, in_depth, out_depth);
+ vi->maxcalc = calcerr(dp->pm, in_depth, out_depth);
+ vi->maxout = outerr(dp->pm, in_depth, out_depth);
+ vi->outquant = output_quantization_factor(dp->pm, in_depth, out_depth);
+ vi->maxout_total = vi->maxout + vi->outquant * .5;
+ vi->outlog = outlog(dp->pm, in_depth, out_depth);
+
+ if ((dp->this.colour_type & PNG_COLOR_MASK_ALPHA) != 0 ||
+ (dp->this.colour_type == 3 && dp->this.is_transparent) ||
+ ((dp->this.colour_type == 0 || dp->this.colour_type == 2) &&
+ dp->this.has_tRNS))
+ {
+ vi->do_background = dp->do_background;
+
+ if (vi->do_background != 0)
+ {
+ const double bg_inverse = 1/dp->background_gamma;
+ double r, g, b;
+
+ /* Caller must at least put the gray value into the red channel */
+ r = dp->background_color.red; r /= outmax;
+ g = dp->background_color.green; g /= outmax;
+ b = dp->background_color.blue; b /= outmax;
+
+# if 0
+ /* libpng doesn't do this optimization, if we do pngvalid will fail.
+ */
+ if (fabs(bg_inverse-1) >= PNG_GAMMA_THRESHOLD)
+# endif
+ {
+ r = pow(r, bg_inverse);
+ g = pow(g, bg_inverse);
+ b = pow(b, bg_inverse);
+ }
+
+ vi->background_red = r;
+ vi->background_green = g;
+ vi->background_blue = b;
+ }
+ }
+ else /* Do not expect any background processing */
+ vi->do_background = 0;
+
+ if (vi->do_background == 0)
+ vi->background_red = vi->background_green = vi->background_blue = 0;
+
+ vi->gamma_correction = 1/(dp->file_gamma*dp->screen_gamma);
+ if (fabs(vi->gamma_correction-1) < PNG_GAMMA_THRESHOLD)
+ vi->gamma_correction = 0;
+
+ vi->file_inverse = 1/dp->file_gamma;
+ if (fabs(vi->file_inverse-1) < PNG_GAMMA_THRESHOLD)
+ vi->file_inverse = 0;
+
+ vi->scale16 = dp->scale16;
+}
+
+/* This function handles composition of a single non-alpha component. The
+ * argument is the input sample value, in the range 0..1, and the alpha value.
+ * The result is the composed, linear, input sample. If alpha is less than zero
+ * this is the alpha component and the function should not be called!
+ */
+static double
+gamma_component_compose(int do_background, double input_sample, double alpha,
+ double background, int *compose)
+{
+ switch (do_background)
+ {
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ case PNG_BACKGROUND_GAMMA_FILE:
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ /* Standard PNG background processing. */
+ if (alpha < 1)
+ {
+ if (alpha > 0)
+ {
+ input_sample = input_sample * alpha + background * (1-alpha);
+ if (compose != NULL)
+ *compose = 1;
+ }
+
+ else
+ input_sample = background;
+ }
+ break;
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
+ /* The components are premultiplied in either case and the output is
+ * gamma encoded (to get standard Porter-Duff we expect the output
+ * gamma to be set to 1.0!)
+ */
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
+ /* The optimization is that the partial-alpha entries are linear
+ * while the opaque pixels are gamma encoded, but this only affects the
+ * output encoding.
+ */
+ if (alpha < 1)
+ {
+ if (alpha > 0)
+ {
+ input_sample *= alpha;
+ if (compose != NULL)
+ *compose = 1;
+ }
+
+ else
+ input_sample = 0;
+ }
+ break;
+#endif
+
+ default:
+ /* Standard cases where no compositing is done (so the component
+ * value is already correct.)
+ */
+ UNUSED(alpha)
+ UNUSED(background)
+ UNUSED(compose)
+ break;
+ }
+
+ return input_sample;
+}
+
+/* This API returns the encoded *input* component, in the range 0..1 */
+static double
+gamma_component_validate(const char *name, const validate_info *vi,
+ const unsigned int id, const unsigned int od,
+ const double alpha /* <0 for the alpha channel itself */,
+ const double background /* component background value */)
+{
+ const unsigned int isbit = id >> vi->isbit_shift;
+ const unsigned int sbit_max = vi->sbit_max;
+ const unsigned int outmax = vi->outmax;
+ const int do_background = vi->do_background;
+
+ double i;
+
+ /* First check on the 'perfect' result obtained from the digitized input
+ * value, id, and compare this against the actual digitized result, 'od'.
+ * 'i' is the input result in the range 0..1:
+ */
+ i = isbit; i /= sbit_max;
+
+ /* Check for the fast route: if we don't do any background composition or if
+ * this is the alpha channel ('alpha' < 0) or if the pixel is opaque then
+ * just use the gamma_correction field to correct to the final output gamma.
+ */
+ if (alpha == 1 /* opaque pixel component */ || !do_background
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ || do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_PNG
+#endif
+ || (alpha < 0 /* alpha channel */
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ && do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN
+#endif
+ ))
+ {
+ /* Then get the gamma corrected version of 'i' and compare to 'od', any
+ * error less than .5 is insignificant - just quantization of the output
+ * value to the nearest digital value (nevertheless the error is still
+ * recorded - it's interesting ;-)
+ */
+ double encoded_sample = i;
+ double encoded_error;
+
+ /* alpha less than 0 indicates the alpha channel, which is always linear
+ */
+ if (alpha >= 0 && vi->gamma_correction > 0)
+ encoded_sample = pow(encoded_sample, vi->gamma_correction);
+ encoded_sample *= outmax;
+
+ encoded_error = fabs(od-encoded_sample);
+
+ if (encoded_error > vi->dp->maxerrout)
+ vi->dp->maxerrout = encoded_error;
+
+ if (encoded_error < vi->maxout_total && encoded_error < vi->outlog)
+ return i;
+ }
+
+ /* The slow route - attempt to do linear calculations. */
+ /* There may be an error, or background processing is required, so calculate
+ * the actual sample values - unencoded light intensity values. Note that in
+ * practice these are not completely unencoded because they include a
+ * 'viewing correction' to decrease or (normally) increase the perceptual
+ * contrast of the image. There's nothing we can do about this - we don't
+ * know what it is - so assume the unencoded value is perceptually linear.
+ */
+ {
+ double input_sample = i; /* In range 0..1 */
+ double output, error, encoded_sample, encoded_error;
+ double es_lo, es_hi;
+ int compose = 0; /* Set to one if composition done */
+ int output_is_encoded; /* Set if encoded to screen gamma */
+ int log_max_error = 1; /* Check maximum error values */
+ png_const_charp pass = 0; /* Reason test passes (or 0 for fail) */
+
+ /* Convert to linear light (with the above caveat.) The alpha channel is
+ * already linear.
+ */
+ if (alpha >= 0)
+ {
+ int tcompose;
+
+ if (vi->file_inverse > 0)
+ input_sample = pow(input_sample, vi->file_inverse);
+
+ /* Handle the compose processing: */
+ tcompose = 0;
+ input_sample = gamma_component_compose(do_background, input_sample,
+ alpha, background, &tcompose);
+
+ if (tcompose)
+ compose = 1;
+ }
+
+ /* And similarly for the output value, but we need to check the background
+ * handling to linearize it correctly.
+ */
+ output = od;
+ output /= outmax;
+
+ output_is_encoded = vi->screen_gamma > 0;
+
+ if (alpha < 0) /* The alpha channel */
+ {
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ if (do_background != ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN)
+#endif
+ {
+ /* In all other cases the output alpha channel is linear already,
+ * don't log errors here, they are much larger in linear data.
+ */
+ output_is_encoded = 0;
+ log_max_error = 0;
+ }
+ }
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ else /* A component */
+ {
+ if (do_background == ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED &&
+ alpha < 1) /* the optimized case - linear output */
+ {
+ if (alpha > 0) log_max_error = 0;
+ output_is_encoded = 0;
+ }
+ }
+#endif
+
+ if (output_is_encoded)
+ output = pow(output, vi->screen_gamma);
+
+ /* Calculate (or recalculate) the encoded_sample value and repeat the
+ * check above (unnecessary if we took the fast route, but harmless.)
+ */
+ encoded_sample = input_sample;
+ if (output_is_encoded)
+ encoded_sample = pow(encoded_sample, vi->screen_inverse);
+ encoded_sample *= outmax;
+
+ encoded_error = fabs(od-encoded_sample);
+
+ /* Don't log errors in the alpha channel, or the 'optimized' case,
+ * neither are significant to the overall perception.
+ */
+ if (log_max_error && encoded_error > vi->dp->maxerrout)
+ vi->dp->maxerrout = encoded_error;
+
+ if (encoded_error < vi->maxout_total)
+ {
+ if (encoded_error < vi->outlog)
+ return i;
+
+ /* Test passed but error is bigger than the log limit, record why the
+ * test passed:
+ */
+ pass = "less than maxout:\n";
+ }
+
+ /* i: the original input value in the range 0..1
+ *
+ * pngvalid calculations:
+ * input_sample: linear result; i linearized and composed, range 0..1
+ * encoded_sample: encoded result; input_sample scaled to ouput bit depth
+ *
+ * libpng calculations:
+ * output: linear result; od scaled to 0..1 and linearized
+ * od: encoded result from libpng
+ */
+
+ /* Now we have the numbers for real errors, both absolute values as as a
+ * percentage of the correct value (output):
+ */
+ error = fabs(input_sample-output);
+
+ if (log_max_error && error > vi->dp->maxerrabs)
+ vi->dp->maxerrabs = error;
+
+ /* The following is an attempt to ignore the tendency of quantization to
+ * dominate the percentage errors for lower result values:
+ */
+ if (log_max_error && input_sample > .5)
+ {
+ double percentage_error = error/input_sample;
+ if (percentage_error > vi->dp->maxerrpc)
+ vi->dp->maxerrpc = percentage_error;
+ }
+
+ /* Now calculate the digitization limits for 'encoded_sample' using the
+ * 'max' values. Note that maxout is in the encoded space but maxpc and
+ * maxabs are in linear light space.
+ *
+ * First find the maximum error in linear light space, range 0..1:
+ */
+ {
+ double tmp = input_sample * vi->maxpc;
+ if (tmp < vi->maxabs) tmp = vi->maxabs;
+ /* If 'compose' is true the composition was done in linear space using
+ * integer arithmetic. This introduces an extra error of +/- 0.5 (at
+ * least) in the integer space used. 'maxcalc' records this, taking
+ * into account the possibility that even for 16 bit output 8 bit space
+ * may have been used.
+ */
+ if (compose && tmp < vi->maxcalc) tmp = vi->maxcalc;
+
+ /* The 'maxout' value refers to the encoded result, to compare with
+ * this encode input_sample adjusted by the maximum error (tmp) above.
+ */
+ es_lo = encoded_sample - vi->maxout;
+
+ if (es_lo > 0 && input_sample-tmp > 0)
+ {
+ double low_value = input_sample-tmp;
+ if (output_is_encoded)
+ low_value = pow(low_value, vi->screen_inverse);
+ low_value *= outmax;
+ if (low_value < es_lo) es_lo = low_value;
+
+ /* Quantize this appropriately: */
+ es_lo = ceil(es_lo / vi->outquant - .5) * vi->outquant;
+ }
+
+ else
+ es_lo = 0;
+
+ es_hi = encoded_sample + vi->maxout;
+
+ if (es_hi < outmax && input_sample+tmp < 1)
+ {
+ double high_value = input_sample+tmp;
+ if (output_is_encoded)
+ high_value = pow(high_value, vi->screen_inverse);
+ high_value *= outmax;
+ if (high_value > es_hi) es_hi = high_value;
+
+ es_hi = floor(es_hi / vi->outquant + .5) * vi->outquant;
+ }
+
+ else
+ es_hi = outmax;
+ }
+
+ /* The primary test is that the final encoded value returned by the
+ * library should be between the two limits (inclusive) that were
+ * calculated above.
+ */
+ if (od >= es_lo && od <= es_hi)
+ {
+ /* The value passes, but we may need to log the information anyway. */
+ if (encoded_error < vi->outlog)
+ return i;
+
+ if (pass == 0)
+ pass = "within digitization limits:\n";
+ }
+
+ {
+ /* There has been an error in processing, or we need to log this
+ * value.
+ */
+ double is_lo, is_hi;
+
+ /* pass is set at this point if either of the tests above would have
+ * passed. Don't do these additional tests here - just log the
+ * original [es_lo..es_hi] values.
+ */
+ if (pass == 0 && vi->use_input_precision && vi->dp->sbit)
+ {
+ /* Ok, something is wrong - this actually happens in current libpng
+ * 16-to-8 processing. Assume that the input value (id, adjusted
+ * for sbit) can be anywhere between value-.5 and value+.5 - quite a
+ * large range if sbit is low.
+ *
+ * NOTE: at present because the libpng gamma table stuff has been
+ * changed to use a rounding algorithm to correct errors in 8-bit
+ * calculations the precise sbit calculation (a shift) has been
+ * lost. This can result in up to a +/-1 error in the presence of
+ * an sbit less than the bit depth.
+ */
+# if PNG_LIBPNG_VER < 10700
+# define SBIT_ERROR .5
+# else
+# define SBIT_ERROR 1.
+# endif
+ double tmp = (isbit - SBIT_ERROR)/sbit_max;
+
+ if (tmp <= 0)
+ tmp = 0;
+
+ else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
+ tmp = pow(tmp, vi->file_inverse);
+
+ tmp = gamma_component_compose(do_background, tmp, alpha, background,
+ NULL);
+
+ if (output_is_encoded && tmp > 0 && tmp < 1)
+ tmp = pow(tmp, vi->screen_inverse);
+
+ is_lo = ceil(outmax * tmp - vi->maxout_total);
+
+ if (is_lo < 0)
+ is_lo = 0;
+
+ tmp = (isbit + SBIT_ERROR)/sbit_max;
+
+ if (tmp >= 1)
+ tmp = 1;
+
+ else if (alpha >= 0 && vi->file_inverse > 0 && tmp < 1)
+ tmp = pow(tmp, vi->file_inverse);
+
+ tmp = gamma_component_compose(do_background, tmp, alpha, background,
+ NULL);
+
+ if (output_is_encoded && tmp > 0 && tmp < 1)
+ tmp = pow(tmp, vi->screen_inverse);
+
+ is_hi = floor(outmax * tmp + vi->maxout_total);
+
+ if (is_hi > outmax)
+ is_hi = outmax;
+
+ if (!(od < is_lo || od > is_hi))
+ {
+ if (encoded_error < vi->outlog)
+ return i;
+
+ pass = "within input precision limits:\n";
+ }
+
+ /* One last chance. If this is an alpha channel and the 16to8
+ * option has been used and 'inaccurate' scaling is used then the
+ * bit reduction is obtained by simply using the top 8 bits of the
+ * value.
+ *
+ * This is only done for older libpng versions when the 'inaccurate'
+ * (chop) method of scaling was used.
+ */
+# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+# if PNG_LIBPNG_VER < 10504
+ /* This may be required for other components in the future,
+ * but at present the presence of gamma correction effectively
+ * prevents the errors in the component scaling (I don't quite
+ * understand why, but since it's better this way I care not
+ * to ask, JB 20110419.)
+ */
+ if (pass == 0 && alpha < 0 && vi->scale16 && vi->sbit > 8 &&
+ vi->sbit + vi->isbit_shift == 16)
+ {
+ tmp = ((id >> 8) - .5)/255;
+
+ if (tmp > 0)
+ {
+ is_lo = ceil(outmax * tmp - vi->maxout_total);
+ if (is_lo < 0) is_lo = 0;
+ }
+
+ else
+ is_lo = 0;
+
+ tmp = ((id >> 8) + .5)/255;
+
+ if (tmp < 1)
+ {
+ is_hi = floor(outmax * tmp + vi->maxout_total);
+ if (is_hi > outmax) is_hi = outmax;
+ }
+
+ else
+ is_hi = outmax;
+
+ if (!(od < is_lo || od > is_hi))
+ {
+ if (encoded_error < vi->outlog)
+ return i;
+
+ pass = "within 8 bit limits:\n";
+ }
+ }
+# endif
+# endif
+ }
+ else /* !use_input_precision */
+ is_lo = es_lo, is_hi = es_hi;
+
+ /* Attempt to output a meaningful error/warning message: the message
+ * output depends on the background/composite operation being performed
+ * because this changes what parameters were actually used above.
+ */
+ {
+ size_t pos = 0;
+ /* Need either 1/255 or 1/65535 precision here; 3 or 6 decimal
+ * places. Just use outmax to work out which.
+ */
+ int precision = (outmax >= 1000 ? 6 : 3);
+ int use_input=1, use_background=0, do_compose=0;
+ char msg[256];
+
+ if (pass != 0)
+ pos = safecat(msg, sizeof msg, pos, "\n\t");
+
+ /* Set up the various flags, the output_is_encoded flag above
+ * is also used below. do_compose is just a double check.
+ */
+ switch (do_background)
+ {
+# ifdef PNG_READ_BACKGROUND_SUPPORTED
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ case PNG_BACKGROUND_GAMMA_FILE:
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ use_background = (alpha >= 0 && alpha < 1);
+ /*FALL THROUGH*/
+# endif
+# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
+# endif /* ALPHA_MODE_SUPPORTED */
+ do_compose = (alpha > 0 && alpha < 1);
+ use_input = (alpha != 0);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check the 'compose' flag */
+ if (compose != do_compose)
+ png_error(vi->pp, "internal error (compose)");
+
+ /* 'name' is the component name */
+ pos = safecat(msg, sizeof msg, pos, name);
+ pos = safecat(msg, sizeof msg, pos, "(");
+ pos = safecatn(msg, sizeof msg, pos, id);
+ if (use_input || pass != 0/*logging*/)
+ {
+ if (isbit != id)
+ {
+ /* sBIT has reduced the precision of the input: */
+ pos = safecat(msg, sizeof msg, pos, ", sbit(");
+ pos = safecatn(msg, sizeof msg, pos, vi->sbit);
+ pos = safecat(msg, sizeof msg, pos, "): ");
+ pos = safecatn(msg, sizeof msg, pos, isbit);
+ }
+ pos = safecat(msg, sizeof msg, pos, "/");
+ /* The output is either "id/max" or "id sbit(sbit): isbit/max" */
+ pos = safecatn(msg, sizeof msg, pos, vi->sbit_max);
+ }
+ pos = safecat(msg, sizeof msg, pos, ")");
+
+ /* A component may have been multiplied (in linear space) by the
+ * alpha value, 'compose' says whether this is relevant.
+ */
+ if (compose || pass != 0)
+ {
+ /* If any form of composition is being done report our
+ * calculated linear value here (the code above doesn't record
+ * the input value before composition is performed, so what
+ * gets reported is the value after composition.)
+ */
+ if (use_input || pass != 0)
+ {
+ if (vi->file_inverse > 0)
+ {
+ pos = safecat(msg, sizeof msg, pos, "^");
+ pos = safecatd(msg, sizeof msg, pos, vi->file_inverse, 2);
+ }
+
+ else
+ pos = safecat(msg, sizeof msg, pos, "[linear]");
+
+ pos = safecat(msg, sizeof msg, pos, "*(alpha)");
+ pos = safecatd(msg, sizeof msg, pos, alpha, precision);
+ }
+
+ /* Now record the *linear* background value if it was used
+ * (this function is not passed the original, non-linear,
+ * value but it is contained in the test name.)
+ */
+ if (use_background)
+ {
+ pos = safecat(msg, sizeof msg, pos, use_input ? "+" : " ");
+ pos = safecat(msg, sizeof msg, pos, "(background)");
+ pos = safecatd(msg, sizeof msg, pos, background, precision);
+ pos = safecat(msg, sizeof msg, pos, "*");
+ pos = safecatd(msg, sizeof msg, pos, 1-alpha, precision);
+ }
+ }
+
+ /* Report the calculated value (input_sample) and the linearized
+ * libpng value (output) unless this is just a component gamma
+ * correction.
+ */
+ if (compose || alpha < 0 || pass != 0)
+ {
+ pos = safecat(msg, sizeof msg, pos,
+ pass != 0 ? " =\n\t" : " = ");
+ pos = safecatd(msg, sizeof msg, pos, input_sample, precision);
+ pos = safecat(msg, sizeof msg, pos, " (libpng: ");
+ pos = safecatd(msg, sizeof msg, pos, output, precision);
+ pos = safecat(msg, sizeof msg, pos, ")");
+
+ /* Finally report the output gamma encoding, if any. */
+ if (output_is_encoded)
+ {
+ pos = safecat(msg, sizeof msg, pos, " ^");
+ pos = safecatd(msg, sizeof msg, pos, vi->screen_inverse, 2);
+ pos = safecat(msg, sizeof msg, pos, "(to screen) =");
+ }
+
+ else
+ pos = safecat(msg, sizeof msg, pos, " [screen is linear] =");
+ }
+
+ if ((!compose && alpha >= 0) || pass != 0)
+ {
+ if (pass != 0) /* logging */
+ pos = safecat(msg, sizeof msg, pos, "\n\t[overall:");
+
+ /* This is the non-composition case, the internal linear
+ * values are irrelevant (though the log below will reveal
+ * them.) Output a much shorter warning/error message and report
+ * the overall gamma correction.
+ */
+ if (vi->gamma_correction > 0)
+ {
+ pos = safecat(msg, sizeof msg, pos, " ^");
+ pos = safecatd(msg, sizeof msg, pos, vi->gamma_correction, 2);
+ pos = safecat(msg, sizeof msg, pos, "(gamma correction) =");
+ }
+
+ else
+ pos = safecat(msg, sizeof msg, pos,
+ " [no gamma correction] =");
+
+ if (pass != 0)
+ pos = safecat(msg, sizeof msg, pos, "]");
+ }
+
+ /* This is our calculated encoded_sample which should (but does
+ * not) match od:
+ */
+ pos = safecat(msg, sizeof msg, pos, pass != 0 ? "\n\t" : " ");
+ pos = safecatd(msg, sizeof msg, pos, is_lo, 1);
+ pos = safecat(msg, sizeof msg, pos, " < ");
+ pos = safecatd(msg, sizeof msg, pos, encoded_sample, 1);
+ pos = safecat(msg, sizeof msg, pos, " (libpng: ");
+ pos = safecatn(msg, sizeof msg, pos, od);
+ pos = safecat(msg, sizeof msg, pos, ")");
+ pos = safecat(msg, sizeof msg, pos, "/");
+ pos = safecatn(msg, sizeof msg, pos, outmax);
+ pos = safecat(msg, sizeof msg, pos, " < ");
+ pos = safecatd(msg, sizeof msg, pos, is_hi, 1);
+
+ if (pass == 0) /* The error condition */
+ {
+# ifdef PNG_WARNINGS_SUPPORTED
+ png_warning(vi->pp, msg);
+# else
+ store_warning(vi->pp, msg);
+# endif
+ }
+
+ else /* logging this value */
+ store_verbose(&vi->dp->pm->this, vi->pp, pass, msg);
+ }
+ }
+ }
+
+ return i;
+}
+
+static void
+gamma_image_validate(gamma_display *dp, png_const_structp pp,
+ png_infop pi)
+{
+ /* Get some constants derived from the input and output file formats: */
+ const png_store* const ps = dp->this.ps;
+ const png_byte in_ct = dp->this.colour_type;
+ const png_byte in_bd = dp->this.bit_depth;
+ const png_uint_32 w = dp->this.w;
+ const png_uint_32 h = dp->this.h;
+ const size_t cbRow = dp->this.cbRow;
+ const png_byte out_ct = png_get_color_type(pp, pi);
+ const png_byte out_bd = png_get_bit_depth(pp, pi);
+
+ /* There are three sources of error, firstly the quantization in the
+ * file encoding, determined by sbit and/or the file depth, secondly
+ * the output (screen) gamma and thirdly the output file encoding.
+ *
+ * Since this API receives the screen and file gamma in double
+ * precision it is possible to calculate an exact answer given an input
+ * pixel value. Therefore we assume that the *input* value is exact -
+ * sample/maxsample - calculate the corresponding gamma corrected
+ * output to the limits of double precision arithmetic and compare with
+ * what libpng returns.
+ *
+ * Since the library must quantize the output to 8 or 16 bits there is
+ * a fundamental limit on the accuracy of the output of +/-.5 - this
+ * quantization limit is included in addition to the other limits
+ * specified by the paramaters to the API. (Effectively, add .5
+ * everywhere.)
+ *
+ * The behavior of the 'sbit' paramter is defined by section 12.5
+ * (sample depth scaling) of the PNG spec. That section forces the
+ * decoder to assume that the PNG values have been scaled if sBIT is
+ * present:
+ *
+ * png-sample = floor( input-sample * (max-out/max-in) + .5);
+ *
+ * This means that only a subset of the possible PNG values should
+ * appear in the input. However, the spec allows the encoder to use a
+ * variety of approximations to the above and doesn't require any
+ * restriction of the values produced.
+ *
+ * Nevertheless the spec requires that the upper 'sBIT' bits of the
+ * value stored in a PNG file be the original sample bits.
+ * Consequently the code below simply scales the top sbit bits by
+ * (1<<sbit)-1 to obtain an original sample value.
+ *
+ * Because there is limited precision in the input it is arguable that
+ * an acceptable result is any valid result from input-.5 to input+.5.
+ * The basic tests below do not do this, however if 'use_input_precision'
+ * is set a subsequent test is performed above.
+ */
+ const unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
+ int processing;
+ png_uint_32 y;
+ const store_palette_entry *in_palette = dp->this.palette;
+ const int in_is_transparent = dp->this.is_transparent;
+ int process_tRNS;
+ int out_npalette = -1;
+ int out_is_transparent = 0; /* Just refers to the palette case */
+ store_palette out_palette;
+ validate_info vi;
+
+ /* Check for row overwrite errors */
+ store_image_check(dp->this.ps, pp, 0);
+
+ /* Supply the input and output sample depths here - 8 for an indexed image,
+ * otherwise the bit depth.
+ */
+ init_validate_info(&vi, dp, pp, in_ct==3?8:in_bd, out_ct==3?8:out_bd);
+
+ processing = (vi.gamma_correction > 0 && !dp->threshold_test)
+ || in_bd != out_bd || in_ct != out_ct || vi.do_background;
+ process_tRNS = dp->this.has_tRNS && vi.do_background;
+
+ /* TODO: FIX THIS: MAJOR BUG! If the transformations all happen inside
+ * the palette there is no way of finding out, because libpng fails to
+ * update the palette on png_read_update_info. Indeed, libpng doesn't
+ * even do the required work until much later, when it doesn't have any
+ * info pointer. Oops. For the moment 'processing' is turned off if
+ * out_ct is palette.
+ */
+ if (in_ct == 3 && out_ct == 3)
+ processing = 0;
+
+ if (processing && out_ct == 3)
+ out_is_transparent = read_palette(out_palette, &out_npalette, pp, pi);
+
+ for (y=0; y<h; ++y)
+ {
+ png_const_bytep pRow = store_image_row(ps, pp, 0, y);
+ png_byte std[STANDARD_ROWMAX];
+
+ transform_row(pp, std, in_ct, in_bd, y);
+
+ if (processing)
+ {
+ unsigned int x;
+
+ for (x=0; x<w; ++x)
+ {
+ double alpha = 1; /* serves as a flag value */
+
+ /* Record the palette index for index images. */
+ const unsigned int in_index =
+ in_ct == 3 ? sample(std, 3, in_bd, x, 0, 0, 0) : 256;
+ const unsigned int out_index =
+ out_ct == 3 ? sample(std, 3, out_bd, x, 0, 0, 0) : 256;
+
+ /* Handle input alpha - png_set_background will cause the output
+ * alpha to disappear so there is nothing to check.
+ */
+ if ((in_ct & PNG_COLOR_MASK_ALPHA) != 0 ||
+ (in_ct == 3 && in_is_transparent))
+ {
+ const unsigned int input_alpha = in_ct == 3 ?
+ dp->this.palette[in_index].alpha :
+ sample(std, in_ct, in_bd, x, samples_per_pixel, 0, 0);
+
+ unsigned int output_alpha = 65536 /* as a flag value */;
+
+ if (out_ct == 3)
+ {
+ if (out_is_transparent)
+ output_alpha = out_palette[out_index].alpha;
+ }
+
+ else if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0)
+ output_alpha = sample(pRow, out_ct, out_bd, x,
+ samples_per_pixel, 0, 0);
+
+ if (output_alpha != 65536)
+ alpha = gamma_component_validate("alpha", &vi, input_alpha,
+ output_alpha, -1/*alpha*/, 0/*background*/);
+
+ else /* no alpha in output */
+ {
+ /* This is a copy of the calculation of 'i' above in order to
+ * have the alpha value to use in the background calculation.
+ */
+ alpha = input_alpha >> vi.isbit_shift;
+ alpha /= vi.sbit_max;
+ }
+ }
+
+ else if (process_tRNS)
+ {
+ /* alpha needs to be set appropriately for this pixel, it is
+ * currently 1 and needs to be 0 for an input pixel which matches
+ * the values in tRNS.
+ */
+ switch (in_ct)
+ {
+ case 0: /* gray */
+ if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
+ dp->this.transparent.red)
+ alpha = 0;
+ break;
+
+ case 2: /* RGB */
+ if (sample(std, in_ct, in_bd, x, 0, 0, 0) ==
+ dp->this.transparent.red &&
+ sample(std, in_ct, in_bd, x, 1, 0, 0) ==
+ dp->this.transparent.green &&
+ sample(std, in_ct, in_bd, x, 2, 0, 0) ==
+ dp->this.transparent.blue)
+ alpha = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Handle grayscale or RGB components. */
+ if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */
+ (void)gamma_component_validate("gray", &vi,
+ sample(std, in_ct, in_bd, x, 0, 0, 0),
+ sample(pRow, out_ct, out_bd, x, 0, 0, 0),
+ alpha/*component*/, vi.background_red);
+ else /* RGB or palette */
+ {
+ (void)gamma_component_validate("red", &vi,
+ in_ct == 3 ? in_palette[in_index].red :
+ sample(std, in_ct, in_bd, x, 0, 0, 0),
+ out_ct == 3 ? out_palette[out_index].red :
+ sample(pRow, out_ct, out_bd, x, 0, 0, 0),
+ alpha/*component*/, vi.background_red);
+
+ (void)gamma_component_validate("green", &vi,
+ in_ct == 3 ? in_palette[in_index].green :
+ sample(std, in_ct, in_bd, x, 1, 0, 0),
+ out_ct == 3 ? out_palette[out_index].green :
+ sample(pRow, out_ct, out_bd, x, 1, 0, 0),
+ alpha/*component*/, vi.background_green);
+
+ (void)gamma_component_validate("blue", &vi,
+ in_ct == 3 ? in_palette[in_index].blue :
+ sample(std, in_ct, in_bd, x, 2, 0, 0),
+ out_ct == 3 ? out_palette[out_index].blue :
+ sample(pRow, out_ct, out_bd, x, 2, 0, 0),
+ alpha/*component*/, vi.background_blue);
+ }
+ }
+ }
+
+ else if (memcmp(std, pRow, cbRow) != 0)
+ {
+ char msg[64];
+
+ /* No transform is expected on the threshold tests. */
+ sprintf(msg, "gamma: below threshold row %lu changed",
+ (unsigned long)y);
+
+ png_error(pp, msg);
+ }
+ } /* row (y) loop */
+
+ dp->this.ps->validated = 1;
+}
+
+static void PNGCBAPI
+gamma_end(png_structp ppIn, png_infop pi)
+{
+ png_const_structp pp = ppIn;
+ gamma_display *dp = voidcast(gamma_display*, png_get_progressive_ptr(pp));
+
+ if (!dp->this.speed)
+ gamma_image_validate(dp, pp, pi);
+ else
+ dp->this.ps->validated = 1;
+}
+
+/* A single test run checking a gamma transformation.
+ *
+ * maxabs: maximum absolute error as a fraction
+ * maxout: maximum output error in the output units
+ * maxpc: maximum percentage error (as a percentage)
+ */
+static void
+gamma_test(png_modifier *pmIn, const png_byte colour_typeIn,
+ const png_byte bit_depthIn, const int palette_numberIn,
+ const int interlace_typeIn,
+ const double file_gammaIn, const double screen_gammaIn,
+ const png_byte sbitIn, const int threshold_testIn,
+ const char *name,
+ const int use_input_precisionIn, const int scale16In,
+ const int expand16In, const int do_backgroundIn,
+ const png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
+{
+ gamma_display d;
+ context(&pmIn->this, fault);
+
+ gamma_display_init(&d, pmIn, FILEID(colour_typeIn, bit_depthIn,
+ palette_numberIn, interlace_typeIn, 0, 0, 0),
+ file_gammaIn, screen_gammaIn, sbitIn,
+ threshold_testIn, use_input_precisionIn, scale16In,
+ expand16In, do_backgroundIn, bkgd_colorIn, bkgd_gammaIn);
+
+ Try
+ {
+ png_structp pp;
+ png_infop pi;
+ gama_modification gama_mod;
+ srgb_modification srgb_mod;
+ sbit_modification sbit_mod;
+
+ /* For the moment don't use the png_modifier support here. */
+ d.pm->encoding_counter = 0;
+ modifier_set_encoding(d.pm); /* Just resets everything */
+ d.pm->current_gamma = d.file_gamma;
+
+ /* Make an appropriate modifier to set the PNG file gamma to the
+ * given gamma value and the sBIT chunk to the given precision.
+ */
+ d.pm->modifications = NULL;
+ gama_modification_init(&gama_mod, d.pm, d.file_gamma);
+ srgb_modification_init(&srgb_mod, d.pm, 127 /*delete*/);
+ if (d.sbit > 0)
+ sbit_modification_init(&sbit_mod, d.pm, d.sbit);
+
+ modification_reset(d.pm->modifications);
+
+ /* Get a png_struct for reading the image. */
+ pp = set_modifier_for_read(d.pm, &pi, d.this.id, name);
+ standard_palette_init(&d.this);
+
+ /* Introduce the correct read function. */
+ if (d.pm->this.progressive)
+ {
+ /* Share the row function with the standard implementation. */
+ png_set_progressive_read_fn(pp, &d, gamma_info, progressive_row,
+ gamma_end);
+
+ /* Now feed data into the reader until we reach the end: */
+ modifier_progressive_read(d.pm, pp, pi);
+ }
+ else
+ {
+ /* modifier_read expects a png_modifier* */
+ png_set_read_fn(pp, d.pm, modifier_read);
+
+ /* Check the header values: */
+ png_read_info(pp, pi);
+
+ /* Process the 'info' requirements. Only one image is generated */
+ gamma_info_imp(&d, pp, pi);
+
+ sequential_row(&d.this, pp, pi, -1, 0);
+
+ if (!d.this.speed)
+ gamma_image_validate(&d, pp, pi);
+ else
+ d.this.ps->validated = 1;
+ }
+
+ modifier_reset(d.pm);
+
+ if (d.pm->log && !d.threshold_test && !d.this.speed)
+ fprintf(stderr, "%d bit %s %s: max error %f (%.2g, %2g%%)\n",
+ d.this.bit_depth, colour_types[d.this.colour_type], name,
+ d.maxerrout, d.maxerrabs, 100*d.maxerrpc);
+
+ /* Log the summary values too. */
+ if (d.this.colour_type == 0 || d.this.colour_type == 4)
+ {
+ switch (d.this.bit_depth)
+ {
+ case 1:
+ break;
+
+ case 2:
+ if (d.maxerrout > d.pm->error_gray_2)
+ d.pm->error_gray_2 = d.maxerrout;
+
+ break;
+
+ case 4:
+ if (d.maxerrout > d.pm->error_gray_4)
+ d.pm->error_gray_4 = d.maxerrout;
+
+ break;
+
+ case 8:
+ if (d.maxerrout > d.pm->error_gray_8)
+ d.pm->error_gray_8 = d.maxerrout;
+
+ break;
+
+ case 16:
+ if (d.maxerrout > d.pm->error_gray_16)
+ d.pm->error_gray_16 = d.maxerrout;
+
+ break;
+
+ default:
+ png_error(pp, "bad bit depth (internal: 1)");
+ }
+ }
+
+ else if (d.this.colour_type == 2 || d.this.colour_type == 6)
+ {
+ switch (d.this.bit_depth)
+ {
+ case 8:
+
+ if (d.maxerrout > d.pm->error_color_8)
+ d.pm->error_color_8 = d.maxerrout;
+
+ break;
+
+ case 16:
+
+ if (d.maxerrout > d.pm->error_color_16)
+ d.pm->error_color_16 = d.maxerrout;
+
+ break;
+
+ default:
+ png_error(pp, "bad bit depth (internal: 2)");
+ }
+ }
+
+ else if (d.this.colour_type == 3)
+ {
+ if (d.maxerrout > d.pm->error_indexed)
+ d.pm->error_indexed = d.maxerrout;
+ }
+ }
+
+ Catch(fault)
+ modifier_reset(voidcast(png_modifier*,(void*)fault));
+}
+
+static void gamma_threshold_test(png_modifier *pm, png_byte colour_type,
+ png_byte bit_depth, int interlace_type, double file_gamma,
+ double screen_gamma)
+{
+ size_t pos = 0;
+ char name[64];
+ pos = safecat(name, sizeof name, pos, "threshold ");
+ pos = safecatd(name, sizeof name, pos, file_gamma, 3);
+ pos = safecat(name, sizeof name, pos, "/");
+ pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
+
+ (void)gamma_test(pm, colour_type, bit_depth, 0/*palette*/, interlace_type,
+ file_gamma, screen_gamma, 0/*sBIT*/, 1/*threshold test*/, name,
+ 0 /*no input precision*/,
+ 0 /*no scale16*/, 0 /*no expand16*/, 0 /*no background*/, 0 /*hence*/,
+ 0 /*no background gamma*/);
+}
+
+static void
+perform_gamma_threshold_tests(png_modifier *pm)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ unsigned int palette_number = 0;
+
+ /* Don't test more than one instance of each palette - it's pointless, in
+ * fact this test is somewhat excessive since libpng doesn't make this
+ * decision based on colour type or bit depth!
+ *
+ * CHANGED: now test two palettes and, as a side effect, images with and
+ * without tRNS.
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number,
+ pm->test_lbg_gamma_threshold, pm->test_tRNS))
+ if (palette_number < 2)
+ {
+ double test_gamma = 1.0;
+ while (test_gamma >= .4)
+ {
+ /* There's little point testing the interlacing vs non-interlacing,
+ * but this can be set from the command line.
+ */
+ gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
+ test_gamma, 1/test_gamma);
+ test_gamma *= .95;
+ }
+
+ /* And a special test for sRGB */
+ gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
+ .45455, 2.2);
+
+ if (fail(pm))
+ return;
+ }
+}
+
+static void gamma_transform_test(png_modifier *pm,
+ const png_byte colour_type, const png_byte bit_depth,
+ const int palette_number,
+ const int interlace_type, const double file_gamma,
+ const double screen_gamma, const png_byte sbit,
+ const int use_input_precision, const int scale16)
+{
+ size_t pos = 0;
+ char name[64];
+
+ if (sbit != bit_depth && sbit != 0)
+ {
+ pos = safecat(name, sizeof name, pos, "sbit(");
+ pos = safecatn(name, sizeof name, pos, sbit);
+ pos = safecat(name, sizeof name, pos, ") ");
+ }
+
+ else
+ pos = safecat(name, sizeof name, pos, "gamma ");
+
+ if (scale16)
+ pos = safecat(name, sizeof name, pos, "16to8 ");
+
+ pos = safecatd(name, sizeof name, pos, file_gamma, 3);
+ pos = safecat(name, sizeof name, pos, "->");
+ pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
+
+ gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
+ file_gamma, screen_gamma, sbit, 0, name, use_input_precision,
+ scale16, pm->test_gamma_expand16, 0 , 0, 0);
+}
+
+static void perform_gamma_transform_tests(png_modifier *pm)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ unsigned int palette_number = 0;
+
+ while (next_format(&colour_type, &bit_depth, &palette_number,
+ pm->test_lbg_gamma_transform, pm->test_tRNS))
+ {
+ unsigned int i, j;
+
+ for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j)
+ if (i != j)
+ {
+ gamma_transform_test(pm, colour_type, bit_depth, palette_number,
+ pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], 0/*sBIT*/,
+ pm->use_input_precision, 0 /*do not scale16*/);
+
+ if (fail(pm))
+ return;
+ }
+ }
+}
+
+static void perform_gamma_sbit_tests(png_modifier *pm)
+{
+ png_byte sbit;
+
+ /* The only interesting cases are colour and grayscale, alpha is ignored here
+ * for overall speed. Only bit depths where sbit is less than the bit depth
+ * are tested.
+ */
+ for (sbit=pm->sbitlow; sbit<(1<<READ_BDHI); ++sbit)
+ {
+ png_byte colour_type = 0, bit_depth = 0;
+ unsigned int npalette = 0;
+
+ while (next_format(&colour_type, &bit_depth, &npalette,
+ pm->test_lbg_gamma_sbit, pm->test_tRNS))
+ if ((colour_type & PNG_COLOR_MASK_ALPHA) == 0 &&
+ ((colour_type == 3 && sbit < 8) ||
+ (colour_type != 3 && sbit < bit_depth)))
+ {
+ unsigned int i;
+
+ for (i=0; i<pm->ngamma_tests; ++i)
+ {
+ unsigned int j;
+
+ for (j=0; j<pm->ngamma_tests; ++j) if (i != j)
+ {
+ gamma_transform_test(pm, colour_type, bit_depth, npalette,
+ pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+ sbit, pm->use_input_precision_sbit, 0 /*scale16*/);
+
+ if (fail(pm))
+ return;
+ }
+ }
+ }
+ }
+}
+
+/* Note that this requires a 16 bit source image but produces 8 bit output, so
+ * we only need the 16bit write support, but the 16 bit images are only
+ * generated if DO_16BIT is defined.
+ */
+#ifdef DO_16BIT
+static void perform_gamma_scale16_tests(png_modifier *pm)
+{
+# ifndef PNG_MAX_GAMMA_8
+# define PNG_MAX_GAMMA_8 11
+# endif
+# if defined PNG_MAX_GAMMA_8 || PNG_LIBPNG_VER < 10700
+# define SBIT_16_TO_8 PNG_MAX_GAMMA_8
+# else
+# define SBIT_16_TO_8 16
+# endif
+ /* Include the alpha cases here. Note that sbit matches the internal value
+ * used by the library - otherwise we will get spurious errors from the
+ * internal sbit style approximation.
+ *
+ * The threshold test is here because otherwise the 16 to 8 conversion will
+ * proceed *without* gamma correction, and the tests above will fail (but not
+ * by much) - this could be fixed, it only appears with the -g option.
+ */
+ unsigned int i, j;
+ for (i=0; i<pm->ngamma_tests; ++i)
+ {
+ for (j=0; j<pm->ngamma_tests; ++j)
+ {
+ if (i != j &&
+ fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD)
+ {
+ gamma_transform_test(pm, 0, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+
+ gamma_transform_test(pm, 2, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+
+ gamma_transform_test(pm, 4, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+
+ gamma_transform_test(pm, 6, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], SBIT_16_TO_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+ }
+ }
+ }
+}
+#endif /* 16 to 8 bit conversion */
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+ defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+static void gamma_composition_test(png_modifier *pm,
+ const png_byte colour_type, const png_byte bit_depth,
+ const int palette_number,
+ const int interlace_type, const double file_gamma,
+ const double screen_gamma,
+ const int use_input_precision, const int do_background,
+ const int expand_16)
+{
+ size_t pos = 0;
+ png_const_charp base;
+ double bg;
+ char name[128];
+ png_color_16 background;
+
+ /* Make up a name and get an appropriate background gamma value. */
+ switch (do_background)
+ {
+ default:
+ base = "";
+ bg = 4; /* should not be used */
+ break;
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ base = " bckg(Screen):";
+ bg = 1/screen_gamma;
+ break;
+ case PNG_BACKGROUND_GAMMA_FILE:
+ base = " bckg(File):";
+ bg = file_gamma;
+ break;
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ base = " bckg(Unique):";
+ /* This tests the handling of a unique value, the math is such that the
+ * value tends to be <1, but is neither screen nor file (even if they
+ * match!)
+ */
+ bg = (file_gamma + screen_gamma) / 3;
+ break;
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_PNG:
+ base = " alpha(PNG)";
+ bg = 4; /* should not be used */
+ break;
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD:
+ base = " alpha(Porter-Duff)";
+ bg = 4; /* should not be used */
+ break;
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_OPTIMIZED:
+ base = " alpha(Optimized)";
+ bg = 4; /* should not be used */
+ break;
+ case ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN:
+ base = " alpha(Broken)";
+ bg = 4; /* should not be used */
+ break;
+#endif
+ }
+
+ /* Use random background values - the background is always presented in the
+ * output space (8 or 16 bit components).
+ */
+ if (expand_16 || bit_depth == 16)
+ {
+ png_uint_32 r = random_32();
+
+ background.red = (png_uint_16)r;
+ background.green = (png_uint_16)(r >> 16);
+ r = random_32();
+ background.blue = (png_uint_16)r;
+ background.gray = (png_uint_16)(r >> 16);
+
+ /* In earlier libpng versions, those where DIGITIZE is set, any background
+ * gamma correction in the expand16 case was done using 8-bit gamma
+ * correction tables, resulting in larger errors. To cope with those
+ * cases use a 16-bit background value which will handle this gamma
+ * correction.
+ */
+# if DIGITIZE
+ if (expand_16 && (do_background == PNG_BACKGROUND_GAMMA_UNIQUE ||
+ do_background == PNG_BACKGROUND_GAMMA_FILE) &&
+ fabs(bg*screen_gamma-1) > PNG_GAMMA_THRESHOLD)
+ {
+ /* The background values will be looked up in an 8-bit table to do
+ * the gamma correction, so only select values which are an exact
+ * match for the 8-bit table entries:
+ */
+ background.red = (png_uint_16)((background.red >> 8) * 257);
+ background.green = (png_uint_16)((background.green >> 8) * 257);
+ background.blue = (png_uint_16)((background.blue >> 8) * 257);
+ background.gray = (png_uint_16)((background.gray >> 8) * 257);
+ }
+# endif
+ }
+
+ else /* 8 bit colors */
+ {
+ png_uint_32 r = random_32();
+
+ background.red = (png_byte)r;
+ background.green = (png_byte)(r >> 8);
+ background.blue = (png_byte)(r >> 16);
+ background.gray = (png_byte)(r >> 24);
+ }
+
+ background.index = 193; /* rgb(193,193,193) to detect errors */
+
+ if (!(colour_type & PNG_COLOR_MASK_COLOR))
+ {
+ /* Because, currently, png_set_background is always called with
+ * 'need_expand' false in this case and because the gamma test itself
+ * doesn't cause an expand to 8-bit for lower bit depths the colour must
+ * be reduced to the correct range.
+ */
+ if (bit_depth < 8)
+ background.gray &= (png_uint_16)((1U << bit_depth)-1);
+
+ /* Grayscale input, we do not convert to RGB (TBD), so we must set the
+ * background to gray - else libpng seems to fail.
+ */
+ background.red = background.green = background.blue = background.gray;
+ }
+
+ pos = safecat(name, sizeof name, pos, "gamma ");
+ pos = safecatd(name, sizeof name, pos, file_gamma, 3);
+ pos = safecat(name, sizeof name, pos, "->");
+ pos = safecatd(name, sizeof name, pos, screen_gamma, 3);
+
+ pos = safecat(name, sizeof name, pos, base);
+ if (do_background < ALPHA_MODE_OFFSET)
+ {
+ /* Include the background color and gamma in the name: */
+ pos = safecat(name, sizeof name, pos, "(");
+ /* This assumes no expand gray->rgb - the current code won't handle that!
+ */
+ if (colour_type & PNG_COLOR_MASK_COLOR)
+ {
+ pos = safecatn(name, sizeof name, pos, background.red);
+ pos = safecat(name, sizeof name, pos, ",");
+ pos = safecatn(name, sizeof name, pos, background.green);
+ pos = safecat(name, sizeof name, pos, ",");
+ pos = safecatn(name, sizeof name, pos, background.blue);
+ }
+ else
+ pos = safecatn(name, sizeof name, pos, background.gray);
+ pos = safecat(name, sizeof name, pos, ")^");
+ pos = safecatd(name, sizeof name, pos, bg, 3);
+ }
+
+ gamma_test(pm, colour_type, bit_depth, palette_number, interlace_type,
+ file_gamma, screen_gamma, 0/*sBIT*/, 0, name, use_input_precision,
+ 0/*strip 16*/, expand_16, do_background, &background, bg);
+}
+
+
+static void
+perform_gamma_composition_tests(png_modifier *pm, int do_background,
+ int expand_16)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ unsigned int palette_number = 0;
+
+ /* Skip the non-alpha cases - there is no setting of a transparency colour at
+ * present.
+ *
+ * TODO: incorrect; the palette case sets tRNS and, now RGB and gray do,
+ * however the palette case fails miserably so is commented out below.
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number,
+ pm->test_lbg_gamma_composition, pm->test_tRNS))
+ if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0
+#if 0 /* TODO: FIXME */
+ /*TODO: FIXME: this should work */
+ || colour_type == 3
+#endif
+ || (colour_type != 3 && palette_number != 0))
+ {
+ unsigned int i, j;
+
+ /* Don't skip the i==j case here - it's relevant. */
+ for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j)
+ {
+ gamma_composition_test(pm, colour_type, bit_depth, palette_number,
+ pm->interlace_type, 1/pm->gammas[i], pm->gammas[j],
+ pm->use_input_precision, do_background, expand_16);
+
+ if (fail(pm))
+ return;
+ }
+ }
+}
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE */
+
+static void
+init_gamma_errors(png_modifier *pm)
+{
+ /* Use -1 to catch tests that were not actually run */
+ pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = -1.;
+ pm->error_color_8 = -1.;
+ pm->error_indexed = -1.;
+ pm->error_gray_16 = pm->error_color_16 = -1.;
+}
+
+static void
+print_one(const char *leader, double err)
+{
+ if (err != -1.)
+ printf(" %s %.5f\n", leader, err);
+}
+
+static void
+summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth,
+ int indexed)
+{
+ fflush(stderr);
+
+ if (who)
+ printf("\nGamma correction with %s:\n", who);
+
+ else
+ printf("\nBasic gamma correction:\n");
+
+ if (low_bit_depth)
+ {
+ print_one(" 2 bit gray: ", pm->error_gray_2);
+ print_one(" 4 bit gray: ", pm->error_gray_4);
+ print_one(" 8 bit gray: ", pm->error_gray_8);
+ print_one(" 8 bit color:", pm->error_color_8);
+ if (indexed)
+ print_one(" indexed: ", pm->error_indexed);
+ }
+
+ print_one("16 bit gray: ", pm->error_gray_16);
+ print_one("16 bit color:", pm->error_color_16);
+
+ fflush(stdout);
+}
+
+static void
+perform_gamma_test(png_modifier *pm, int summary)
+{
+ /*TODO: remove this*/
+ /* Save certain values for the temporary overrides below. */
+ unsigned int calculations_use_input_precision =
+ pm->calculations_use_input_precision;
+# ifdef PNG_READ_BACKGROUND_SUPPORTED
+ double maxout8 = pm->maxout8;
+# endif
+
+ /* First some arbitrary no-transform tests: */
+ if (!pm->this.speed && pm->test_gamma_threshold)
+ {
+ perform_gamma_threshold_tests(pm);
+
+ if (fail(pm))
+ return;
+ }
+
+ /* Now some real transforms. */
+ if (pm->test_gamma_transform)
+ {
+ if (summary)
+ {
+ fflush(stderr);
+ printf("Gamma correction error summary\n\n");
+ printf("The printed value is the maximum error in the pixel values\n");
+ printf("calculated by the libpng gamma correction code. The error\n");
+ printf("is calculated as the difference between the output pixel\n");
+ printf("value (always an integer) and the ideal value from the\n");
+ printf("libpng specification (typically not an integer).\n\n");
+
+ printf("Expect this value to be less than .5 for 8 bit formats,\n");
+ printf("less than 1 for formats with fewer than 8 bits and a small\n");
+ printf("number (typically less than 5) for the 16 bit formats.\n");
+ printf("For performance reasons the value for 16 bit formats\n");
+ printf("increases when the image file includes an sBIT chunk.\n");
+ fflush(stdout);
+ }
+
+ init_gamma_errors(pm);
+ /*TODO: remove this. Necessary because the current libpng
+ * implementation works in 8 bits:
+ */
+ if (pm->test_gamma_expand16)
+ pm->calculations_use_input_precision = 1;
+ perform_gamma_transform_tests(pm);
+ if (!calculations_use_input_precision)
+ pm->calculations_use_input_precision = 0;
+
+ if (summary)
+ summarize_gamma_errors(pm, 0/*who*/, 1/*low bit depth*/, 1/*indexed*/);
+
+ if (fail(pm))
+ return;
+ }
+
+ /* The sbit tests produce much larger errors: */
+ if (pm->test_gamma_sbit)
+ {
+ init_gamma_errors(pm);
+ perform_gamma_sbit_tests(pm);
+
+ if (summary)
+ summarize_gamma_errors(pm, "sBIT", pm->sbitlow < 8U, 1/*indexed*/);
+
+ if (fail(pm))
+ return;
+ }
+
+#ifdef DO_16BIT /* Should be READ_16BIT_SUPPORTED */
+ if (pm->test_gamma_scale16)
+ {
+ /* The 16 to 8 bit strip operations: */
+ init_gamma_errors(pm);
+ perform_gamma_scale16_tests(pm);
+
+ if (summary)
+ {
+ fflush(stderr);
+ printf("\nGamma correction with 16 to 8 bit reduction:\n");
+ printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
+ printf(" 16 bit color: %.5f\n", pm->error_color_16);
+ fflush(stdout);
+ }
+
+ if (fail(pm))
+ return;
+ }
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ if (pm->test_gamma_background)
+ {
+ init_gamma_errors(pm);
+
+ /*TODO: remove this. Necessary because the current libpng
+ * implementation works in 8 bits:
+ */
+ if (pm->test_gamma_expand16)
+ {
+ pm->calculations_use_input_precision = 1;
+ pm->maxout8 = .499; /* because the 16 bit background is smashed */
+ }
+ perform_gamma_composition_tests(pm, PNG_BACKGROUND_GAMMA_UNIQUE,
+ pm->test_gamma_expand16);
+ if (!calculations_use_input_precision)
+ pm->calculations_use_input_precision = 0;
+ pm->maxout8 = maxout8;
+
+ if (summary)
+ summarize_gamma_errors(pm, "background", 1, 0/*indexed*/);
+
+ if (fail(pm))
+ return;
+ }
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ if (pm->test_gamma_alpha_mode)
+ {
+ int do_background;
+
+ init_gamma_errors(pm);
+
+ /*TODO: remove this. Necessary because the current libpng
+ * implementation works in 8 bits:
+ */
+ if (pm->test_gamma_expand16)
+ pm->calculations_use_input_precision = 1;
+ for (do_background = ALPHA_MODE_OFFSET + PNG_ALPHA_STANDARD;
+ do_background <= ALPHA_MODE_OFFSET + PNG_ALPHA_BROKEN && !fail(pm);
+ ++do_background)
+ perform_gamma_composition_tests(pm, do_background,
+ pm->test_gamma_expand16);
+ if (!calculations_use_input_precision)
+ pm->calculations_use_input_precision = 0;
+
+ if (summary)
+ summarize_gamma_errors(pm, "alpha mode", 1, 0/*indexed*/);
+
+ if (fail(pm))
+ return;
+ }
+#endif
+}
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED */
+
+/* INTERLACE MACRO VALIDATION */
+/* This is copied verbatim from the specification, it is simply the pass
+ * number in which each pixel in each 8x8 tile appears. The array must
+ * be indexed adam7[y][x] and notice that the pass numbers are based at
+ * 1, not 0 - the base libpng uses.
+ */
+static const
+png_byte adam7[8][8] =
+{
+ { 1,6,4,6,2,6,4,6 },
+ { 7,7,7,7,7,7,7,7 },
+ { 5,6,5,6,5,6,5,6 },
+ { 7,7,7,7,7,7,7,7 },
+ { 3,6,4,6,3,6,4,6 },
+ { 7,7,7,7,7,7,7,7 },
+ { 5,6,5,6,5,6,5,6 },
+ { 7,7,7,7,7,7,7,7 }
+};
+
+/* This routine validates all the interlace support macros in png.h for
+ * a variety of valid PNG widths and heights. It uses a number of similarly
+ * named internal routines that feed off the above array.
+ */
+static png_uint_32
+png_pass_start_row(int pass)
+{
+ int x, y;
+ ++pass;
+ for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ return y;
+ return 0xf;
+}
+
+static png_uint_32
+png_pass_start_col(int pass)
+{
+ int x, y;
+ ++pass;
+ for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ return x;
+ return 0xf;
+}
+
+static int
+png_pass_row_shift(int pass)
+{
+ int x, y, base=(-1), inc=8;
+ ++pass;
+ for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ {
+ if (base == (-1))
+ base = y;
+ else if (base == y)
+ {}
+ else if (inc == y-base)
+ base=y;
+ else if (inc == 8)
+ inc = y-base, base=y;
+ else if (inc != y-base)
+ return 0xff; /* error - more than one 'inc' value! */
+ }
+
+ if (base == (-1)) return 0xfe; /* error - no row in pass! */
+
+ /* The shift is always 1, 2 or 3 - no pass has all the rows! */
+ switch (inc)
+ {
+case 2: return 1;
+case 4: return 2;
+case 8: return 3;
+default: break;
+ }
+
+ /* error - unrecognized 'inc' */
+ return (inc << 8) + 0xfd;
+}
+
+static int
+png_pass_col_shift(int pass)
+{
+ int x, y, base=(-1), inc=8;
+ ++pass;
+ for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ {
+ if (base == (-1))
+ base = x;
+ else if (base == x)
+ {}
+ else if (inc == x-base)
+ base=x;
+ else if (inc == 8)
+ inc = x-base, base=x;
+ else if (inc != x-base)
+ return 0xff; /* error - more than one 'inc' value! */
+ }
+
+ if (base == (-1)) return 0xfe; /* error - no row in pass! */
+
+ /* The shift is always 1, 2 or 3 - no pass has all the rows! */
+ switch (inc)
+ {
+case 1: return 0; /* pass 7 has all the columns */
+case 2: return 1;
+case 4: return 2;
+case 8: return 3;
+default: break;
+ }
+
+ /* error - unrecognized 'inc' */
+ return (inc << 8) + 0xfd;
+}
+
+static png_uint_32
+png_row_from_pass_row(png_uint_32 yIn, int pass)
+{
+ /* By examination of the array: */
+ switch (pass)
+ {
+case 0: return yIn * 8;
+case 1: return yIn * 8;
+case 2: return yIn * 8 + 4;
+case 3: return yIn * 4;
+case 4: return yIn * 4 + 2;
+case 5: return yIn * 2;
+case 6: return yIn * 2 + 1;
+default: break;
+ }
+
+ return 0xff; /* bad pass number */
+}
+
+static png_uint_32
+png_col_from_pass_col(png_uint_32 xIn, int pass)
+{
+ /* By examination of the array: */
+ switch (pass)
+ {
+case 0: return xIn * 8;
+case 1: return xIn * 8 + 4;
+case 2: return xIn * 4;
+case 3: return xIn * 4 + 2;
+case 4: return xIn * 2;
+case 5: return xIn * 2 + 1;
+case 6: return xIn;
+default: break;
+ }
+
+ return 0xff; /* bad pass number */
+}
+
+static int
+png_row_in_interlace_pass(png_uint_32 y, int pass)
+{
+ /* Is row 'y' in pass 'pass'? */
+ int x;
+ y &= 7;
+ ++pass;
+ for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ return 1;
+
+ return 0;
+}
+
+static int
+png_col_in_interlace_pass(png_uint_32 x, int pass)
+{
+ /* Is column 'x' in pass 'pass'? */
+ int y;
+ x &= 7;
+ ++pass;
+ for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ return 1;
+
+ return 0;
+}
+
+static png_uint_32
+png_pass_rows(png_uint_32 height, int pass)
+{
+ png_uint_32 tiles = height>>3;
+ png_uint_32 rows = 0;
+ unsigned int x, y;
+
+ height &= 7;
+ ++pass;
+ for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ {
+ rows += tiles;
+ if (y < height) ++rows;
+ break; /* i.e. break the 'x', column, loop. */
+ }
+
+ return rows;
+}
+
+static png_uint_32
+png_pass_cols(png_uint_32 width, int pass)
+{
+ png_uint_32 tiles = width>>3;
+ png_uint_32 cols = 0;
+ unsigned int x, y;
+
+ width &= 7;
+ ++pass;
+ for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ {
+ cols += tiles;
+ if (x < width) ++cols;
+ break; /* i.e. break the 'y', row, loop. */
+ }
+
+ return cols;
+}
+
+static void
+perform_interlace_macro_validation(void)
+{
+ /* The macros to validate, first those that depend only on pass:
+ *
+ * PNG_PASS_START_ROW(pass)
+ * PNG_PASS_START_COL(pass)
+ * PNG_PASS_ROW_SHIFT(pass)
+ * PNG_PASS_COL_SHIFT(pass)
+ */
+ int pass;
+
+ for (pass=0; pass<7; ++pass)
+ {
+ png_uint_32 m, f, v;
+
+ m = PNG_PASS_START_ROW(pass);
+ f = png_pass_start_row(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_PASS_START_COL(pass);
+ f = png_pass_start_col(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_PASS_ROW_SHIFT(pass);
+ f = png_pass_row_shift(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_PASS_COL_SHIFT(pass);
+ f = png_pass_col_shift(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
+ exit(99);
+ }
+
+ /* Macros that depend on the image or sub-image height too:
+ *
+ * PNG_PASS_ROWS(height, pass)
+ * PNG_PASS_COLS(width, pass)
+ * PNG_ROW_FROM_PASS_ROW(yIn, pass)
+ * PNG_COL_FROM_PASS_COL(xIn, pass)
+ * PNG_ROW_IN_INTERLACE_PASS(y, pass)
+ * PNG_COL_IN_INTERLACE_PASS(x, pass)
+ */
+ for (v=0;;)
+ {
+ /* First the base 0 stuff: */
+ m = PNG_ROW_FROM_PASS_ROW(v, pass);
+ f = png_row_from_pass_row(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_COL_FROM_PASS_COL(v, pass);
+ f = png_col_from_pass_col(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
+ f = png_row_in_interlace_pass(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_COL_IN_INTERLACE_PASS(v, pass);
+ f = png_col_in_interlace_pass(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
+
+ /* Then the base 1 stuff: */
+ ++v;
+ m = PNG_PASS_ROWS(v, pass);
+ f = png_pass_rows(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
+
+ m = PNG_PASS_COLS(v, pass);
+ f = png_pass_cols(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(99);
+ }
+
+ /* Move to the next v - the stepping algorithm starts skipping
+ * values above 1024.
+ */
+ if (v > 1024)
+ {
+ if (v == PNG_UINT_31_MAX)
+ break;
+
+ v = (v << 1) ^ v;
+ if (v >= PNG_UINT_31_MAX)
+ v = PNG_UINT_31_MAX-1;
+ }
+ }
+ }
+}
+
+/* Test color encodings. These values are back-calculated from the published
+ * chromaticities. The values are accurate to about 14 decimal places; 15 are
+ * given. These values are much more accurate than the ones given in the spec,
+ * which typically don't exceed 4 decimal places. This allows testing of the
+ * libpng code to its theoretical accuracy of 4 decimal places. (If pngvalid
+ * used the published errors the 'slack' permitted would have to be +/-.5E-4 or
+ * more.)
+ *
+ * The png_modifier code assumes that encodings[0] is sRGB and treats it
+ * specially: do not change the first entry in this list!
+ */
+static const color_encoding test_encodings[] =
+{
+/* sRGB: must be first in this list! */
+/*gamma:*/ { 1/2.2,
+/*red: */ { 0.412390799265959, 0.212639005871510, 0.019330818715592 },
+/*green:*/ { 0.357584339383878, 0.715168678767756, 0.119194779794626 },
+/*blue: */ { 0.180480788401834, 0.072192315360734, 0.950532152249660} },
+/* Kodak ProPhoto (wide gamut) */
+/*gamma:*/ { 1/1.6 /*approximate: uses 1.8 power law compared to sRGB 2.4*/,
+/*red: */ { 0.797760489672303, 0.288071128229293, 0.000000000000000 },
+/*green:*/ { 0.135185837175740, 0.711843217810102, 0.000000000000000 },
+/*blue: */ { 0.031349349581525, 0.000085653960605, 0.825104602510460} },
+/* Adobe RGB (1998) */
+/*gamma:*/ { 1/(2+51./256),
+/*red: */ { 0.576669042910131, 0.297344975250536, 0.027031361386412 },
+/*green:*/ { 0.185558237906546, 0.627363566255466, 0.070688852535827 },
+/*blue: */ { 0.188228646234995, 0.075291458493998, 0.991337536837639} },
+/* Adobe Wide Gamut RGB */
+/*gamma:*/ { 1/(2+51./256),
+/*red: */ { 0.716500716779386, 0.258728243040113, 0.000000000000000 },
+/*green:*/ { 0.101020574397477, 0.724682314948566, 0.051211818965388 },
+/*blue: */ { 0.146774385252705, 0.016589442011321, 0.773892783545073} },
+/* Fake encoding which selects just the green channel */
+/*gamma:*/ { 1.45/2.2, /* the 'Mac' gamma */
+/*red: */ { 0.716500716779386, 0.000000000000000, 0.000000000000000 },
+/*green:*/ { 0.101020574397477, 1.000000000000000, 0.051211818965388 },
+/*blue: */ { 0.146774385252705, 0.000000000000000, 0.773892783545073} },
+};
+
+/* signal handler
+ *
+ * This attempts to trap signals and escape without crashing. It needs a
+ * context pointer so that it can throw an exception (call longjmp) to recover
+ * from the condition; this is handled by making the png_modifier used by 'main'
+ * into a global variable.
+ */
+static png_modifier pm;
+
+static void signal_handler(int signum)
+{
+
+ size_t pos = 0;
+ char msg[64];
+
+ pos = safecat(msg, sizeof msg, pos, "caught signal: ");
+
+ switch (signum)
+ {
+ case SIGABRT:
+ pos = safecat(msg, sizeof msg, pos, "abort");
+ break;
+
+ case SIGFPE:
+ pos = safecat(msg, sizeof msg, pos, "floating point exception");
+ break;
+
+ case SIGILL:
+ pos = safecat(msg, sizeof msg, pos, "illegal instruction");
+ break;
+
+ case SIGINT:
+ pos = safecat(msg, sizeof msg, pos, "interrupt");
+ break;
+
+ case SIGSEGV:
+ pos = safecat(msg, sizeof msg, pos, "invalid memory access");
+ break;
+
+ case SIGTERM:
+ pos = safecat(msg, sizeof msg, pos, "termination request");
+ break;
+
+ default:
+ pos = safecat(msg, sizeof msg, pos, "unknown ");
+ pos = safecatn(msg, sizeof msg, pos, signum);
+ break;
+ }
+
+ store_log(&pm.this, NULL/*png_structp*/, msg, 1/*error*/);
+
+ /* And finally throw an exception so we can keep going, unless this is
+ * SIGTERM in which case stop now.
+ */
+ if (signum != SIGTERM)
+ {
+ struct exception_context *the_exception_context =
+ &pm.this.exception_context;
+
+ Throw &pm.this;
+ }
+
+ else
+ exit(1);
+}
+
+/* main program */
+int main(int argc, char **argv)
+{
+ int summary = 1; /* Print the error summary at the end */
+ int memstats = 0; /* Print memory statistics at the end */
+
+ /* Create the given output file on success: */
+ const char *touch = NULL;
+
+ /* This is an array of standard gamma values (believe it or not I've seen
+ * every one of these mentioned somewhere.)
+ *
+ * In the following list the most useful values are first!
+ */
+ static double
+ gammas[]={2.2, 1.0, 2.2/1.45, 1.8, 1.5, 2.4, 2.5, 2.62, 2.9};
+
+ /* This records the command and arguments: */
+ size_t cp = 0;
+ char command[1024];
+
+ anon_context(&pm.this);
+
+ gnu_volatile(summary)
+ gnu_volatile(memstats)
+ gnu_volatile(touch)
+
+ /* Add appropriate signal handlers, just the ANSI specified ones: */
+ signal(SIGABRT, signal_handler);
+ signal(SIGFPE, signal_handler);
+ signal(SIGILL, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGSEGV, signal_handler);
+ signal(SIGTERM, signal_handler);
+
+#ifdef HAVE_FEENABLEEXCEPT
+ /* Only required to enable FP exceptions on platforms where they start off
+ * disabled; this is not necessary but if it is not done pngvalid will likely
+ * end up ignoring FP conditions that other platforms fault.
+ */
+ feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
+ modifier_init(&pm);
+
+ /* Preallocate the image buffer, because we know how big it needs to be,
+ * note that, for testing purposes, it is deliberately mis-aligned by tag
+ * bytes either side. All rows have an additional five bytes of padding for
+ * overwrite checking.
+ */
+ store_ensure_image(&pm.this, NULL, 2, TRANSFORM_ROWMAX, TRANSFORM_HEIGHTMAX);
+
+ /* Don't give argv[0], it's normally some horrible libtool string: */
+ cp = safecat(command, sizeof command, cp, "pngvalid");
+
+ /* Default to error on warning: */
+ pm.this.treat_warnings_as_errors = 1;
+
+ /* Default assume_16_bit_calculations appropriately; this tells the checking
+ * code that 16-bit arithmetic is used for 8-bit samples when it would make a
+ * difference.
+ */
+ pm.assume_16_bit_calculations = PNG_LIBPNG_VER >= 10700;
+
+ /* Currently 16 bit expansion happens at the end of the pipeline, so the
+ * calculations are done in the input bit depth not the output.
+ *
+ * TODO: fix this
+ */
+ pm.calculations_use_input_precision = 1U;
+
+ /* Store the test gammas */
+ pm.gammas = gammas;
+ pm.ngammas = ARRAY_SIZE(gammas);
+ pm.ngamma_tests = 0; /* default to off */
+
+ /* Low bit depth gray images don't do well in the gamma tests, until
+ * this is fixed turn them off for some gamma cases:
+ */
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ pm.test_tRNS = 1;
+# endif
+ pm.test_lbg = PNG_LIBPNG_VER >= 10600;
+ pm.test_lbg_gamma_threshold = 1;
+ pm.test_lbg_gamma_transform = PNG_LIBPNG_VER >= 10600;
+ pm.test_lbg_gamma_sbit = 1;
+ pm.test_lbg_gamma_composition = PNG_LIBPNG_VER >= 10700;
+
+ /* And the test encodings */
+ pm.encodings = test_encodings;
+ pm.nencodings = ARRAY_SIZE(test_encodings);
+
+# if PNG_LIBPNG_VER < 10700
+ pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+# else
+ pm.sbitlow = 1U;
+# endif
+
+ /* The following allows results to pass if they correspond to anything in the
+ * transformed range [input-.5,input+.5]; this is is required because of the
+ * way libpng treates the 16_TO_8 flag when building the gamma tables in
+ * releases up to 1.6.0.
+ *
+ * TODO: review this
+ */
+ pm.use_input_precision_16to8 = 1U;
+ pm.use_input_precision_sbit = 1U; /* because libpng now rounds sBIT */
+
+ /* Some default values (set the behavior for 'make check' here).
+ * These values simply control the maximum error permitted in the gamma
+ * transformations. The practial limits for human perception are described
+ * below (the setting for maxpc16), however for 8 bit encodings it isn't
+ * possible to meet the accepted capabilities of human vision - i.e. 8 bit
+ * images can never be good enough, regardless of encoding.
+ */
+ pm.maxout8 = .1; /* Arithmetic error in *encoded* value */
+ pm.maxabs8 = .00005; /* 1/20000 */
+ pm.maxcalc8 = 1./255; /* +/-1 in 8 bits for compose errors */
+ pm.maxpc8 = .499; /* I.e., .499% fractional error */
+ pm.maxout16 = .499; /* Error in *encoded* value */
+ pm.maxabs16 = .00005;/* 1/20000 */
+ pm.maxcalc16 =1./65535;/* +/-1 in 16 bits for compose errors */
+# if PNG_LIBPNG_VER < 10700
+ pm.maxcalcG = 1./((1<<PNG_MAX_GAMMA_8)-1);
+# else
+ pm.maxcalcG = 1./((1<<16)-1);
+# endif
+
+ /* NOTE: this is a reasonable perceptual limit. We assume that humans can
+ * perceive light level differences of 1% over a 100:1 range, so we need to
+ * maintain 1 in 10000 accuracy (in linear light space), which is what the
+ * following guarantees. It also allows significantly higher errors at
+ * higher 16 bit values, which is important for performance. The actual
+ * maximum 16 bit error is about +/-1.9 in the fixed point implementation but
+ * this is only allowed for values >38149 by the following:
+ */
+ pm.maxpc16 = .005; /* I.e., 1/200% - 1/20000 */
+
+ /* Now parse the command line options. */
+ while (--argc >= 1)
+ {
+ int catmore = 0; /* Set if the argument has an argument. */
+
+ /* Record each argument for posterity: */
+ cp = safecat(command, sizeof command, cp, " ");
+ cp = safecat(command, sizeof command, cp, *++argv);
+
+ if (strcmp(*argv, "-v") == 0)
+ pm.this.verbose = 1;
+
+ else if (strcmp(*argv, "-l") == 0)
+ pm.log = 1;
+
+ else if (strcmp(*argv, "-q") == 0)
+ summary = pm.this.verbose = pm.log = 0;
+
+ else if (strcmp(*argv, "-w") == 0 ||
+ strcmp(*argv, "--strict") == 0)
+ pm.this.treat_warnings_as_errors = 0;
+
+ else if (strcmp(*argv, "--speed") == 0)
+ pm.this.speed = 1, pm.ngamma_tests = pm.ngammas, pm.test_standard = 0,
+ summary = 0;
+
+ else if (strcmp(*argv, "--memory") == 0)
+ memstats = 1;
+
+ else if (strcmp(*argv, "--size") == 0)
+ pm.test_size = 1;
+
+ else if (strcmp(*argv, "--nosize") == 0)
+ pm.test_size = 0;
+
+ else if (strcmp(*argv, "--standard") == 0)
+ pm.test_standard = 1;
+
+ else if (strcmp(*argv, "--nostandard") == 0)
+ pm.test_standard = 0;
+
+ else if (strcmp(*argv, "--transform") == 0)
+ pm.test_transform = 1;
+
+ else if (strcmp(*argv, "--notransform") == 0)
+ pm.test_transform = 0;
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ else if (strncmp(*argv, "--transform-disable=",
+ sizeof "--transform-disable") == 0)
+ {
+ pm.test_transform = 1;
+ transform_disable(*argv + sizeof "--transform-disable");
+ }
+
+ else if (strncmp(*argv, "--transform-enable=",
+ sizeof "--transform-enable") == 0)
+ {
+ pm.test_transform = 1;
+ transform_enable(*argv + sizeof "--transform-enable");
+ }
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+ else if (strcmp(*argv, "--gamma") == 0)
+ {
+ /* Just do two gamma tests here (2.2 and linear) for speed: */
+ pm.ngamma_tests = 2U;
+ pm.test_gamma_threshold = 1;
+ pm.test_gamma_transform = 1;
+ pm.test_gamma_sbit = 1;
+ pm.test_gamma_scale16 = 1;
+ pm.test_gamma_background = 1; /* composition */
+ pm.test_gamma_alpha_mode = 1;
+ }
+
+ else if (strcmp(*argv, "--nogamma") == 0)
+ pm.ngamma_tests = 0;
+
+ else if (strcmp(*argv, "--gamma-threshold") == 0)
+ pm.ngamma_tests = 2U, pm.test_gamma_threshold = 1;
+
+ else if (strcmp(*argv, "--nogamma-threshold") == 0)
+ pm.test_gamma_threshold = 0;
+
+ else if (strcmp(*argv, "--gamma-transform") == 0)
+ pm.ngamma_tests = 2U, pm.test_gamma_transform = 1;
+
+ else if (strcmp(*argv, "--nogamma-transform") == 0)
+ pm.test_gamma_transform = 0;
+
+ else if (strcmp(*argv, "--gamma-sbit") == 0)
+ pm.ngamma_tests = 2U, pm.test_gamma_sbit = 1;
+
+ else if (strcmp(*argv, "--nogamma-sbit") == 0)
+ pm.test_gamma_sbit = 0;
+
+ else if (strcmp(*argv, "--gamma-16-to-8") == 0)
+ pm.ngamma_tests = 2U, pm.test_gamma_scale16 = 1;
+
+ else if (strcmp(*argv, "--nogamma-16-to-8") == 0)
+ pm.test_gamma_scale16 = 0;
+
+ else if (strcmp(*argv, "--gamma-background") == 0)
+ pm.ngamma_tests = 2U, pm.test_gamma_background = 1;
+
+ else if (strcmp(*argv, "--nogamma-background") == 0)
+ pm.test_gamma_background = 0;
+
+ else if (strcmp(*argv, "--gamma-alpha-mode") == 0)
+ pm.ngamma_tests = 2U, pm.test_gamma_alpha_mode = 1;
+
+ else if (strcmp(*argv, "--nogamma-alpha-mode") == 0)
+ pm.test_gamma_alpha_mode = 0;
+
+ else if (strcmp(*argv, "--expand16") == 0)
+ pm.test_gamma_expand16 = 1;
+
+ else if (strcmp(*argv, "--noexpand16") == 0)
+ pm.test_gamma_expand16 = 0;
+
+ else if (strcmp(*argv, "--low-depth-gray") == 0)
+ pm.test_lbg = pm.test_lbg_gamma_threshold =
+ pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
+ pm.test_lbg_gamma_composition = 1;
+
+ else if (strcmp(*argv, "--nolow-depth-gray") == 0)
+ pm.test_lbg = pm.test_lbg_gamma_threshold =
+ pm.test_lbg_gamma_transform = pm.test_lbg_gamma_sbit =
+ pm.test_lbg_gamma_composition = 0;
+
+# ifdef PNG_WRITE_tRNS_SUPPORTED
+ else if (strcmp(*argv, "--tRNS") == 0)
+ pm.test_tRNS = 1;
+# endif
+
+ else if (strcmp(*argv, "--notRNS") == 0)
+ pm.test_tRNS = 0;
+
+ else if (strcmp(*argv, "--more-gammas") == 0)
+ pm.ngamma_tests = 3U;
+
+ else if (strcmp(*argv, "--all-gammas") == 0)
+ pm.ngamma_tests = pm.ngammas;
+
+ else if (strcmp(*argv, "--progressive-read") == 0)
+ pm.this.progressive = 1;
+
+ else if (strcmp(*argv, "--use-update-info") == 0)
+ ++pm.use_update_info; /* Can call multiple times */
+
+ else if (strcmp(*argv, "--interlace") == 0)
+ {
+# if CAN_WRITE_INTERLACE
+ pm.interlace_type = PNG_INTERLACE_ADAM7;
+# else /* !CAN_WRITE_INTERLACE */
+ fprintf(stderr, "pngvalid: no write interlace support\n");
+ return SKIP;
+# endif /* !CAN_WRITE_INTERLACE */
+ }
+
+ else if (strcmp(*argv, "--use-input-precision") == 0)
+ pm.use_input_precision = 1U;
+
+ else if (strcmp(*argv, "--use-calculation-precision") == 0)
+ pm.use_input_precision = 0;
+
+ else if (strcmp(*argv, "--calculations-use-input-precision") == 0)
+ pm.calculations_use_input_precision = 1U;
+
+ else if (strcmp(*argv, "--assume-16-bit-calculations") == 0)
+ pm.assume_16_bit_calculations = 1U;
+
+ else if (strcmp(*argv, "--calculations-follow-bit-depth") == 0)
+ pm.calculations_use_input_precision =
+ pm.assume_16_bit_calculations = 0;
+
+ else if (strcmp(*argv, "--exhaustive") == 0)
+ pm.test_exhaustive = 1;
+
+ else if (argc > 1 && strcmp(*argv, "--sbitlow") == 0)
+ --argc, pm.sbitlow = (png_byte)atoi(*++argv), catmore = 1;
+
+ else if (argc > 1 && strcmp(*argv, "--touch") == 0)
+ --argc, touch = *++argv, catmore = 1;
+
+ else if (argc > 1 && strncmp(*argv, "--max", 5) == 0)
+ {
+ --argc;
+
+ if (strcmp(5+*argv, "abs8") == 0)
+ pm.maxabs8 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "abs16") == 0)
+ pm.maxabs16 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "calc8") == 0)
+ pm.maxcalc8 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "calc16") == 0)
+ pm.maxcalc16 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "out8") == 0)
+ pm.maxout8 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "out16") == 0)
+ pm.maxout16 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "pc8") == 0)
+ pm.maxpc8 = atof(*++argv);
+
+ else if (strcmp(5+*argv, "pc16") == 0)
+ pm.maxpc16 = atof(*++argv);
+
+ else
+ {
+ fprintf(stderr, "pngvalid: %s: unknown 'max' option\n", *argv);
+ exit(99);
+ }
+
+ catmore = 1;
+ }
+
+ else if (strcmp(*argv, "--log8") == 0)
+ --argc, pm.log8 = atof(*++argv), catmore = 1;
+
+ else if (strcmp(*argv, "--log16") == 0)
+ --argc, pm.log16 = atof(*++argv), catmore = 1;
+
+#ifdef PNG_SET_OPTION_SUPPORTED
+ else if (strncmp(*argv, "--option=", 9) == 0)
+ {
+ /* Syntax of the argument is <option>:{on|off} */
+ const char *arg = 9+*argv;
+ unsigned char option=0, setting=0;
+
+#ifdef PNG_ARM_NEON
+ if (strncmp(arg, "arm-neon:", 9) == 0)
+ option = PNG_ARM_NEON, arg += 9;
+
+ else
+#endif
+#ifdef PNG_EXTENSIONS
+ if (strncmp(arg, "extensions:", 11) == 0)
+ option = PNG_EXTENSIONS, arg += 11;
+
+ else
+#endif
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+ if (strncmp(arg, "max-inflate-window:", 19) == 0)
+ option = PNG_MAXIMUM_INFLATE_WINDOW, arg += 19;
+
+ else
+#endif
+ {
+ fprintf(stderr, "pngvalid: %s: %s: unknown option\n", *argv, arg);
+ exit(99);
+ }
+
+ if (strcmp(arg, "off") == 0)
+ setting = PNG_OPTION_OFF;
+
+ else if (strcmp(arg, "on") == 0)
+ setting = PNG_OPTION_ON;
+
+ else
+ {
+ fprintf(stderr,
+ "pngvalid: %s: %s: unknown setting (use 'on' or 'off')\n",
+ *argv, arg);
+ exit(99);
+ }
+
+ pm.this.options[pm.this.noptions].option = option;
+ pm.this.options[pm.this.noptions++].setting = setting;
+ }
+#endif /* PNG_SET_OPTION_SUPPORTED */
+
+ else
+ {
+ fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
+ exit(99);
+ }
+
+ if (catmore) /* consumed an extra *argv */
+ {
+ cp = safecat(command, sizeof command, cp, " ");
+ cp = safecat(command, sizeof command, cp, *argv);
+ }
+ }
+
+ /* If pngvalid is run with no arguments default to a reasonable set of the
+ * tests.
+ */
+ if (pm.test_standard == 0 && pm.test_size == 0 && pm.test_transform == 0 &&
+ pm.ngamma_tests == 0)
+ {
+ /* Make this do all the tests done in the test shell scripts with the same
+ * parameters, where possible. The limitation is that all the progressive
+ * read and interlace stuff has to be done in separate runs, so only the
+ * basic 'standard' and 'size' tests are done.
+ */
+ pm.test_standard = 1;
+ pm.test_size = 1;
+ pm.test_transform = 1;
+ pm.ngamma_tests = 2U;
+ }
+
+ if (pm.ngamma_tests > 0 &&
+ pm.test_gamma_threshold == 0 && pm.test_gamma_transform == 0 &&
+ pm.test_gamma_sbit == 0 && pm.test_gamma_scale16 == 0 &&
+ pm.test_gamma_background == 0 && pm.test_gamma_alpha_mode == 0)
+ {
+ pm.test_gamma_threshold = 1;
+ pm.test_gamma_transform = 1;
+ pm.test_gamma_sbit = 1;
+ pm.test_gamma_scale16 = 1;
+ pm.test_gamma_background = 1;
+ pm.test_gamma_alpha_mode = 1;
+ }
+
+ else if (pm.ngamma_tests == 0)
+ {
+ /* Nothing to test so turn everything off: */
+ pm.test_gamma_threshold = 0;
+ pm.test_gamma_transform = 0;
+ pm.test_gamma_sbit = 0;
+ pm.test_gamma_scale16 = 0;
+ pm.test_gamma_background = 0;
+ pm.test_gamma_alpha_mode = 0;
+ }
+
+ Try
+ {
+ /* Make useful base images */
+ make_transform_images(&pm);
+
+ /* Perform the standard and gamma tests. */
+ if (pm.test_standard)
+ {
+ perform_interlace_macro_validation();
+ perform_formatting_test(&pm.this);
+# ifdef PNG_READ_SUPPORTED
+ perform_standard_test(&pm);
+# endif
+ perform_error_test(&pm);
+ }
+
+ /* Various oddly sized images: */
+ if (pm.test_size)
+ {
+ make_size_images(&pm.this);
+# ifdef PNG_READ_SUPPORTED
+ perform_size_test(&pm);
+# endif
+ }
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+ /* Combinatorial transforms: */
+ if (pm.test_transform)
+ perform_transform_test(&pm);
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (pm.ngamma_tests > 0)
+ perform_gamma_test(&pm, summary);
+#endif
+ }
+
+ Catch_anonymous
+ {
+ fprintf(stderr, "pngvalid: test aborted (probably failed in cleanup)\n");
+ if (!pm.this.verbose)
+ {
+ if (pm.this.error[0] != 0)
+ fprintf(stderr, "pngvalid: first error: %s\n", pm.this.error);
+
+ fprintf(stderr, "pngvalid: run with -v to see what happened\n");
+ }
+ exit(1);
+ }
+
+ if (summary)
+ {
+ printf("%s: %s (%s point arithmetic)\n",
+ (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
+ pm.this.nwarnings)) ? "FAIL" : "PASS",
+ command,
+#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || PNG_LIBPNG_VER < 10500
+ "floating"
+#else
+ "fixed"
+#endif
+ );
+ }
+
+ if (memstats)
+ {
+ printf("Allocated memory statistics (in bytes):\n"
+ "\tread %lu maximum single, %lu peak, %lu total\n"
+ "\twrite %lu maximum single, %lu peak, %lu total\n",
+ (unsigned long)pm.this.read_memory_pool.max_max,
+ (unsigned long)pm.this.read_memory_pool.max_limit,
+ (unsigned long)pm.this.read_memory_pool.max_total,
+ (unsigned long)pm.this.write_memory_pool.max_max,
+ (unsigned long)pm.this.write_memory_pool.max_limit,
+ (unsigned long)pm.this.write_memory_pool.max_total);
+ }
+
+ /* Do this here to provoke memory corruption errors in memory not directly
+ * allocated by libpng - not a complete test, but better than nothing.
+ */
+ store_delete(&pm.this);
+
+ /* Error exit if there are any errors, and maybe if there are any
+ * warnings.
+ */
+ if (pm.this.nerrors || (pm.this.treat_warnings_as_errors &&
+ pm.this.nwarnings))
+ {
+ if (!pm.this.verbose)
+ fprintf(stderr, "pngvalid: %s\n", pm.this.error);
+
+ fprintf(stderr, "pngvalid: %d errors, %d warnings\n", pm.this.nerrors,
+ pm.this.nwarnings);
+
+ exit(1);
+ }
+
+ /* Success case. */
+ if (touch != NULL)
+ {
+ FILE *fsuccess = fopen(touch, "wt");
+
+ if (fsuccess != NULL)
+ {
+ int error = 0;
+ fprintf(fsuccess, "PNG validation succeeded\n");
+ fflush(fsuccess);
+ error = ferror(fsuccess);
+
+ if (fclose(fsuccess) || error)
+ {
+ fprintf(stderr, "%s: write failed\n", touch);
+ exit(1);
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "%s: open failed\n", touch);
+ exit(1);
+ }
+ }
+
+ /* This is required because some very minimal configurations do not use it:
+ */
+ UNUSED(fail)
+ return 0;
+}
+#else /* write or low level APIs not supported */
+int main(void)
+{
+ fprintf(stderr,
+ "pngvalid: no low level write support in libpng, all tests skipped\n");
+ /* So the test is skipped: */
+ return SKIP;
+}
+#endif
diff --git a/contrib/libtests/readpng.c b/contrib/libtests/readpng.c
new file mode 100644
index 0000000..3336d4e
--- /dev/null
+++ b/contrib/libtests/readpng.c
@@ -0,0 +1,115 @@
+/* readpng.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.1 [March 28, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Load an arbitrary number of PNG files (from the command line, or, if there
+ * are no arguments on the command line, from stdin) then run a time test by
+ * reading each file by row. The test does nothing with the read result and
+ * does no transforms. The only output is a time as a floating point number of
+ * seconds with 9 decimal digits.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+static int
+read_png(FILE *fp)
+{
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ png_infop info_ptr = NULL;
+ png_bytep row = NULL, display = NULL;
+
+ if (png_ptr == NULL)
+ return 0;
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ if (row != NULL) free(row);
+ if (display != NULL) free(display);
+ return 0;
+ }
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
+
+ png_read_info(png_ptr, info_ptr);
+
+ {
+ png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ /* Failure to initialize these is harmless */
+ row = malloc(rowbytes);
+ display = malloc(rowbytes);
+
+ if (row == NULL || display == NULL)
+ png_error(png_ptr, "OOM allocating row buffers");
+
+ {
+ png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+# ifdef PNG_READ_INTERLACING_SUPPORTED
+ int passes = png_set_interlace_handling(png_ptr);
+# else /* !READ_INTERLACING */
+ int passes = png_get_interlace_type(png_ptr, info_ptr) ==
+ PNG_INTERLACE_ADAM7 ? PNG_INTERLACE_ADAM7_PASSES : 1;
+# endif /* !READ_INTERLACING */
+ int pass;
+
+ png_start_read_image(png_ptr);
+
+ for (pass = 0; pass < passes; ++pass)
+ {
+ png_uint_32 y = height;
+
+# ifndef PNG_READ_INTERLACING_SUPPORTED
+ if (passes == PNG_INTERLACE_ADAM7_PASSES)
+ y = PNG_PASS_ROWS(y, pass);
+# endif /* READ_INTERLACING */
+
+ /* NOTE: this trashes the row each time; interlace handling won't
+ * work, but this avoids memory thrashing for speed testing.
+ */
+ while (y-- > 0)
+ png_read_row(png_ptr, row, display);
+ }
+ }
+ }
+
+ /* Make sure to read to the end of the file: */
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ free(row);
+ free(display);
+ return 1;
+}
+
+int
+main(void)
+{
+ /* Exit code 0 on success. */
+ return !read_png(stdin);
+}
diff --git a/contrib/libtests/tarith.c b/contrib/libtests/tarith.c
new file mode 100644
index 0000000..cb17ffa
--- /dev/null
+++ b/contrib/libtests/tarith.c
@@ -0,0 +1,999 @@
+
+/* tarith.c
+ *
+ * Copyright (c) 2011-2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Test internal arithmetic functions of libpng.
+ *
+ * This code must be linked against a math library (-lm), but does not require
+ * libpng or zlib to work. Because it includes the complete source of 'png.c'
+ * it tests the code with whatever compiler options are used to build it.
+ * Changing these options can substantially change the errors in the
+ * calculations that the compiler chooses!
+ */
+#define _POSIX_SOURCE 1
+#define _ISOC99_SOURCE 1
+
+/* Obtain a copy of the code to be tested (plus other things), disabling
+ * stuff that is not required.
+ */
+#include <math.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../../pngpriv.h"
+
+#define png_error png_warning
+
+void png_warning(png_const_structrp png_ptr, png_const_charp msg)
+{
+ fprintf(stderr, "validation: %s\n", msg);
+}
+
+#define png_fixed_error png_fixed_warning
+
+void png_fixed_warning(png_const_structrp png_ptr, png_const_charp msg)
+{
+ fprintf(stderr, "overflow in: %s\n", msg);
+}
+
+#define png_set_error_fn(pp, ep, efp, wfp) ((void)0)
+#define png_malloc(pp, s) malloc(s)
+#define png_malloc_warn(pp, s) malloc(s)
+#define png_malloc_base(pp, s) malloc(s)
+#define png_calloc(pp, s) calloc(1, (s))
+#define png_free(pp, s) free(s)
+
+#define png_safecat(b, sb, pos, str) (pos)
+#define png_format_number(start, end, format, number) (start)
+
+#define crc32(crc, pp, s) (crc)
+#define inflateReset(zs) Z_OK
+
+#define png_create_struct(type) (0)
+#define png_destroy_struct(pp) ((void)0)
+#define png_create_struct_2(type, m, mm) (0)
+#define png_destroy_struct_2(pp, f, mm) ((void)0)
+
+#undef PNG_SIMPLIFIED_READ_SUPPORTED
+#undef PNG_SIMPLIFIED_WRITE_SUPPORTED
+#undef PNG_USER_MEM_SUPPORTED
+
+#include "../../png.c"
+
+/* Validate ASCII to fp routines. */
+static int verbose = 0;
+
+int validation_ascii_to_fp(int count, int argc, char **argv)
+{
+ int showall = 0;
+ double max_error=2; /* As a percentage error-in-last-digit/.5 */
+ double max_error_abs=17; /* Used when precision is DBL_DIG */
+ double max = 0;
+ double max_abs = 0;
+ double test = 0; /* Important to test this. */
+ int precision = 5;
+ int nonfinite = 0;
+ int finite = 0;
+ int ok = 0;
+ int failcount = 0;
+ int minorarith = 0;
+
+ while (--argc > 0)
+ if (strcmp(*++argv, "-a") == 0)
+ showall = 1;
+ else if (strcmp(*argv, "-e") == 0 && argc > 0)
+ {
+ --argc;
+ max_error = atof(*++argv);
+ }
+ else if (strcmp(*argv, "-E") == 0 && argc > 0)
+ {
+ --argc;
+ max_error_abs = atof(*++argv);
+ }
+ else
+ {
+ fprintf(stderr, "unknown argument %s\n", *argv);
+ return 1;
+ }
+
+ do
+ {
+ png_size_t index;
+ int state, failed = 0;
+ char buffer[64];
+
+ if (isfinite(test))
+ ++finite;
+ else
+ ++nonfinite;
+
+ if (verbose)
+ fprintf(stderr, "%.*g %d\n", DBL_DIG, test, precision);
+
+ /* Check for overflow in the buffer by setting a marker. */
+ memset(buffer, 71, sizeof buffer);
+
+ png_ascii_from_fp(0, buffer, precision+10, test, precision);
+
+ /* Allow for a three digit exponent, this stuff will fail if
+ * the exponent is bigger than this!
+ */
+ if (buffer[precision+7] != 71)
+ {
+ fprintf(stderr, "%g[%d] -> '%s'[%lu] buffer overflow\n", test,
+ precision, buffer, (unsigned long)strlen(buffer));
+ failed = 1;
+ }
+
+ /* Following are used for the number parser below and must be
+ * initialized to zero.
+ */
+ state = 0;
+ index = 0;
+ if (!isfinite(test))
+ {
+ /* Expect 'inf' */
+ if (test >= 0 && strcmp(buffer, "inf") ||
+ test < 0 && strcmp(buffer, "-inf"))
+ {
+ fprintf(stderr, "%g[%d] -> '%s' but expected 'inf'\n", test,
+ precision, buffer);
+ failed = 1;
+ }
+ }
+ else if (!png_check_fp_number(buffer, precision+10, &state, &index) ||
+ buffer[index] != 0)
+ {
+ fprintf(stderr, "%g[%d] -> '%s' but has bad format ('%c')\n", test,
+ precision, buffer, buffer[index]);
+ failed = 1;
+ }
+ else if (PNG_FP_IS_NEGATIVE(state) && !(test < 0))
+ {
+ fprintf(stderr, "%g[%d] -> '%s' but negative value not so reported\n",
+ test, precision, buffer);
+ failed = 1;
+ assert(!PNG_FP_IS_ZERO(state));
+ assert(!PNG_FP_IS_POSITIVE(state));
+ }
+ else if (PNG_FP_IS_ZERO(state) && !(test == 0))
+ {
+ fprintf(stderr, "%g[%d] -> '%s' but zero value not so reported\n",
+ test, precision, buffer);
+ failed = 1;
+ assert(!PNG_FP_IS_NEGATIVE(state));
+ assert(!PNG_FP_IS_POSITIVE(state));
+ }
+ else if (PNG_FP_IS_POSITIVE(state) && !(test > 0))
+ {
+ fprintf(stderr, "%g[%d] -> '%s' but postive value not so reported\n",
+ test, precision, buffer);
+ failed = 1;
+ assert(!PNG_FP_IS_NEGATIVE(state));
+ assert(!PNG_FP_IS_ZERO(state));
+ }
+ else
+ {
+ /* Check the result against the original. */
+ double out = atof(buffer);
+ double change = fabs((out - test)/test);
+ double allow = .5/pow(10,
+ (precision >= DBL_DIG) ? DBL_DIG-1 : precision-1);
+
+ /* NOTE: if you hit this error case are you compiling with gcc
+ * and -O0? Try -O2 - the errors can accumulate if the FP
+ * code above is not optimized and may drift outside the .5 in
+ * DBL_DIG allowed. In any case a small number of errors may
+ * occur (very small ones - 1 or 2%) because of rounding in the
+ * calculations, either in the conversion API or in atof.
+ */
+ if (change >= allow && (isfinite(out) ||
+ fabs(test/DBL_MAX) <= 1-allow))
+ {
+ double percent = (precision >= DBL_DIG) ? max_error_abs : max_error;
+ double allowp = (change-allow)*100/allow;
+
+ if (precision >= DBL_DIG)
+ {
+ if (max_abs < allowp) max_abs = allowp;
+ }
+
+ else
+ {
+ if (max < allowp) max = allowp;
+ }
+
+ if (showall || allowp >= percent)
+ {
+ fprintf(stderr,
+ "%.*g[%d] -> '%s' -> %.*g number changed (%g > %g (%d%%))\n",
+ DBL_DIG, test, precision, buffer, DBL_DIG, out, change, allow,
+ (int)round(allowp));
+ failed = 1;
+ }
+ else
+ ++minorarith;
+ }
+ }
+
+ if (failed)
+ ++failcount;
+ else
+ ++ok;
+
+skip:
+ /* Generate a new number and precision. */
+ precision = rand();
+ if (precision & 1) test = -test;
+ precision >>= 1;
+
+ /* Generate random numbers. */
+ if (test == 0 || !isfinite(test))
+ test = precision+1;
+ else
+ {
+ /* Derive the exponent from the previous rand() value. */
+ int exponent = precision % (DBL_MAX_EXP - DBL_MIN_EXP) + DBL_MIN_EXP;
+ int tmp;
+ test = frexp(test * rand(), &tmp);
+ test = ldexp(test, exponent);
+ precision >>= 8; /* arbitrary */
+ }
+
+ /* This limits the precision to 32 digits, enough for standard
+ * IEEE implementations which have at most 15 digits.
+ */
+ precision = (precision & 0x1f) + 1;
+ }
+ while (--count);
+
+ printf("Tested %d finite values, %d non-finite, %d OK (%d failed) %d minor "
+ "arithmetic errors\n", finite, nonfinite, ok, failcount, minorarith);
+ printf(" Error with >=%d digit precision %.2f%%\n", DBL_DIG, max_abs);
+ printf(" Error with < %d digit precision %.2f%%\n", DBL_DIG, max);
+
+ return 0;
+}
+
+/* Observe that valid FP numbers have the forms listed in the PNG extensions
+ * specification:
+ *
+ * [+,-]{integer,integer.fraction,.fraction}[{e,E}[+,-]integer]
+ *
+ * Test each of these in turn, including invalid cases.
+ */
+typedef enum checkfp_state
+{
+ start, fraction, exponent, states
+} checkfp_state;
+
+/* The characters (other than digits) that characterize the states: */
+static const char none[] = "";
+static const char hexdigits[16] = "0123456789ABCDEF";
+
+static const struct
+{
+ const char *start; /* Characters valid at the start */
+ const char *end; /* Valid characters that end the state */
+ const char *tests; /* Characters to test after 2 digits seen */
+}
+state_characters[states] =
+{
+ /* start: */ { "+-.", ".eE", "+-.e*0369" },
+ /* fraction: */ { none, "eE", "+-.E#0147" },
+ /* exponent: */ { "+-", none, "+-.eE^0258" }
+};
+
+typedef struct
+{
+ char number[1024]; /* Buffer for number being tested */
+ int limit; /* Command line limit */
+ int verbose; /* Shadows global variable */
+ int ctimes; /* Number of numbers tested */
+ int cmillions; /* Count of millions of numbers */
+ int cinvalid; /* Invalid strings checked */
+ int cnoaccept; /* Characters not accepted */
+}
+checkfp_command;
+
+typedef struct
+{
+ int cnumber; /* Index into number string */
+ checkfp_state check_state; /* Current number state */
+ int at_start; /* At start (first character) of state */
+ int cdigits_in_state; /* Digits seen in that state */
+ int limit; /* Limit on same for checking all chars */
+ int state; /* Current parser state */
+ int is_negative; /* Number is negative */
+ int is_zero; /* Number is (still) zero */
+ int number_was_valid; /* Previous character validity */
+}
+checkfp_control;
+
+static int check_all_characters(checkfp_command *co, checkfp_control c);
+
+static int check_some_characters(checkfp_command *co, checkfp_control c,
+ const char *tests);
+
+static int check_one_character(checkfp_command *co, checkfp_control c, int ch)
+{
+ /* Test this character (ch) to ensure the parser does the correct thing.
+ */
+ png_size_t index = 0;
+ const char test = (char)ch;
+ const int number_is_valid = png_check_fp_number(&test, 1, &c.state, &index);
+ const int character_accepted = (index == 1);
+
+ if (c.check_state != exponent && isdigit(ch) && ch != '0')
+ c.is_zero = 0;
+
+ if (c.check_state == start && c.at_start && ch == '-')
+ c.is_negative = 1;
+
+ if (isprint(ch))
+ co->number[c.cnumber++] = (char)ch;
+ else
+ {
+ co->number[c.cnumber++] = '<';
+ co->number[c.cnumber++] = hexdigits[(ch >> 4) & 0xf];
+ co->number[c.cnumber++] = hexdigits[ch & 0xf];
+ co->number[c.cnumber++] = '>';
+ }
+ co->number[c.cnumber] = 0;
+
+ if (co->verbose > 1)
+ fprintf(stderr, "%s\n", co->number);
+
+ if (++(co->ctimes) == 1000000)
+ {
+ if (co->verbose == 1)
+ fputc('.', stderr);
+ co->ctimes = 0;
+ ++(co->cmillions);
+ }
+
+ if (!number_is_valid)
+ ++(co->cinvalid);
+
+ if (!character_accepted)
+ ++(co->cnoaccept);
+
+ /* This should never fail (it's a serious bug if it does): */
+ if (index != 0 && index != 1)
+ {
+ fprintf(stderr, "%s: read beyond end of string (%lu)\n", co->number,
+ (unsigned long)index);
+ return 0;
+ }
+
+ /* Validate the new state, note that the PNG_FP_IS_ macros all return
+ * false unless the number is valid.
+ */
+ if (PNG_FP_IS_NEGATIVE(c.state) !=
+ (number_is_valid && !c.is_zero && c.is_negative))
+ {
+ fprintf(stderr, "%s: negative when it is not\n", co->number);
+ return 0;
+ }
+
+ if (PNG_FP_IS_ZERO(c.state) != (number_is_valid && c.is_zero))
+ {
+ fprintf(stderr, "%s: zero when it is not\n", co->number);
+ return 0;
+ }
+
+ if (PNG_FP_IS_POSITIVE(c.state) !=
+ (number_is_valid && !c.is_zero && !c.is_negative))
+ {
+ fprintf(stderr, "%s: positive when it is not\n", co->number);
+ return 0;
+ }
+
+ /* Testing a digit */
+ if (isdigit(ch))
+ {
+ if (!character_accepted)
+ {
+ fprintf(stderr, "%s: digit '%c' not accepted\n", co->number, ch);
+ return 0;
+ }
+
+ if (!number_is_valid)
+ {
+ fprintf(stderr, "%s: saw a digit (%c) but number not valid\n",
+ co->number, ch);
+ return 0;
+ }
+
+ ++c.cdigits_in_state;
+ c.at_start = 0;
+ c.number_was_valid = 1;
+
+ /* Continue testing characters in this state. Either test all of
+ * them or, if we have already seen one digit in this state, just test a
+ * limited set.
+ */
+ if (c.cdigits_in_state < 1)
+ return check_all_characters(co, c);
+
+ else
+ return check_some_characters(co, c,
+ state_characters[c.check_state].tests);
+ }
+
+ /* A non-digit; is it allowed here? */
+ else if (((ch == '+' || ch == '-') && c.check_state != fraction &&
+ c.at_start) ||
+ (ch == '.' && c.check_state == start) ||
+ ((ch == 'e' || ch == 'E') && c.number_was_valid &&
+ c.check_state != exponent))
+ {
+ if (!character_accepted)
+ {
+ fprintf(stderr, "%s: character '%c' not accepted\n", co->number, ch);
+ return 0;
+ }
+
+ /* The number remains valid after start of fraction but nowhere else. */
+ if (number_is_valid && (c.check_state != start || ch != '.'))
+ {
+ fprintf(stderr, "%s: saw a non-digit (%c) but number valid\n",
+ co->number, ch);
+ return 0;
+ }
+
+ c.number_was_valid = number_is_valid;
+
+ /* Check for a state change. When changing to 'fraction' if the number
+ * is valid at this point set the at_start to false to allow an exponent
+ * 'e' to come next.
+ */
+ if (c.check_state == start && ch == '.')
+ {
+ c.check_state = fraction;
+ c.at_start = !number_is_valid;
+ c.cdigits_in_state = 0;
+ c.limit = co->limit;
+ return check_all_characters(co, c);
+ }
+
+ else if (c.check_state < exponent && (ch == 'e' || ch == 'E'))
+ {
+ c.check_state = exponent;
+ c.at_start = 1;
+ c.cdigits_in_state = 0;
+ c.limit = co->limit;
+ return check_all_characters(co, c);
+ }
+
+ /* Else it was a sign, and the state doesn't change. */
+ else
+ {
+ if (ch != '-' && ch != '+')
+ {
+ fprintf(stderr, "checkfp: internal error (1)\n");
+ return 0;
+ }
+
+ c.at_start = 0;
+ return check_all_characters(co, c);
+ }
+ }
+
+ /* Testing an invalid character */
+ else
+ {
+ if (character_accepted)
+ {
+ fprintf(stderr, "%s: character '%c' [0x%.2x] accepted\n", co->number,
+ ch, ch);
+ return 0;
+ }
+
+ if (number_is_valid != c.number_was_valid)
+ {
+ fprintf(stderr,
+ "%s: character '%c' [0x%.2x] changed number validity\n", co->number,
+ ch, ch);
+ return 0;
+ }
+
+ /* Do nothing - the parser has stuck; return success and keep going with
+ * the next character.
+ */
+ }
+
+ /* Successful return (the caller will try the next character.) */
+ return 1;
+}
+
+static int check_all_characters(checkfp_command *co, checkfp_control c)
+{
+ int ch;
+
+ if (c.cnumber+4 < sizeof co->number) for (ch=0; ch<256; ++ch)
+ {
+ if (!check_one_character(co, c, ch))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int check_some_characters(checkfp_command *co, checkfp_control c,
+ const char *tests)
+{
+ int i;
+
+ --(c.limit);
+
+ if (c.cnumber+4 < sizeof co->number && c.limit >= 0)
+ {
+ if (c.limit > 0) for (i=0; tests[i]; ++i)
+ {
+ if (!check_one_character(co, c, tests[i]))
+ return 0;
+ }
+
+ /* At the end check all the characters. */
+ else
+ return check_all_characters(co, c);
+ }
+
+ return 1;
+}
+
+int validation_checkfp(int count, int argc, char **argv)
+{
+ int result;
+ checkfp_command command;
+ checkfp_control control;
+
+ command.number[0] = 0;
+ command.limit = 3;
+ command.verbose = verbose;
+ command.ctimes = 0;
+ command.cmillions = 0;
+ command.cinvalid = 0;
+ command.cnoaccept = 0;
+
+ while (--argc > 0)
+ {
+ ++argv;
+ if (argc > 1 && strcmp(*argv, "-l") == 0)
+ {
+ --argc;
+ command.limit = atoi(*++argv);
+ }
+
+ else
+ {
+ fprintf(stderr, "unknown argument %s\n", *argv);
+ return 1;
+ }
+ }
+
+ control.cnumber = 0;
+ control.check_state = start;
+ control.at_start = 1;
+ control.cdigits_in_state = 0;
+ control.limit = command.limit;
+ control.state = 0;
+ control.is_negative = 0;
+ control.is_zero = 1;
+ control.number_was_valid = 0;
+
+ result = check_all_characters(&command, control);
+
+ printf("checkfp: %s: checked %d,%.3d,%.3d,%.3d strings (%d invalid)\n",
+ result ? "pass" : "FAIL", command.cmillions / 1000,
+ command.cmillions % 1000, command.ctimes / 1000, command.ctimes % 1000,
+ command.cinvalid);
+
+ return result;
+}
+
+int validation_muldiv(int count, int argc, char **argv)
+{
+ int tested = 0;
+ int overflow = 0;
+ int error = 0;
+ int error64 = 0;
+ int passed = 0;
+ int randbits = 0;
+ png_uint_32 randbuffer;
+ png_fixed_point a;
+ png_int_32 times, div;
+
+ while (--argc > 0)
+ {
+ fprintf(stderr, "unknown argument %s\n", *++argv);
+ return 1;
+ }
+
+ /* Find out about the random number generator. */
+ randbuffer = RAND_MAX;
+ while (randbuffer != 0) ++randbits, randbuffer >>= 1;
+ printf("Using random number generator that makes %d bits\n", randbits);
+ for (div=0; div<32; div += randbits)
+ randbuffer = (randbuffer << randbits) ^ rand();
+
+ a = 0;
+ times = div = 0;
+ do
+ {
+ png_fixed_point result;
+ /* NOTE: your mileage may vary, a type is required below that can
+ * hold 64 bits or more, if floating point is used a 64-bit or
+ * better mantissa is required.
+ */
+ long long int fp, fpround;
+ unsigned long hi, lo;
+ int ok;
+
+ /* Check the values, png_64bit_product can only handle positive
+ * numbers, so correct for that here.
+ */
+ {
+ long u1, u2;
+ int n = 0;
+ if (a < 0) u1 = -a, n = 1; else u1 = a;
+ if (times < 0) u2 = -times, n = !n; else u2 = times;
+ png_64bit_product(u1, u2, &hi, &lo);
+ if (n)
+ {
+ /* -x = ~x+1 */
+ lo = ((~lo) + 1) & 0xffffffff;
+ hi = ~hi;
+ if (lo == 0) ++hi;
+ }
+ }
+
+ fp = a;
+ fp *= times;
+ if ((fp & 0xffffffff) != lo || ((fp >> 32) & 0xffffffff) != hi)
+ {
+ fprintf(stderr, "png_64bit_product %d * %d -> %lx|%.8lx not %llx\n",
+ a, times, hi, lo, fp);
+ ++error64;
+ }
+
+ if (div != 0)
+ {
+ /* Round - this is C round to zero. */
+ if ((fp < 0) != (div < 0))
+ fp -= div/2;
+ else
+ fp += div/2;
+
+ fp /= div;
+ fpround = fp;
+ /* Assume 2's complement here: */
+ ok = fpround <= PNG_UINT_31_MAX &&
+ fpround >= -1-(long long int)PNG_UINT_31_MAX;
+ if (!ok) ++overflow;
+ }
+ else
+ ok = 0, ++overflow, fpround = fp/*misleading*/;
+
+ if (verbose)
+ fprintf(stderr, "TEST %d * %d / %d -> %lld (%s)\n", a, times, div,
+ fp, ok ? "ok" : "overflow");
+
+ ++tested;
+ if (png_muldiv(&result, a, times, div) != ok)
+ {
+ ++error;
+ if (ok)
+ fprintf(stderr, "%d * %d / %d -> overflow (expected %lld)\n", a,
+ times, div, fp);
+ else
+ fprintf(stderr, "%d * %d / %d -> %d (expected overflow %lld)\n", a,
+ times, div, result, fp);
+ }
+ else if (ok && result != fpround)
+ {
+ ++error;
+ fprintf(stderr, "%d * %d / %d -> %d not %lld\n", a, times, div, result,
+ fp);
+ }
+ else
+ ++passed;
+
+ /* Generate three new values, this uses rand() and rand() only returns
+ * up to RAND_MAX.
+ */
+ /* CRUDE */
+ a += times;
+ times += div;
+ div = randbuffer;
+ randbuffer = (randbuffer << randbits) ^ rand();
+ }
+ while (--count > 0);
+
+ printf("%d tests including %d overflows, %d passed, %d failed (%d 64-bit "
+ "errors)\n", tested, overflow, passed, error, error64);
+ return 0;
+}
+
+/* When FP is on this just becomes a speed test - compile without FP to get real
+ * validation.
+ */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define LN2 .000010576586617430806112933839 /* log(2)/65536 */
+#define L2INV 94548.46219969910586572651 /* 65536/log(2) */
+
+/* For speed testing, need the internal functions too: */
+static png_uint_32 png_log8bit(unsigned x)
+{
+ if (x > 0)
+ return (png_uint_32)floor(.5-log(x/255.)*L2INV);
+
+ return 0xffffffff;
+}
+
+static png_uint_32 png_log16bit(png_uint_32 x)
+{
+ if (x > 0)
+ return (png_uint_32)floor(.5-log(x/65535.)*L2INV);
+
+ return 0xffffffff;
+}
+
+static png_uint_32 png_exp(png_uint_32 x)
+{
+ return (png_uint_32)floor(.5 + exp(x * -LN2) * 0xffffffffU);
+}
+
+static png_byte png_exp8bit(png_uint_32 log)
+{
+ return (png_byte)floor(.5 + exp(log * -LN2) * 255);
+}
+
+static png_uint_16 png_exp16bit(png_uint_32 log)
+{
+ return (png_uint_16)floor(.5 + exp(log * -LN2) * 65535);
+}
+#endif /* FLOATING_ARITHMETIC */
+
+int validation_gamma(int argc, char **argv)
+{
+ double gamma[9] = { 2.2, 1.8, 1.52, 1.45, 1., 1/1.45, 1/1.52, 1/1.8, 1/2.2 };
+ double maxerr;
+ int i, silent=0, onlygamma=0;
+
+ /* Silence the output with -s, just test the gamma functions with -g: */
+ while (--argc > 0)
+ if (strcmp(*++argv, "-s") == 0)
+ silent = 1;
+ else if (strcmp(*argv, "-g") == 0)
+ onlygamma = 1;
+ else
+ {
+ fprintf(stderr, "unknown argument %s\n", *argv);
+ return 1;
+ }
+
+ if (!onlygamma)
+ {
+ /* First validate the log functions: */
+ maxerr = 0;
+ for (i=0; i<256; ++i)
+ {
+ double correct = -log(i/255.)/log(2.)*65536;
+ double error = png_log8bit(i) - correct;
+
+ if (i != 0 && fabs(error) > maxerr)
+ maxerr = fabs(error);
+
+ if (i == 0 && png_log8bit(i) != 0xffffffff ||
+ i != 0 && png_log8bit(i) != floor(correct+.5))
+ {
+ fprintf(stderr, "8-bit log error: %d: got %u, expected %f\n",
+ i, png_log8bit(i), correct);
+ }
+ }
+
+ if (!silent)
+ printf("maximum 8-bit log error = %f\n", maxerr);
+
+ maxerr = 0;
+ for (i=0; i<65536; ++i)
+ {
+ double correct = -log(i/65535.)/log(2.)*65536;
+ double error = png_log16bit(i) - correct;
+
+ if (i != 0 && fabs(error) > maxerr)
+ maxerr = fabs(error);
+
+ if (i == 0 && png_log16bit(i) != 0xffffffff ||
+ i != 0 && png_log16bit(i) != floor(correct+.5))
+ {
+ if (error > .68) /* By experiment error is less than .68 */
+ {
+ fprintf(stderr, "16-bit log error: %d: got %u, expected %f"
+ " error: %f\n", i, png_log16bit(i), correct, error);
+ }
+ }
+ }
+
+ if (!silent)
+ printf("maximum 16-bit log error = %f\n", maxerr);
+
+ /* Now exponentiations. */
+ maxerr = 0;
+ for (i=0; i<=0xfffff; ++i)
+ {
+ double correct = exp(-i/65536. * log(2.)) * (65536. * 65536);
+ double error = png_exp(i) - correct;
+
+ if (fabs(error) > maxerr)
+ maxerr = fabs(error);
+ if (fabs(error) > 1883) /* By experiment. */
+ {
+ fprintf(stderr, "32-bit exp error: %d: got %u, expected %f"
+ " error: %f\n", i, png_exp(i), correct, error);
+ }
+ }
+
+ if (!silent)
+ printf("maximum 32-bit exp error = %f\n", maxerr);
+
+ maxerr = 0;
+ for (i=0; i<=0xfffff; ++i)
+ {
+ double correct = exp(-i/65536. * log(2.)) * 255;
+ double error = png_exp8bit(i) - correct;
+
+ if (fabs(error) > maxerr)
+ maxerr = fabs(error);
+ if (fabs(error) > .50002) /* By experiment */
+ {
+ fprintf(stderr, "8-bit exp error: %d: got %u, expected %f"
+ " error: %f\n", i, png_exp8bit(i), correct, error);
+ }
+ }
+
+ if (!silent)
+ printf("maximum 8-bit exp error = %f\n", maxerr);
+
+ maxerr = 0;
+ for (i=0; i<=0xfffff; ++i)
+ {
+ double correct = exp(-i/65536. * log(2.)) * 65535;
+ double error = png_exp16bit(i) - correct;
+
+ if (fabs(error) > maxerr)
+ maxerr = fabs(error);
+ if (fabs(error) > .524) /* By experiment */
+ {
+ fprintf(stderr, "16-bit exp error: %d: got %u, expected %f"
+ " error: %f\n", i, png_exp16bit(i), correct, error);
+ }
+ }
+
+ if (!silent)
+ printf("maximum 16-bit exp error = %f\n", maxerr);
+ } /* !onlygamma */
+
+ /* Test the overall gamma correction. */
+ for (i=0; i<9; ++i)
+ {
+ unsigned j;
+ double g = gamma[i];
+ png_fixed_point gfp = floor(g * PNG_FP_1 + .5);
+
+ if (!silent)
+ printf("Test gamma %f\n", g);
+
+ maxerr = 0;
+ for (j=0; j<256; ++j)
+ {
+ double correct = pow(j/255., g) * 255;
+ png_byte out = png_gamma_8bit_correct(j, gfp);
+ double error = out - correct;
+
+ if (fabs(error) > maxerr)
+ maxerr = fabs(error);
+ if (out != floor(correct+.5))
+ {
+ fprintf(stderr, "8bit %d ^ %f: got %d expected %f error %f\n",
+ j, g, out, correct, error);
+ }
+ }
+
+ if (!silent)
+ printf("gamma %f: maximum 8-bit error %f\n", g, maxerr);
+
+ maxerr = 0;
+ for (j=0; j<65536; ++j)
+ {
+ double correct = pow(j/65535., g) * 65535;
+ png_uint_16 out = png_gamma_16bit_correct(j, gfp);
+ double error = out - correct;
+
+ if (fabs(error) > maxerr)
+ maxerr = fabs(error);
+ if (fabs(error) > 1.62)
+ {
+ fprintf(stderr, "16bit %d ^ %f: got %d expected %f error %f\n",
+ j, g, out, correct, error);
+ }
+ }
+
+ if (!silent)
+ printf("gamma %f: maximum 16-bit error %f\n", g, maxerr);
+ }
+
+ return 0;
+}
+
+/**************************** VALIDATION TESTS ********************************/
+/* Various validation routines are included herein, they require some
+ * definition for png_warning and png_error, seetings of VALIDATION:
+ *
+ * 1: validates the ASCII to floating point conversions
+ * 2: validates png_muldiv
+ * 3: accuracy test of fixed point gamma tables
+ */
+
+/* The following COUNT (10^8) takes about 1 hour on a 1GHz Pentium IV
+ * processor.
+ */
+#define COUNT 1000000000
+
+int main(int argc, char **argv)
+{
+ int count = COUNT;
+
+ while (argc > 1)
+ {
+ if (argc > 2 && strcmp(argv[1], "-c") == 0)
+ {
+ count = atoi(argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+
+ else if (strcmp(argv[1], "-v") == 0)
+ {
+ ++verbose;
+ --argc;
+ ++argv;
+ }
+
+ else
+ break;
+ }
+
+ if (count > 0 && argc > 1)
+ {
+ if (strcmp(argv[1], "ascii") == 0)
+ return validation_ascii_to_fp(count, argc-1, argv+1);
+ else if (strcmp(argv[1], "checkfp") == 0)
+ return validation_checkfp(count, argc-1, argv+1);
+ else if (strcmp(argv[1], "muldiv") == 0)
+ return validation_muldiv(count, argc-1, argv+1);
+ else if (strcmp(argv[1], "gamma") == 0)
+ return validation_gamma(argc-1, argv+1);
+ }
+
+ /* Bad argument: */
+ fprintf(stderr,
+ "usage: tarith [-v] [-c count] {ascii,muldiv,gamma} [args]\n");
+ fprintf(stderr, " arguments: ascii [-a (all results)] [-e error%%]\n");
+ fprintf(stderr, " checkfp [-l max-number-chars]\n");
+ fprintf(stderr, " muldiv\n");
+ fprintf(stderr, " gamma -s (silent) -g (only gamma; no log)\n");
+ return 1;
+}
diff --git a/contrib/libtests/timepng.c b/contrib/libtests/timepng.c
new file mode 100644
index 0000000..7c93797
--- /dev/null
+++ b/contrib/libtests/timepng.c
@@ -0,0 +1,303 @@
+/* timepng.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.1 [March 28, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Load an arbitrary number of PNG files (from the command line, or, if there
+ * are no arguments on the command line, from stdin) then run a time test by
+ * reading each file by row. The test does nothing with the read result and
+ * does no transforms. The only output is a time as a floating point number of
+ * seconds with 9 decimal digits.
+ */
+#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <time.h>
+
+#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
+# include <config.h>
+#endif
+
+/* Define the following to use this test against your installed libpng, rather
+ * than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+static int read_png(FILE *fp)
+{
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
+ png_infop info_ptr = NULL;
+ png_bytep row = NULL, display = NULL;
+
+ if (png_ptr == NULL)
+ return 0;
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ if (row != NULL) free(row);
+ if (display != NULL) free(display);
+ return 0;
+ }
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ png_read_info(png_ptr, info_ptr);
+
+ {
+ png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ row = malloc(rowbytes);
+ display = malloc(rowbytes);
+
+ if (row == NULL || display == NULL)
+ png_error(png_ptr, "OOM allocating row buffers");
+
+ {
+ png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+ int passes = png_set_interlace_handling(png_ptr);
+ int pass;
+
+ png_start_read_image(png_ptr);
+
+ for (pass = 0; pass < passes; ++pass)
+ {
+ png_uint_32 y = height;
+
+ /* NOTE: this trashes the row each time; interlace handling won't
+ * work, but this avoids memory thrashing for speed testing.
+ */
+ while (y-- > 0)
+ png_read_row(png_ptr, row, display);
+ }
+ }
+ }
+
+ /* Make sure to read to the end of the file: */
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ free(row);
+ free(display);
+ return 1;
+}
+
+static int mytime(struct timespec *t)
+{
+ /* Do the timing using clock_gettime and the per-process timer. */
+ if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t))
+ return 1;
+
+ perror("CLOCK_PROCESS_CPUTIME_ID");
+ fprintf(stderr, "timepng: could not get the time\n");
+ return 0;
+}
+
+static int perform_one_test(FILE *fp, int nfiles)
+{
+ int i;
+ struct timespec before, after;
+
+ /* Clear out all errors: */
+ rewind(fp);
+
+ if (mytime(&before))
+ {
+ for (i=0; i<nfiles; ++i)
+ {
+ if (read_png(fp))
+ {
+ if (ferror(fp))
+ {
+ perror("temporary file");
+ fprintf(stderr, "file %d: error reading PNG data\n", i);
+ return 0;
+ }
+ }
+
+ else
+ {
+ perror("temporary file");
+ fprintf(stderr, "file %d: error from libpng\n", i);
+ return 0;
+ }
+ }
+ }
+
+ else
+ return 0;
+
+ if (mytime(&after))
+ {
+ /* Work out the time difference and print it - this is the only output,
+ * so flush it immediately.
+ */
+ unsigned long s = after.tv_sec - before.tv_sec;
+ long ns = after.tv_nsec - before.tv_nsec;
+
+ if (ns < 0)
+ {
+ --s;
+ ns += 1000000000;
+
+ if (ns < 0)
+ {
+ fprintf(stderr, "timepng: bad clock from kernel\n");
+ return 0;
+ }
+ }
+
+ printf("%lu.%.9ld\n", s, ns);
+ fflush(stdout);
+ if (ferror(stdout))
+ {
+ fprintf(stderr, "timepng: error writing output\n");
+ return 0;
+ }
+
+ /* Successful return */
+ return 1;
+ }
+
+ else
+ return 0;
+}
+
+static int add_one_file(FILE *fp, char *name)
+{
+ FILE *ip = fopen(name, "rb");
+
+ if (ip != NULL)
+ {
+ int ch;
+ for (;;)
+ {
+ ch = getc(ip);
+ if (ch == EOF) break;
+ putc(ch, fp);
+ }
+
+ if (ferror(ip))
+ {
+ perror(name);
+ fprintf(stderr, "%s: read error\n", name);
+ return 0;
+ }
+
+ (void)fclose(ip);
+
+ if (ferror(fp))
+ {
+ perror("temporary file");
+ fprintf(stderr, "temporary file write error\n");
+ return 0;
+ }
+ }
+
+ else
+ {
+ perror(name);
+ fprintf(stderr, "%s: open failed\n", name);
+ return 0;
+ }
+
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int ok = 0;
+ FILE *fp = tmpfile();
+
+ if (fp != NULL)
+ {
+ int err = 0;
+ int nfiles = 0;
+
+ if (argc > 1)
+ {
+ int i;
+
+ for (i=1; i<argc; ++i)
+ {
+ if (add_one_file(fp, argv[i]))
+ ++nfiles;
+
+ else
+ {
+ err = 1;
+ break;
+ }
+ }
+ }
+
+ else
+ {
+ char filename[FILENAME_MAX+1];
+
+ while (fgets(filename, FILENAME_MAX+1, stdin))
+ {
+ size_t len = strlen(filename);
+
+ if (filename[len-1] == '\n')
+ {
+ filename[len-1] = 0;
+ if (add_one_file(fp, filename))
+ ++nfiles;
+
+ else
+ {
+ err = 1;
+ break;
+ }
+ }
+
+ else
+ {
+ fprintf(stderr, "timepng: truncated file name ...%s\n",
+ filename+len-32);
+ err = 1;
+ break;
+ }
+ }
+
+ if (ferror(stdin))
+ {
+ fprintf(stderr, "timepng: stdin: read error\n");
+ err = 1;
+ }
+ }
+
+ if (!err)
+ {
+ if (nfiles > 0)
+ ok = perform_one_test(fp, nfiles);
+
+ else
+ fprintf(stderr, "usage: timepng {files} or ls files | timepng\n");
+ }
+
+ (void)fclose(fp);
+ }
+
+ else
+ fprintf(stderr, "timepng: could not open temporary file\n");
+
+ /* Exit code 0 on success. */
+ return ok == 0;
+}
diff --git a/contrib/pngminim/README b/contrib/pngminim/README
new file mode 100644
index 0000000..e17fe35
--- /dev/null
+++ b/contrib/pngminim/README
@@ -0,0 +1,5 @@
+
+This demonstrates the use of PNG_USER_CONFIG, pngusr.h and pngusr.dfa
+to build minimal decoder, encoder, and progressive reader applications.
+
+See the individual README and pngusr.dfa files for more explanation.
diff --git a/contrib/pngminim/decoder/README b/contrib/pngminim/decoder/README
index a656e0b..fa979fc 100644
--- a/contrib/pngminim/decoder/README
+++ b/contrib/pngminim/decoder/README
@@ -1,9 +1,10 @@
-This demonstrates the use of PNG_USER_CONFIG and pngusr.h
+This demonstrates the use of PNG_USER_CONFIG, pngusr.h and pngusr.dfa
-To build a minimal read-only decoder with embedded libpng and zlib, run
+The makefile builds a minimal read-only decoder with embedded libpng
+and zlib.
- gather.sh # to collect needed files from pngminus, libpng, and zlib
- make
+Specify the location of the zlib source (1.2.1 or later) as ZLIBSRC
+on the make command line.
If you prefer to use the shared libraries, go to contrib/pngminus
-and build the png2pnm application there.
+and build the png2pnm application there.
diff --git a/contrib/pngminim/decoder/gather.sh b/contrib/pngminim/decoder/gather.sh
deleted file mode 100755
index 913b78d..0000000
--- a/contrib/pngminim/decoder/gather.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-cp ../../pngminus/png2pnm.c pngm2pnm.c
-cp ../../../*.h .
-cp ../../../*.c .
-rm pnggccrd.c pngvcrd.c
-rm example.c pngtest.c pngpread.c pngw*.c
-# change the following 2 lines if zlib is somewhere else
-cp ../../../../zlib/*.h .
-cp ../../../../zlib/*.c .
-rm minigzip.c example.c compress.c deflate.c gz*
diff --git a/contrib/pngminim/decoder/makefile b/contrib/pngminim/decoder/makefile
index 4c73839..4acf3c1 100644
--- a/contrib/pngminim/decoder/makefile
+++ b/contrib/pngminim/decoder/makefile
@@ -5,28 +5,84 @@
CC=gcc
LD=$(CC)
-RM=rm -f
+# If awk fails try
+# make AWK=nawk
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \
- -DdeflateParams\(a,b,c\)=Z_OK -I. -O1
+# If cpp fails try
+# make CPP=/lib/cpp
+
+RM=rm -f
+COPY=cp
+
+CPPFLAGS=-I. -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
C=.c
O=.o
L=.a
E=
-ZOBJS = adler32$(O) crc32$(O) \
- infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
- trees$(O) uncompr$(O) zutil$(O)
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/pngminus
-OBJS = pngm2pnm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# Zlib (minimal inflate requirements - crc32 is used by libpng)
+# zutil can be eliminated if you provide your own zcalloc and zcfree
+ZSRCS = adler32$(C) crc32$(C) \
+ inffast$(C) inflate$(C) inftrees$(C) \
+ zutil$(C)
+
+# Standard headers
+ZH = zlib.h crc32.h inffast.h inffixed.h \
+ inflate.h inftrees.h zutil.h
+
+# Machine generated headers
+ZCONF = zconf.h
+
+# Headers callers use
+ZINC = zlib.h $(ZCONF)
+
+# Headers the Zlib source uses
+ZHDRS = $(ZH) $(ZCONF)
+
+ZOBJS = adler32$(O) crc32$(O) \
+ inffast$(O) inflate$(O) inftrees$(O) \
+ zutil$(O)
+
+# libpng
+PNGSRCS=png$(C) pngerror$(C) pngget$(C) pngmem$(C) \
+ pngread$(C) pngrio$(C) pngrtran$(C) pngrutil$(C) \
+ pngset$(C) pngtrans$(C)
+
+# Standard headers
+PNGH =png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h
+
+# Machine generated headers
+PNGCONF=pnglibconf.h
+
+# Headers callers use
+PNGINC= png.h pngconf.h pngusr.h $(PNGCONF)
+
+# Headers the PNG library uses
+PNGHDRS=$(PNGH) $(PNGCONF) pngusr.h
+
+PNGOBJS=png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
- pngset$(O) pngtrans$(O) $(ZOBJS)
+ pngset$(O) pngtrans$(O)
+
+PROGSRCS= pngm2pnm$(C)
+PROGHDRS=
+PROGDOCS=
+PROGOBJS= pngm2pnm$(O)
+
+OBJS = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
# implicit make rules -------------------------------------------------------
-.c$(O): png.h pngconf.h pngusr.h zlib.h
- $(CC) -c $(CFLAGS) $<
+# note: dependencies do not work on implicit rule lines
+.c$(O):
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
# dependencies
@@ -34,11 +90,62 @@
pngm2pnm$(E): $(OBJS)
$(LD) -o pngm2pnm$(E) $(OBJS)
- strip pngm2pnm$(E)
+
+# The DFA_XTRA setting turns all libpng options off then
+# turns on those required for this minimal build.
+# The CPP_FLAGS setting causes pngusr.h to be included in
+# both the build of pnglibconf.h and, subsequently, when
+# building libpng itself.
+$(PNGCONF): $(PNGSRC)/scripts/pnglibconf.mak $(ZH)\
+ $(PNGSRC)/scripts/pnglibconf.dfa \
+ $(PNGSRC)/scripts/options.awk pngusr.h pngusr.dfa
+ $(RM) pnglibconf.h pnglibconf.dfn
+ $(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+ srcdir=$(PNGSRC) CPPFLAGS="-DPNG_USER_CONFIG -I."\
+ DFA_XTRA="pngusr.dfa" $@
clean:
+ $(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+ srcdir=$(PNGSRC) clean
$(RM) pngm2pnm$(O)
$(RM) pngm2pnm$(E)
$(RM) $(OBJS)
+# distclean also removes the copied source and headers
+distclean: clean
+ $(RM) -r scripts # historical reasons
+ $(RM) $(PNGSRCS) $(PNGH)
+ $(RM) $(ZSRCS) $(ZH) $(ZCONF)
+ $(RM) $(PROGSRCS) $(PROGHDRS) $(PROGDOCS)
+
+# Header file dependencies:
+$(PROGOBJS): $(PROGHDRS) $(PNGINC) $(ZINC)
+$(PNGOBJS): $(PNGHDRS) $(ZINC)
+$(ZOBJS): $(ZHDRS)
+
+# Gather the source code from the respective directories
+$(PNGSRCS) $(PNGH): $(PNGSRC)/$@
+ $(RM) $@
+ $(COPY) $(PNGSRC)/$@ $@
+
+# No dependency on the ZLIBSRC target so that it only needs
+# to be specified once.
+$(ZSRCS) $(ZH):
+ $(RM) $@
+ $(COPY) $(ZLIBSRC)/$@ $@
+
+# The unconfigured zconf.h varies in name according to the
+# zlib release
+$(ZCONF):
+ $(RM) $@
+ @for f in zconf.h.in zconf.in.h zconf.h; do\
+ test -r $(ZLIBSRC)/$$f &&\
+ echo $(COPY) $(ZLIBSRC)/$$f $@ &&\
+ $(COPY) $(ZLIBSRC)/$$f $@ && exit 0;\
+ done; echo copy: $(ZLIBSRC)/zconf.h not found; exit 1
+
+pngm2pnm.c: $(PROGSRC)/png2pnm.c
+ $(RM) $@
+ $(COPY) $(PROGSRC)/png2pnm.c $@
+
# End of makefile for pngm2pnm
diff --git a/contrib/pngminim/decoder/makefile.std b/contrib/pngminim/decoder/makefile.std
deleted file mode 100644
index 27e04cb..0000000
--- a/contrib/pngminim/decoder/makefile.std
+++ /dev/null
@@ -1,44 +0,0 @@
-# Makefile for PngMinus (pngm2pnm)
-# Linux / Unix
-
-#CC=cc
-CC=gcc
-LD=$(CC)
-
-RM=rm -f
-
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \
- -DdeflateParams\(a,b,c\)=Z_OK -I. -O1
-
-C=.c
-O=.o
-L=.a
-E=
-
-ZOBJS = adler32$(O) crc32$(O) gzio$(O) \
- infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
- trees$(O) uncompr$(O) zutil$(O)
-
-OBJS = pngm2pnm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
- pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
- pngset$(O) pngtrans$(O) $(ZOBJS)
-
-# implicit make rules -------------------------------------------------------
-
-.c$(O): png.h pngconf.h pngusr.h zlib.h
- $(CC) -c $(CFLAGS) $<
-
-# dependencies
-
-all: pngm2pnm$(E)
-
-pngm2pnm$(E): $(OBJS)
- $(LD) -o pngm2pnm$(E) $(OBJS)
- strip pngm2pnm$(E)
-
-clean:
- $(RM) pngm2pnm$(O)
- $(RM) pngm2pnm$(E)
- $(RM) $(OBJS)
-
-# End of makefile for pngm2pnm
diff --git a/contrib/pngminim/decoder/pngusr.dfa b/contrib/pngminim/decoder/pngusr.dfa
new file mode 100644
index 0000000..4f40272
--- /dev/null
+++ b/contrib/pngminim/decoder/pngusr.dfa
@@ -0,0 +1,40 @@
+# pngminim/decoder/pngusr.dfa
+#
+# Copyright (c) 2010-2013 Glenn Randers-Pehrson
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# First all the build options off:
+
+everything = off
+
+# All that is required is some read code. This example switches
+# on the sequential read code (see ../preader for a progressive
+# read example).
+
+option SEQUENTIAL_READ on
+
+# You must choose fixed or floating point arithmetic:
+# option FLOATING_POINT on
+
+option FIXED_POINT on
+
+# You must chose the internal fixed point implementation or to
+# use the system floating point. The latter is considerably
+# smaller (by about 1kbyte on an x86 system):
+# option FLOATING_ARITHMETIC on
+
+option FLOATING_ARITHMETIC off
+
+# Your program will probably need other options. The example
+# program here, pngm2pnm, requires the following. Take a look
+# at pnglibconf.h to find out the full set of what has to be
+# enabled to make the following work.
+
+option SETJMP on
+option STDIO on
+option READ_EXPAND on
+option READ_STRIP_16_TO_8 on
+option USER_LIMITS on
diff --git a/contrib/pngminim/decoder/pngusr.h b/contrib/pngminim/decoder/pngusr.h
index 2312a78..cbd7890 100644
--- a/contrib/pngminim/decoder/pngusr.h
+++ b/contrib/pngminim/decoder/pngusr.h
@@ -1,6 +1,6 @@
/* minrdpngconf.h: headers to make a minimal png-read-only library
*
- * Copyright (c) 2007, 2009 Glenn Randers-Pehrson
+ * Copyright (c) 2007, 2010-2013 Glenn Randers-Pehrson
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -12,67 +12,12 @@
#ifndef MINRDPNGCONF_H
#define MINRDPNGCONF_H
-#ifdef NJET
- /* No 16-bit support beyond reading with strip_16 */
-#endif
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
-#define PNG_NO_GLOBAL_ARRAYS
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
-#define PNG_NO_WARNINGS
-#define png_warning(s1,s2) ""
-#define png_chunk_warning(s1,s2) ""
-#define PNG_NO_ERROR_TEXT
-#define png_error(s1,s2) png_err(s1)
-#define png_chunk_error(s1,s2) png_err(s1)
-
-#define PNG_NO_ASSEMBLER_CODE
-#define PNG_NO_OPTIMIZED_CODE
-#define PNG_NO_READ_GAMMA
-#define PNG_NO_READ_BACKGROUND
-#define PNG_NO_READ_DITHER
-#define PNG_NO_READ_INVERT
-#define PNG_NO_READ_SHIFT
-#define PNG_NO_READ_PACK
-#define PNG_NO_READ_PACKSWAP
-#define PNG_NO_READ_FILLER
-#define PNG_NO_READ_SWAP
-#define PNG_NO_READ_SWAP_ALPHA
-#define PNG_NO_READ_INVERT_ALPHA
-#define PNG_NO_READ_RGB_TO_GRAY
-#define PNG_NO_READ_USER_TRANSFORM
-#define PNG_NO_READ_bKGD
-#define PNG_NO_READ_cHRM
-#define PNG_NO_READ_gAMA
-#define PNG_NO_READ_hIST
-#define PNG_NO_READ_iCCP
-#define PNG_NO_READ_pCAL
-#define PNG_NO_READ_pHYs
-#define PNG_NO_READ_sBIT
-#define PNG_NO_READ_sCAL
-#define PNG_NO_READ_sPLT
-#define PNG_NO_READ_sRGB
-#define PNG_NO_READ_TEXT
-#define PNG_NO_READ_tIME
-#define PNG_NO_READ_UNKNOWN_CHUNKS
-#define PNG_NO_READ_USER_CHUNKS
-#define PNG_NO_READ_EMPTY_PLTE
-#define PNG_NO_READ_OPT_PLTE
-#define PNG_NO_READ_STRIP_ALPHA
-#define PNG_NO_READ_oFFs
-#define PNG_NO_WARN_UNINITIALIZED_ROW
-
-#define PNG_NO_WRITE_SUPPORTED
-
-#define PNG_NO_INFO_IMAGE
-#define PNG_NO_USER_MEM
-#define PNG_NO_FIXED_POINT_SUPPORTED
-#define PNG_NO_MNG_FEATURES
-#define PNG_NO_USER_TRANSFORM_PTR
-#define PNG_NO_HANDLE_AS_UNKNOWN
-#define PNG_NO_CONSOLE_IO
-#define PNG_NO_ZALLOC_ZERO
-#define PNG_NO_ERROR_NUMBERS
-#define PNG_NO_EASY_ACCESS
-#define PNG_NO_PROGRESSIVE_READ
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
#endif /* MINRDPNGCONF_H */
diff --git a/contrib/pngminim/encoder/README b/contrib/pngminim/encoder/README
index 2285544..d8d9a64 100644
--- a/contrib/pngminim/encoder/README
+++ b/contrib/pngminim/encoder/README
@@ -1,9 +1,10 @@
This demonstrates the use of PNG_USER_CONFIG and pngusr.h
-To build a minimal write-only decoder with embedded libpng and zlib, run
+The makefile builds a minimal write-only encoder with embedded libpng
+and zlib.
- gather.sh # to collect needed files from pngminus, libpng, and zlib
- make
-
+Specify the location of the zlib source (1.2.1 or later) as ZLIBSRC
+on the make command line.
+
If you prefer to use the shared libraries, go to contrib/pngminus
-and build the pnm2png application there.
+and build the pnm2png application there.
diff --git a/contrib/pngminim/encoder/dummy_inflate.c b/contrib/pngminim/encoder/dummy_inflate.c
deleted file mode 100644
index 1422edb..0000000
--- a/contrib/pngminim/encoder/dummy_inflate.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "zlib.h"
-
-int ZEXPORT inflate(strm, flush)
-z_streamp strm;
-int flush;
-{ return Z_OK ; }
-
-int ZEXPORT inflateReset(strm)
-z_streamp strm;
-{ return Z_OK ; }
-
-int ZEXPORT inflateEnd(strm)
-z_streamp strm;
-{ return Z_STREAM_ERROR ; }
-
-int ZEXPORT inflateInit_(strm, version, stream_size)
-z_streamp strm;
-const char *version;
-int stream_size;
-{ return Z_OK ; }
-
-int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
-z_streamp strm;
-int windowBits;
-const char *version;
-int stream_size;
-{ return Z_STREAM_ERROR ; }
diff --git a/contrib/pngminim/encoder/gather.sh b/contrib/pngminim/encoder/gather.sh
deleted file mode 100755
index 61d0b13..0000000
--- a/contrib/pngminim/encoder/gather.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-cp ../../pngminus/pnm2png.c pnm2pngm.c
-cp ../../../*.h .
-cp ../../../*.c .
-rm pnggccrd.c pngvcrd.c
-rm example.c pngtest.c pngr*.c pngpread.c
-# Change the next 2 lines if zlib is somewhere else.
-cp ../../../../zlib/*.h .
-cp ../../../../zlib/*.c .
-rm inf*.[ch]
-rm minigzip.c example.c gz*
diff --git a/contrib/pngminim/encoder/makefile b/contrib/pngminim/encoder/makefile
index dfde950..41b205c 100644
--- a/contrib/pngminim/encoder/makefile
+++ b/contrib/pngminim/encoder/makefile
@@ -5,27 +5,83 @@
CC=gcc
LD=$(CC)
-RM=rm -f
+# If awk fails try
+# make AWK=nawk
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZIP -I. -O1
+# If cpp fails try
+# make CPP=/lib/cpp
+
+RM=rm -f
+COPY=cp
+
+CPPFLAGS=-I. -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
C=.c
O=.o
L=.a
E=
-ZOBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) \
- dummy_inflate$(O) \
- trees$(O) uncompr$(O) zutil$(O)
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/pngminus
-OBJS = pnm2pngm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# Zlib
+ZSRCS = adler32$(C) compress$(C) crc32$(C) deflate$(C) \
+ trees$(C) zutil$(C)
+
+# Standard headers
+#ZH = zlib.h crc32.h deflate.h trees.h zutil.h
+ZH = zlib.h crc32.h deflate.h trees.h zutil.h
+
+# Machine generated headers
+ZCONF = zconf.h
+
+# Headers callers use
+ZINC = zlib.h $(ZCONF)
+
+# Headers the Zlib source uses
+ZHDRS = $(ZH) $(ZCONF)
+
+# compress is not required; it is needed to link the zlib
+# code because deflate defines an unused API function deflateBound
+# which itself calls compressBound from compress.
+ZOBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) \
+ trees$(O) zutil$(O)
+
+# libpng
+PNGSRCS=png$(C) pngerror$(C) pngget$(C) pngmem$(C) \
+ pngset$(C) pngtrans$(C) pngwio$(C) pngwrite$(C) \
+ pngwtran$(C) pngwutil$(C)
+
+# Standard headers
+PNGH =png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h
+
+# Machine generated headers
+PNGCONF=pnglibconf.h
+
+# Headers callers use
+PNGINC= png.h pngconf.h pngusr.h $(PNGCONF)
+
+# Headers the PNG library uses
+PNGHDRS=$(PNGH) $(PNGCONF) pngusr.h
+
+PNGOBJS=png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
pngset$(O) pngtrans$(O) pngwio$(O) pngwrite$(O) \
- pngwtran$(O) pngwutil$(O) $(ZOBJS)
+ pngwtran$(O) pngwutil$(O)
+
+PROGSRCS= pnm2pngm$(C)
+PROGHDRS=
+PROGDOCS=
+PROGOBJS= pnm2pngm$(O)
+
+OBJS = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
# implicit make rules -------------------------------------------------------
-.c$(O): png.h pngconf.h pngusr.h zlib.h
- $(CC) -c $(CFLAGS) $<
+.c$(O):
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
# dependencies
@@ -33,11 +89,62 @@
pnm2pngm$(E): $(OBJS)
$(LD) -o pnm2pngm$(E) $(OBJS)
- strip pnm2pngm$(E)
+
+# The DFA_XTRA setting turns all libpng options off then
+# turns on those required for this minimal build.
+# The CPP_FLAGS setting causes pngusr.h to be included in
+# both the build of pnglibconf.h and, subsequently, when
+# building libpng itself.
+$(PNGCONF): $(PNGSRC)/scripts/pnglibconf.mak $(ZH)\
+ $(PNGSRC)/scripts/pnglibconf.dfa \
+ $(PNGSRC)/scripts/options.awk pngusr.h pngusr.dfa
+ $(RM) pnglibconf.h pnglibconf.dfn
+ $(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+ srcdir=$(PNGSRC) CPPFLAGS="-DPNG_USER_CONFIG -I."\
+ DFA_XTRA="pngusr.dfa" $@
clean:
+ $(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+ srcdir=$(PNGSRC) clean
$(RM) pnm2pngm$(O)
$(RM) pnm2pngm$(E)
$(RM) $(OBJS)
+# distclean also removes the copied source and headers
+distclean: clean
+ $(RM) -r scripts # historical reasons
+ $(RM) $(PNGSRCS) $(PNGH)
+ $(RM) $(ZSRCS) $(ZH) $(ZCONF)
+ $(RM) $(PROGSRCS) $(PROGHDRS) $(PROGDOCS)
+
+# Header file dependencies:
+$(PROGOBJS): $(PROGHDRS) $(PNGINC) $(ZINC)
+$(PNGOBJS): $(PNGHDRS) $(ZINC)
+$(ZOBJS): $(ZHDRS)
+
+# Gather the source code from the respective directories
+$(PNGSRCS) $(PNGH): $(PNGSRC)/$@
+ $(RM) $@
+ $(COPY) $(PNGSRC)/$@ $@
+
+# No dependency on the ZLIBSRC target so that it only needs
+# to be specified once.
+$(ZSRCS) $(ZH):
+ $(RM) $@
+ $(COPY) $(ZLIBSRC)/$@ $@
+
+# The unconfigured zconf.h varies in name according to the
+# zlib release
+$(ZCONF):
+ $(RM) $@
+ @for f in zconf.h.in zconf.in.h zconf.h; do\
+ test -r $(ZLIBSRC)/$$f &&\
+ echo $(COPY) $(ZLIBSRC)/$$f $@ &&\
+ $(COPY) $(ZLIBSRC)/$$f $@ && exit 0;\
+ done; echo copy: $(ZLIBSRC)/zconf.h not found; exit 1
+
+pnm2pngm.c: $(PROGSRC)/pnm2png.c
+ $(RM) $@
+ $(COPY) $(PROGSRC)/pnm2png.c $@
+
# End of makefile for pnm2pngm
diff --git a/contrib/pngminim/encoder/makefile.std b/contrib/pngminim/encoder/makefile.std
deleted file mode 100644
index 1182b5b..0000000
--- a/contrib/pngminim/encoder/makefile.std
+++ /dev/null
@@ -1,43 +0,0 @@
-# Makefile for PngMinus (pnm2pngm)
-# Linux / Unix
-
-#CC=cc
-CC=gcc
-LD=$(CC)
-
-RM=rm -f
-
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZIP -I. -O1
-
-C=.c
-O=.o
-L=.a
-E=
-
-ZOBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzio$(O) \
- dummy_inflate$(O) \
- trees$(O) uncompr$(O) zutil$(O)
-
-OBJS = pnm2pngm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
- pngset$(O) pngtrans$(O) pngwio$(O) pngwrite$(O) \
- pngwtran$(O) pngwutil$(O) $(ZOBJS)
-
-# implicit make rules -------------------------------------------------------
-
-.c$(O): png.h pngconf.h pngusr.h zlib.h
- $(CC) -c $(CFLAGS) $<
-
-# dependencies
-
-all: pnm2pngm$(E)
-
-pnm2pngm$(E): $(OBJS)
- $(LD) -o pnm2pngm$(E) $(OBJS)
- strip pnm2pngm$(E)
-
-clean:
- $(RM) pnm2pngm$(O)
- $(RM) pnm2pngm$(E)
- $(RM) $(OBJS)
-
-# End of makefile for pnm2pngm
diff --git a/contrib/pngminim/encoder/pngusr.dfa b/contrib/pngminim/encoder/pngusr.dfa
new file mode 100644
index 0000000..1fc24f3
--- /dev/null
+++ b/contrib/pngminim/encoder/pngusr.dfa
@@ -0,0 +1,39 @@
+# pngminim/encoder/pngusr.dfa
+#
+# Copyright (c) 2010-2013 Glenn Randers-Pehrson
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# First all the build options off:
+
+everything = off
+
+# Switch on the write code - this makes a minimalist encoder
+
+option WRITE on
+
+# These 2 options are required if you need to read PBM (P1 or P4) files.
+option WRITE_INVERT on
+option WRITE_PACK on
+
+# You must choose fixed or floating point arithmetic:
+# option FLOATING_POINT on
+
+option FIXED_POINT on
+
+# You must chose the internal fixed point implementation or to
+# use the system floating point. The latter is considerably
+# smaller (by about 1kbyte on an x86 system):
+# option FLOATING_ARITHMETIC on
+
+option FLOATING_ARITHMETIC off
+
+# Your program will probably need other options. The example
+# program here, pnm2pngm, requires the following. Take a look
+# at pnglibconf.h to find out the full set of what has to be
+# enabled to make the following work.
+
+option SETJMP on
+option STDIO on
diff --git a/contrib/pngminim/encoder/pngusr.h b/contrib/pngminim/encoder/pngusr.h
index c98b547..997d44f 100644
--- a/contrib/pngminim/encoder/pngusr.h
+++ b/contrib/pngminim/encoder/pngusr.h
@@ -1,6 +1,6 @@
/* minwrpngconf.h: headers to make a minimal png-write-only library
*
- * Copyright (c) 2007, 2009 Glenn Randers-Pehrson
+ * Copyright (c) 2007, 2010-2013 Glenn Randers-Pehrson
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -12,62 +12,12 @@
#ifndef MINWRPNGCONF_H
#define MINWRPNGCONF_H
-#define PNG_NO_GLOBAL_ARRAYS
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
-#define PNG_NO_READ_SUPPORTED
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
-#define PNG_NO_WARNINGS
-#define png_warning(s1,s2) ""
-#define png_chunk_warning(s1,s2) ""
-#define PNG_NO_ERROR_TEXT
-#define png_error(s1,s2) png_err(s1)
-#define png_chunk_error(s1,s2) png_err(s1)
-
-#define PNG_NO_WRITE_BACKGROUND
-#define PNG_NO_WRITE_GAMMA
-#define PNG_NO_WRITE_DITHER
-#define PNG_NO_WRITE_INVERT
-#define PNG_NO_WRITE_SHIFT
-#define PNG_NO_WRITE_PACK
-#define PNG_NO_WRITE_PACKSWAP
-#define PNG_NO_WRITE_FILLER
-#define PNG_NO_WRITE_SWAP
-#define PNG_NO_WRITE_SWAP_ALPHA
-#define PNG_NO_WRITE_INVERT_ALPHA
-#define PNG_NO_WRITE_RGB_TO_GRAY
-#define PNG_NO_WRITE_USER_TRANSFORM
-#define PNG_NO_WRITE_bKGD
-#define PNG_NO_WRITE_cHRM
-#define PNG_NO_WRITE_gAMA
-#define PNG_NO_WRITE_hIST
-#define PNG_NO_WRITE_iCCP
-#define PNG_NO_WRITE_oFFs
-#define PNG_NO_WRITE_pCAL
-#define PNG_NO_WRITE_pHYs
-#define PNG_NO_WRITE_sBIT
-#define PNG_NO_WRITE_sCAL
-#define PNG_NO_WRITE_sPLT
-#define PNG_NO_WRITE_sRGB
-#define PNG_NO_WRITE_TEXT
-#define PNG_NO_WRITE_tIME
-#define PNG_NO_WRITE_UNKNOWN_CHUNKS
-#define PNG_NO_WRITE_USER_CHUNKS
-#define PNG_NO_WRITE_EMPTY_PLTE
-#define PNG_NO_WRITE_OPT_PLTE
-#define PNG_NO_WRITE_FILTER
-#define PNG_NO_WRITE_WEIGHTED_FILTER
-#define PNG_NO_WRITE_INTERLACING_SUPPORTED
-#define PNG_NO_WRITE_FLUSH
-
-#define PNG_NO_INFO_IMAGE
-#define PNG_NO_USER_MEM
-#define PNG_NO_FIXED_POINT_SUPPORTED
-#define PNG_NO_MNG_FEATURES
-#define PNG_NO_USER_TRANSFORM_PTR
-#define PNG_NO_HANDLE_AS_UNKNOWN
-#define PNG_NO_CONSOLE_IO
-#define PNG_NO_ZALLOC_ZERO
-#define PNG_NO_ERROR_NUMBERS
-#define PNG_NO_EASY_ACCESS
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
#endif /* MINWRPNGCONF_H */
diff --git a/contrib/pngminim/preader/README b/contrib/pngminim/preader/README
index 377d641..faa8356 100644
--- a/contrib/pngminim/preader/README
+++ b/contrib/pngminim/preader/README
@@ -1,14 +1,15 @@
This demonstrates the use of PNG_USER_CONFIG and pngusr.h
-To build a minimal read-only progressive decoder embedded libpng and
-zlib and with your system's X library, run
+The makefile builds a minimal read-only progressive decoder with
+embedded libpng, zlib and your system's X library.
- gather.sh # to collect needed files from gregbook, libpng, and zlib
+Specify the location of the zlib source (1.2.1 or later) as ZLIBSRC
+on the make command line.
Edit makefile if required, to find your X library and include files,
then
-
- make
-
+
+ make ZLIBSRC=directory
+
If you prefer to use the shared libraries, go to contrib/gregbook
-and build the rpng2-x application there.
+and build the rpng2-x application there.
diff --git a/contrib/pngminim/preader/gather.sh b/contrib/pngminim/preader/gather.sh
deleted file mode 100755
index 357bb9a..0000000
--- a/contrib/pngminim/preader/gather.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-cp ../../gregbook/rpng2-x.c ../../gregbook/readpng2.[ch] .
-cp ../../gregbook/COPYING ../../gregbook/LICENSE .
-cp ../../../*.h .
-cp ../../../*.c .
-rm pnggccrd.c pngvcrd.c
-rm example.c pngtest.c pngw*.c
-# change the following 2 lines if zlib is somewhere else
-cp ../../../../zlib/*.h .
-cp ../../../../zlib/*.c .
-rm minigzip.c example.c compress.c deflate.c gz*
diff --git a/contrib/pngminim/preader/makefile b/contrib/pngminim/preader/makefile
index 717978d..b625199 100644
--- a/contrib/pngminim/preader/makefile
+++ b/contrib/pngminim/preader/makefile
@@ -5,7 +5,14 @@
CC=gcc
LD=$(CC)
+# If awk fails try
+# make AWK=nawk
+
+# If cpp fails try
+# make CPP=/lib/cpp
+
RM=rm -f
+COPY=cp
#XINC = -I/usr/include # old-style, stock X distributions
#XLIB = -L/usr/lib/X11 -lX11 # (including SGI IRIX)
@@ -23,38 +30,137 @@
#LIBS = $(XLIB)
LIBS = $(XLIB) -lm #platforms that need libm
-CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \
- -DdeflateParams\(a,b,c\)=Z_OK -I. $(XINC) -O1
+CPPFLAGS=-I. $(XINC) -DPNG_USER_CONFIG -DNO_GZCOMPRESS -DZ_SOLO -DNO_GZIP
+CFLAGS=-O1 -Wall
C=.c
O=.o
L=.a
E=
-ZOBJS = adler32$(O) crc32$(O) \
- infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
- trees$(O) uncompr$(O) zutil$(O)
+# Where to find the source code:
+PNGSRC =../../..
+ZLIBSRC=$(PNGSRC)/../zlib
+PROGSRC=$(PNGSRC)/contrib/gregbook
-OBJS = rpng2-x$(O) readpng2$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# Zlib (minimal inflate requirements - crc32 is used by libpng)
+# zutil can be eliminated if you provide your own zcalloc and zcfree
+ZSRCS = adler32$(C) crc32$(C) \
+ inffast$(C) inflate$(C) inftrees$(C) \
+ zutil$(C)
+
+# Standard headers
+ZH = zlib.h crc32.h inffast.h inffixed.h \
+ inflate.h inftrees.h zutil.h
+
+# Machine generated headers
+ZCONF = zconf.h
+
+# Headers callers use
+ZINC = zlib.h $(ZCONF)
+
+# Headers the Zlib source uses
+ZHDRS = $(ZH) $(ZCONF)
+
+ZOBJS = adler32$(O) crc32$(O) \
+ inffast$(O) inflate$(O) inftrees$(O) \
+ zutil$(O)
+
+# libpng
+PNGSRCS=png$(C) pngerror$(C) pngget$(C) pngmem$(C) \
+ pngpread$(C) pngread$(C) pngrio$(C) pngrtran$(C) pngrutil$(C) \
+ pngset$(C) pngtrans$(C)
+
+# Standard headers
+PNGH =png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h
+
+# Machine generated headers
+PNGCONF=pnglibconf.h
+
+# Headers callers use
+PNGINC= png.h pngconf.h pngusr.h $(PNGCONF)
+
+# Headers the PNG library uses
+PNGHDRS=$(PNGH) $(PNGCONF) pngusr.h
+
+PNGOBJS=png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
pngpread$(O) pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
- pngset$(O) pngtrans$(O) $(ZOBJS)
+ pngset$(O) pngtrans$(O)
+
+PROGSRCS= rpng2-x$(C) readpng2$(C)
+PROGHDRS= readpng2.h
+PROGDOCS= COPYING LICENSE
+PROGOBJS= rpng2-x$(O) readpng2$(O)
+
+OBJS = $(PROGOBJS) $(PNGOBJS) $(ZOBJS)
# implicit make rules -------------------------------------------------------
-.c$(O): png.h pngconf.h readpng2.h pngusr.h zlib.h
- $(CC) -c $(CFLAGS) $<
+.c$(O):
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $<
# dependencies
-all: rpng2-x$(E)
+all: $(PROGDOCS) rpng2-x$(E)
rpng2-x$(E): $(OBJS)
$(LD) -o rpng2-x$(E) $(OBJS) $(LIBS)
- strip rpng2-x$(E)
+
+# The DFA_XTRA setting turns all libpng options off then
+# turns on those required for this minimal build.
+# The CPP_FLAGS setting causes pngusr.h to be included in
+# both the build of pnglibconf.h and, subsequently, when
+# building libpng itself.
+$(PNGCONF): $(PNGSRC)/scripts/pnglibconf.mak $(ZH)\
+ $(PNGSRC)/scripts/pnglibconf.dfa \
+ $(PNGSRC)/scripts/options.awk pngusr.h pngusr.dfa
+ $(RM) pnglibconf.h pnglibconf.dfn
+ $(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+ srcdir=$(PNGSRC) CPPFLAGS="-DPNG_USER_CONFIG -I."\
+ DFA_XTRA="pngusr.dfa" $@
clean:
+ $(MAKE) -f $(PNGSRC)/scripts/pnglibconf.mak $(MAKEFLAGS)\
+ srcdir=$(PNGSRC) clean
$(RM) rpng2-x$(O)
$(RM) rpng2-x$(E)
$(RM) $(OBJS)
+# distclean also removes the copied source and headers
+distclean: clean
+ $(RM) -r scripts # historical reasons
+ $(RM) $(PNGSRCS) $(PNGH)
+ $(RM) $(ZSRCS) $(ZH) $(ZCONF)
+ $(RM) $(PROGSRCS) $(PROGHDRS) $(PROGDOCS)
+
+# Header file dependencies:
+$(PROGOBJS): $(PROGHDRS) $(PNGINC) $(ZINC)
+$(PNGOBJS): $(PNGHDRS) $(ZINC)
+$(ZOBJS): $(ZHDRS)
+
+# Gather the source code from the respective directories
+$(PNGSRCS) $(PNGH): $(PNGSRC)/$@
+ $(RM) $@
+ $(COPY) $(PNGSRC)/$@ $@
+
+# No dependency on the ZLIBSRC target so that it only needs
+# to be specified once.
+$(ZSRCS) $(ZH):
+ $(RM) $@
+ $(COPY) $(ZLIBSRC)/$@ $@
+
+# The unconfigured zconf.h varies in name according to the
+# zlib release
+$(ZCONF):
+ $(RM) $@
+ @for f in zconf.h.in zconf.in.h zconf.h; do\
+ test -r $(ZLIBSRC)/$$f &&\
+ echo $(COPY) $(ZLIBSRC)/$$f $@ &&\
+ $(COPY) $(ZLIBSRC)/$$f $@ && exit 0;\
+ done; echo copy: $(ZLIBSRC)/zconf.h not found; exit 1
+
+$(PROGSRCS) $(PROGHDRS) $(PROGDOCS): $(PROGSRC)/$@
+ $(RM) $@
+ $(COPY) $(PROGSRC)/$@ $@
+
# End of makefile for rpng2-x
diff --git a/contrib/pngminim/preader/pngusr.dfa b/contrib/pngminim/preader/pngusr.dfa
new file mode 100644
index 0000000..ac297f4
--- /dev/null
+++ b/contrib/pngminim/preader/pngusr.dfa
@@ -0,0 +1,40 @@
+# pngminim/preader/pngusr.dfa
+#
+# Copyright (c) 2010-2013 Glenn Randers-Pehrson
+#
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# First all the build options off:
+
+everything = off
+
+# Just switch on the progressive read code
+
+option PROGRESSIVE_READ on
+
+# You may choose fixed or floating point APIs:
+# option FLOATING_POINT on
+
+option FIXED_POINT on
+
+# You must chose the internal fixed point implementation or to
+# use the system floating point. The latter is considerably
+# smaller (by about 1kbyte on an x86 system):
+
+option FLOATING_ARITHMETIC on
+# option FLOATING_ARITHMETIC off
+
+# Your program will probably need other options. The example
+# program here, rpng2-x, requires the following. Take a look
+# at pnglibconf.h to find out the full set of what has to be
+# enabled to make the following work.
+
+option SETJMP on
+option STDIO on
+option READ_bKGD on
+option READ_GAMMA on
+option READ_EXPAND on
+option READ_STRIP_16_TO_8 on
+option READ_GRAY_TO_RGB on
diff --git a/contrib/pngminim/preader/pngusr.h b/contrib/pngminim/preader/pngusr.h
index 7d728c8..80db3bb 100644
--- a/contrib/pngminim/preader/pngusr.h
+++ b/contrib/pngminim/preader/pngusr.h
@@ -1,6 +1,6 @@
/* minrdpngconf.h: headers to make a minimal png-read-only library
*
- * Copyright (c) 2009 Glenn Randers-Pehrson
+ * Copyright (c) 2009, 2010-2013 Glenn Randers-Pehrson
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -12,57 +12,12 @@
#ifndef MINPRDPNGCONF_H
#define MINPRDPNGCONF_H
-#define PNG_NO_GLOBAL_ARRAYS
+/* To include pngusr.h set -DPNG_USER_CONFIG in CPPFLAGS */
-#define PNG_NO_WARNINGS
-#define png_warning(s1,s2) ""
-#define png_chunk_warning(s1,s2) ""
-#define PNG_NO_ERROR_TEXT
-#define png_error(s1,s2) png_err(s1)
-#define png_chunk_error(s1,s2) png_err(s1)
+/* List options to turn off features of the build that do not
+ * affect the API (so are not recorded in pnglibconf.h)
+ */
-#define PNG_NO_ASSEMBLER_CODE
-#define PNG_NO_OPTIMIZED_CODE
-#define PNG_NO_READ_DITHER
-#define PNG_NO_READ_INVERT
-#define PNG_NO_READ_SHIFT
-#define PNG_NO_READ_PACK
-#define PNG_NO_READ_PACKSWAP
-#define PNG_NO_READ_FILLER
-#define PNG_NO_READ_SWAP
-#define PNG_NO_READ_SWAP_ALPHA
-#define PNG_NO_READ_INVERT_ALPHA
-#define PNG_NO_READ_RGB_TO_GRAY
-#define PNG_NO_READ_USER_TRANSFORM
-#define PNG_NO_READ_cHRM
-#define PNG_NO_READ_hIST
-#define PNG_NO_READ_iCCP
-#define PNG_NO_READ_pCAL
-#define PNG_NO_READ_pHYs
-#define PNG_NO_READ_sBIT
-#define PNG_NO_READ_sCAL
-#define PNG_NO_READ_sPLT
-#define PNG_NO_READ_TEXT
-#define PNG_NO_READ_tIME
-#define PNG_NO_READ_UNKNOWN_CHUNKS
-#define PNG_NO_READ_USER_CHUNKS
-#define PNG_NO_READ_EMPTY_PLTE
-#define PNG_NO_READ_OPT_PLTE
-#define PNG_NO_READ_STRIP_ALPHA
-#define PNG_NO_READ_oFFs
-#define PNG_NO_WARN_UNINITIALIZED_ROW
-
-#define PNG_NO_WRITE_SUPPORTED
-
-#define PNG_NO_INFO_IMAGE
-#define PNG_NO_USER_MEM
-#define PNG_NO_FIXED_POINT_SUPPORTED
-#define PNG_NO_MNG_FEATURES
-#define PNG_NO_USER_TRANSFORM_PTR
-#define PNG_NO_HANDLE_AS_UNKNOWN
-#define PNG_NO_CONSOLE_IO
-#define PNG_NO_ZALLOC_ZERO
-#define PNG_NO_ERROR_NUMBERS
-#define PNG_NO_EASY_ACCESS
+#define PNG_ALIGN_TYPE PNG_ALIGN_NONE
#endif /* MINPRDPNGCONF_H */
diff --git a/contrib/pngminus/makefile.std b/contrib/pngminus/makefile.std
index 2fb061b..14e25cd 100644
--- a/contrib/pngminus/makefile.std
+++ b/contrib/pngminus/makefile.std
@@ -8,9 +8,9 @@
RM=rm -f
#PNGPATH = /usr/local
-#PNGINC = -I$(PNGPATH)/include/libpng12
-#PNGLIB = -L$(PNGPATH)/lib -lpng12
-#PNGLIBS = $(PNGPATH)/lib/libpng12.a
+#PNGINC = -I$(PNGPATH)/include/libpng16
+#PNGLIB = -L$(PNGPATH)/lib -lpng16
+#PNGLIBS = $(PNGPATH)/lib/libpng16.a
PNGINC = -I../..
PNGLIB = -L../.. -lpng
PNGLIBS = ../../libpng.a
@@ -23,9 +23,10 @@
ZLIB = -L../../../zlib -lz
ZLIBS = ../../../zlib/libz.a
-CFLAGS=-O3 $(PNGINC) $(ZINC)
-LDFLAGS=$(PNGLIB) $(ZLIB)
-LDFLAGSS=$(PNGLIBS) $(ZLIBS)
+CPPFLAGS=$(PNGINC) $(ZINC)
+CFLAGS=
+LDLIBS=$(PNGLIB) $(ZLIB)
+LDLIBSS=$(PNGLIBS) $(ZLIBS)
C=.c
O=.o
L=.a
@@ -37,22 +38,22 @@
all: png2pnm$(E) pnm2png$(E) png2pnm-static$(E) pnm2png-static$(E)
png2pnm$(O): png2pnm$(C)
- $(CC) -c $(CFLAGS) png2pnm$(C)
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) png2pnm$(C)
png2pnm$(E): png2pnm$(O)
- $(LD) -o png2pnm$(E) png2pnm$(O) $(LDFLAGS) -lm
+ $(LD) $(LDFLAGS) -o png2pnm$(E) png2pnm$(O) $(LDLIBS) -lm
png2pnm-static$(E): png2pnm$(O)
- $(LD) -o png2pnm-static$(E) png2pnm$(O) $(LDFLAGSS) -lm
+ $(LD) $(LDFLAGS) -o png2pnm-static$(E) png2pnm$(O) $(LDLIBSS) -lm
pnm2png$(O): pnm2png$(C)
- $(CC) -c $(CFLAGS) pnm2png$(C)
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) pnm2png$(C)
pnm2png$(E): pnm2png$(O)
- $(LD) -o pnm2png$(E) pnm2png$(O) $(LDFLAGS) -lm
+ $(LD) $(LDFLAGS) -o pnm2png$(E) pnm2png$(O) $(LDLIBS) -lm
pnm2png-static$(E): pnm2png$(O)
- $(LD) -o pnm2png-static$(E) pnm2png$(O) $(LDFLAGSS) -lm
+ $(LD) $(LDFLAGS) -o pnm2png-static$(E) pnm2png$(O) $(LDLIBSS) -lm
clean:
$(RM) png2pnm$(O)
diff --git a/contrib/pngminus/makefile.tc3 b/contrib/pngminus/makefile.tc3
index 404f18d..6a2f4b9 100644
--- a/contrib/pngminus/makefile.tc3
+++ b/contrib/pngminus/makefile.tc3
@@ -7,7 +7,8 @@
RM=del
CP=copy
MODEL=l
-CCFLAGS=-O -m$(MODEL) -I..\libpng -I..\zlib
+CPPFLAGS=-I..\libpng -I..\zlib
+CFLAGS=-O -m$(MODEL)
LDFLAGS=-m$(MODEL) -L..\libpng -L..\zlib
C=.c
O=.obj
@@ -19,13 +20,13 @@
all: png2pnm$(E) pnm2png$(E)
png2pnm$(O): png2pnm$(C)
- $(CC) -c $(CCFLAGS) png2pnm$(C)
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) png2pnm$(C)
png2pnm$(E): png2pnm$(O)
$(LD) $(LDFLAGS) png2pnm$(O) libpng$(L) zlib$(L)
pnm2png$(O): pnm2png$(C)
- $(CC) -c $(CCFLAGS) pnm2png$(C)
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) pnm2png$(C)
pnm2png$(E): pnm2png$(O)
$(LD) $(LDFLAGS) pnm2png$(O) libpng$(L) zlib$(L)
@@ -35,4 +36,3 @@
$(RM) *$(E)
# End of makefile for png2pnm / pnm2png
-
diff --git a/contrib/pngminus/png2pnm.bat b/contrib/pngminus/png2pnm.bat
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/png2pnm.c b/contrib/pngminus/png2pnm.c
index ac295aa..f68d7ff 100644
--- a/contrib/pngminus/png2pnm.c
+++ b/contrib/pngminus/png2pnm.c
@@ -18,6 +18,7 @@
#include <mem.h>
#include <fcntl.h>
#endif
+#include <zlib.h>
#ifndef BOOL
#define BOOL unsigned char
@@ -51,7 +52,8 @@
int main (int argc, char *argv[]);
void usage ();
-BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha);
+BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw,
+ BOOL alpha);
/*
* main
@@ -84,7 +86,8 @@
if ((fp_al = fopen (argv[argi], "wb")) == NULL)
{
fprintf (stderr, "PNM2PNG\n");
- fprintf (stderr, "Error: can not create alpha-channel file %s\n", argv[argi]);
+ fprintf (stderr, "Error: can not create alpha-channel file %s\n",
+ argv[argi]);
exit (1);
}
break;
@@ -144,7 +147,7 @@
if (png2pnm (fp_rd, fp_wr, fp_al, raw, alpha) == FALSE)
{
fprintf (stderr, "PNG2PNM\n");
- fprintf (stderr, "Error: unsuccessful convertion of PNG-image\n");
+ fprintf (stderr, "Error: unsuccessful conversion of PNG-image\n");
exit(1);
}
@@ -175,9 +178,11 @@
fprintf (stderr, "Usage: png2pnm [options] <file>.png [<file>.pnm]\n");
fprintf (stderr, " or: ... | png2pnm [options]\n");
fprintf (stderr, "Options:\n");
- fprintf (stderr, " -r[aw] write pnm-file in binary format (P4/P5/P6) (default)\n");
+ fprintf (stderr,
+ " -r[aw] write pnm-file in binary format (P4/P5/P6) (default)\n");
fprintf (stderr, " -n[oraw] write pnm-file in ascii format (P1/P2/P3)\n");
- fprintf (stderr, " -a[lpha] <file>.pgm write PNG alpha channel as pgm-file\n");
+ fprintf (stderr,
+ " -a[lpha] <file>.pgm write PNG alpha channel as pgm-file\n");
fprintf (stderr, " -h | -? print this help-information\n");
}
@@ -185,10 +190,11 @@
* png2pnm
*/
-BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha)
+BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
+ volatile BOOL raw, BOOL alpha)
{
png_struct *png_ptr = NULL;
- png_info *info_ptr = NULL;
+ png_info *info_ptr = NULL;
png_byte buf[8];
png_byte *png_pixels = NULL;
png_byte **row_pointers = NULL;
@@ -211,13 +217,13 @@
if (ret != 8)
return FALSE;
- ret = !png_sig_cmp (buf, 0, 8);
- if (!ret)
+ ret = png_sig_cmp (buf, 0, 8);
+ if (ret)
return FALSE;
/* create png and info structures */
- png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+ png_ptr = png_create_read_struct (png_get_libpng_ver(NULL),
NULL, NULL, NULL);
if (!png_ptr)
return FALSE; /* out of memory */
@@ -260,7 +266,7 @@
png_set_expand (png_ptr);
#ifdef NJET
- /* downgrade 16-bit images to 8 bit */
+ /* downgrade 16-bit images to 8-bit */
if (bit_depth == 16)
png_set_strip_16 (png_ptr);
/* transform grayscale images into full-color */
@@ -314,12 +320,14 @@
/* row_bytes is the width x number of channels x (bit-depth / 8) */
row_bytes = png_get_rowbytes (png_ptr, info_ptr);
- if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL) {
+ if ((png_pixels = (png_byte *)
+ malloc (row_bytes * height * sizeof (png_byte))) == NULL) {
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return FALSE;
}
- if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL)
+ if ((row_pointers = (png_byte **)
+ malloc (height * sizeof (png_bytep))) == NULL)
{
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
free (png_pixels);
@@ -328,7 +336,7 @@
}
/* set the individual row_pointers to point at the correct offsets */
- for (i = 0; i < (height); i++)
+ for (i = 0; i < ((int) height); i++)
row_pointers[i] = png_pixels + i * row_bytes;
/* now we can go ahead and just read the whole image */
@@ -371,9 +379,9 @@
/* write data to PNM file */
pix_ptr = png_pixels;
- for (row = 0; row < height; row++)
+ for (row = 0; row < (int) height; row++)
{
- for (col = 0; col < width; col++)
+ for (col = 0; col < (int) width; col++)
{
for (i = 0; i < (channels - alpha_present); i++)
{
@@ -381,7 +389,7 @@
fputc ((int) *pix_ptr++ , pnm_file);
else
if (bit_depth == 16){
- dep_16 = (long) *pix_ptr++;
+ dep_16 = (long) *pix_ptr++;
fprintf (pnm_file, "%ld ", (dep_16 << 8) + ((long) *pix_ptr++));
}
else
@@ -401,9 +409,9 @@
fputc ((int) *pix_ptr++ , alpha_file);
else
if (bit_depth == 16){
- dep_16 = (long) *pix_ptr++;
+ dep_16 = (long) *pix_ptr++;
fprintf (alpha_file, "%ld ", (dep_16 << 8) + (long) *pix_ptr++);
- }
+ }
else
fprintf (alpha_file, "%ld ", (long) *pix_ptr++);
}
diff --git a/contrib/pngminus/png2pnm.sh b/contrib/pngminus/png2pnm.sh
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pngminus.bat b/contrib/pngminus/pngminus.bat
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pngminus.sh b/contrib/pngminus/pngminus.sh
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pnm2png.bat b/contrib/pngminus/pnm2png.bat
old mode 100644
new mode 100755
diff --git a/contrib/pngminus/pnm2png.c b/contrib/pngminus/pnm2png.c
index 4cdfad8..8fa64cd 100644
--- a/contrib/pngminus/pnm2png.c
+++ b/contrib/pngminus/pnm2png.c
@@ -3,6 +3,7 @@
* copyright (C) 1999 by Willem van Schaik <willem@schaik.com>
*
* version 1.0 - 1999.10.15 - First version.
+ * version 1.1 - 2015.07.29 - Fixed leaks (Glenn Randers-Pehrson)
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
@@ -18,6 +19,7 @@
#include <mem.h>
#include <fcntl.h>
#endif
+#include <zlib.h>
#ifndef BOOL
#define BOOL unsigned char
@@ -49,7 +51,8 @@
int main (int argc, char *argv[]);
void usage ();
-BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, BOOL alpha);
+BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace,
+ BOOL alpha);
void get_token(FILE *pnm_file, char *token);
png_uint_32 get_data (FILE *pnm_file, int depth);
png_uint_32 get_value (FILE *pnm_file, int depth);
@@ -175,7 +178,8 @@
fprintf (stderr, " or: ... | pnm2png [options]\n");
fprintf (stderr, "Options:\n");
fprintf (stderr, " -i[nterlace] write png-file with interlacing on\n");
- fprintf (stderr, " -a[lpha] <file>.pgm read PNG alpha channel as pgm-file\n");
+ fprintf (stderr,
+ " -a[lpha] <file>.pgm read PNG alpha channel as pgm-file\n");
fprintf (stderr, " -h | -? print this help-information\n");
}
@@ -183,29 +187,36 @@
* pnm2png
*/
-BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, BOOL alpha)
+BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace,
+ BOOL alpha)
{
png_struct *png_ptr = NULL;
png_info *info_ptr = NULL;
png_byte *png_pixels = NULL;
png_byte **row_pointers = NULL;
png_byte *pix_ptr = NULL;
- png_uint_32 row_bytes;
+ volatile png_uint_32 row_bytes;
char type_token[16];
char width_token[16];
char height_token[16];
char maxval_token[16];
- int color_type;
- png_uint_32 width, alpha_width;
- png_uint_32 height, alpha_height;
+ volatile int color_type=1;
+ unsigned long ul_width=0, ul_alpha_width=0;
+ unsigned long ul_height=0, ul_alpha_height=0;
+ unsigned long ul_maxval=0;
+ volatile png_uint_32 width=0, height=0;
+ volatile png_uint_32 alpha_width=0, alpha_height=0;
png_uint_32 maxval;
- int bit_depth = 0;
- int channels;
+ volatile int bit_depth = 0;
+ int channels=0;
int alpha_depth = 0;
- int alpha_present;
+ int alpha_present=0;
int row, col;
BOOL raw, alpha_raw = FALSE;
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+ BOOL packed_bitmap = FALSE;
+#endif
png_uint_32 tmp16;
int i;
@@ -218,20 +229,36 @@
}
else if ((type_token[1] == '1') || (type_token[1] == '4'))
{
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
raw = (type_token[1] == '4');
color_type = PNG_COLOR_TYPE_GRAY;
+ get_token(pnm_file, width_token);
+ sscanf (width_token, "%lu", &ul_width);
+ width = (png_uint_32) ul_width;
+ get_token(pnm_file, height_token);
+ sscanf (height_token, "%lu", &ul_height);
+ height = (png_uint_32) ul_height;
bit_depth = 1;
+ packed_bitmap = TRUE;
+#else
+ fprintf (stderr, "PNM2PNG built without PNG_WRITE_INVERT_SUPPORTED and \n");
+ fprintf (stderr, "PNG_WRITE_PACK_SUPPORTED can't read PBM (P1,P4) files\n");
+#endif
}
else if ((type_token[1] == '2') || (type_token[1] == '5'))
{
raw = (type_token[1] == '5');
color_type = PNG_COLOR_TYPE_GRAY;
get_token(pnm_file, width_token);
- sscanf (width_token, "%lu", &width);
+ sscanf (width_token, "%lu", &ul_width);
+ width = (png_uint_32) ul_width;
get_token(pnm_file, height_token);
- sscanf (height_token, "%lu", &height);
+ sscanf (height_token, "%lu", &ul_height);
+ height = (png_uint_32) ul_height;
get_token(pnm_file, maxval_token);
- sscanf (maxval_token, "%lu", &maxval);
+ sscanf (maxval_token, "%lu", &ul_maxval);
+ maxval = (png_uint_32) ul_maxval;
+
if (maxval <= 1)
bit_depth = 1;
else if (maxval <= 3)
@@ -248,11 +275,14 @@
raw = (type_token[1] == '6');
color_type = PNG_COLOR_TYPE_RGB;
get_token(pnm_file, width_token);
- sscanf (width_token, "%lu", &width);
+ sscanf (width_token, "%lu", &ul_width);
+ width = (png_uint_32) ul_width;
get_token(pnm_file, height_token);
- sscanf (height_token, "%lu", &height);
+ sscanf (height_token, "%lu", &ul_height);
+ height = (png_uint_32) ul_height;
get_token(pnm_file, maxval_token);
- sscanf (maxval_token, "%lu", &maxval);
+ sscanf (maxval_token, "%lu", &ul_maxval);
+ maxval = (png_uint_32) ul_maxval;
if (maxval <= 1)
bit_depth = 1;
else if (maxval <= 3)
@@ -287,15 +317,18 @@
{
alpha_raw = (type_token[1] == '5');
get_token(alpha_file, width_token);
- sscanf (width_token, "%lu", &alpha_width);
+ sscanf (width_token, "%lu", &ul_alpha_width);
+ alpha_width=(png_uint_32) ul_alpha_width;
if (alpha_width != width)
return FALSE;
get_token(alpha_file, height_token);
- sscanf (height_token, "%lu", &alpha_height);
+ sscanf (height_token, "%lu", &ul_alpha_height);
+ alpha_height = (png_uint_32) ul_alpha_height;
if (alpha_height != height)
return FALSE;
get_token(alpha_file, maxval_token);
- sscanf (maxval_token, "%lu", &maxval);
+ sscanf (maxval_token, "%lu", &ul_maxval);
+ maxval = (png_uint_32) ul_maxval;
if (maxval <= 1)
alpha_depth = 1;
else if (maxval <= 3)
@@ -324,76 +357,108 @@
channels = 3;
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
channels = 4;
+#if 0
else
- channels = 0; /* should not happen */
+ channels = 0; /* cannot happen */
+#endif
alpha_present = (channels - 1) % 2;
- /* row_bytes is the width x number of channels x (bit-depth / 8) */
- row_bytes = width * channels * ((bit_depth <= 8) ? 1 : 2);
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+ if (packed_bitmap)
+ /* row data is as many bytes as can fit width x channels x bit_depth */
+ row_bytes = (width * channels * bit_depth + 7) / 8;
+ else
+#endif
+ /* row_bytes is the width x number of channels x (bit-depth / 8) */
+ row_bytes = width * channels * ((bit_depth <= 8) ? 1 : 2);
- if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL)
+ if ((png_pixels = (png_byte *)
+ malloc (row_bytes * height * sizeof (png_byte))) == NULL)
return FALSE;
/* read data from PNM file */
pix_ptr = png_pixels;
- for (row = 0; row < height; row++)
+ for (row = 0; row < (int) height; row++)
{
- for (col = 0; col < width; col++)
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+ if (packed_bitmap) {
+ for (i = 0; i < (int) row_bytes; i++)
+ /* png supports this format natively so no conversion is needed */
+ *pix_ptr++ = get_data (pnm_file, 8);
+ } else
+#endif
{
- for (i = 0; i < (channels - alpha_present); i++)
+ for (col = 0; col < (int) width; col++)
{
- if (raw)
- *pix_ptr++ = get_data (pnm_file, bit_depth);
- else
- if (bit_depth <= 8)
- *pix_ptr++ = get_value (pnm_file, bit_depth);
+ for (i = 0; i < (channels - alpha_present); i++)
+ {
+ if (raw)
+ *pix_ptr++ = get_data (pnm_file, bit_depth);
else
- {
- tmp16 = get_value (pnm_file, bit_depth);
- *pix_ptr = (png_byte) ((tmp16 >> 8) & 0xFF);
- pix_ptr++;
- *pix_ptr = (png_byte) (tmp16 & 0xFF);
- pix_ptr++;
- }
- }
+ if (bit_depth <= 8)
+ *pix_ptr++ = get_value (pnm_file, bit_depth);
+ else
+ {
+ tmp16 = get_value (pnm_file, bit_depth);
+ *pix_ptr = (png_byte) ((tmp16 >> 8) & 0xFF);
+ pix_ptr++;
+ *pix_ptr = (png_byte) (tmp16 & 0xFF);
+ pix_ptr++;
+ }
+ }
- if (alpha) /* read alpha-channel from pgm file */
- {
- if (alpha_raw)
- *pix_ptr++ = get_data (alpha_file, alpha_depth);
- else
- if (alpha_depth <= 8)
- *pix_ptr++ = get_value (alpha_file, bit_depth);
+ if (alpha) /* read alpha-channel from pgm file */
+ {
+ if (alpha_raw)
+ *pix_ptr++ = get_data (alpha_file, alpha_depth);
else
- {
- tmp16 = get_value (alpha_file, bit_depth);
- *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF);
- *pix_ptr++ = (png_byte) (tmp16 & 0xFF);
- }
- } /* if alpha */
-
+ if (alpha_depth <= 8)
+ *pix_ptr++ = get_value (alpha_file, bit_depth);
+ else
+ {
+ tmp16 = get_value (alpha_file, bit_depth);
+ *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF);
+ *pix_ptr++ = (png_byte) (tmp16 & 0xFF);
+ }
+ } /* if alpha */
+ } /* if packed_bitmap */
} /* end for col */
} /* end for row */
/* prepare the standard PNG structures */
- png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_ptr = png_create_write_struct (png_get_libpng_ver(NULL), NULL, NULL,
+ NULL);
if (!png_ptr)
{
+ free (png_pixels);
+ png_pixels = NULL;
return FALSE;
}
info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr)
{
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+ free (png_pixels);
+ png_pixels = NULL;
return FALSE;
}
+#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+ if (packed_bitmap == TRUE)
+ {
+ png_set_packing (png_ptr);
+ png_set_invert_mono (png_ptr);
+ }
+#endif
+
/* setjmp() must be called in every function that calls a PNG-reading libpng function */
if (setjmp (png_jmpbuf(png_ptr)))
{
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+ free (png_pixels);
+ png_pixels = NULL;
return FALSE;
}
@@ -411,25 +476,28 @@
/* if needed we will allocate memory for an new array of row-pointers */
if (row_pointers == (unsigned char**) NULL)
{
- if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL)
+ if ((row_pointers = (png_byte **)
+ malloc (height * sizeof (png_bytep))) == NULL)
{
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+ free (png_pixels);
+ png_pixels = NULL;
return FALSE;
}
}
/* set the individual row_pointers to point at the correct offsets */
- for (i = 0; i < (height); i++)
+ for (i = 0; i < (int) height; i++)
row_pointers[i] = png_pixels + i * row_bytes;
/* write out the entire image data in one call */
png_write_image (png_ptr, row_pointers);
- /* write the additional chuncks to the PNG file (not really needed) */
+ /* write the additional chunks to the PNG file (not really needed) */
png_write_end (png_ptr, info_ptr);
/* clean up after the write, and free any memory allocated */
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
if (row_pointers != (unsigned char**) NULL)
free (row_pointers);
@@ -446,19 +514,32 @@
void get_token(FILE *pnm_file, char *token)
{
int i = 0;
+ int ret;
- /* remove white-space */
+ /* remove white-space and comment lines */
do
{
- token[i] = (unsigned char) fgetc (pnm_file);
+ ret = fgetc(pnm_file);
+ if (ret == '#') {
+ /* the rest of this line is a comment */
+ do
+ {
+ ret = fgetc(pnm_file);
+ }
+ while ((ret != '\n') && (ret != '\r') && (ret != EOF));
+ }
+ if (ret == EOF) break;
+ token[i] = (unsigned char) ret;
}
while ((token[i] == '\n') || (token[i] == '\r') || (token[i] == ' '));
/* read string */
do
{
+ ret = fgetc(pnm_file);
+ if (ret == EOF) break;
i++;
- token[i] = (unsigned char) fgetc (pnm_file);
+ token[i] = (unsigned char) ret;
}
while ((token[i] != '\n') && (token[i] != '\r') && (token[i] != ' '));
@@ -510,6 +591,7 @@
{
static png_uint_32 mask = 0;
png_byte token[16];
+ unsigned long ul_ret_value;
png_uint_32 ret_value;
int i = 0;
@@ -518,7 +600,8 @@
mask = (mask << 1) | 0x01;
get_token (pnm_file, (char *) token);
- sscanf ((const char *) token, "%lu", &ret_value);
+ sscanf ((const char *) token, "%lu", &ul_ret_value);
+ ret_value = (png_uint_32) ul_ret_value;
ret_value &= mask;
diff --git a/contrib/pngminus/pnm2png.sh b/contrib/pngminus/pnm2png.sh
old mode 100644
new mode 100755
diff --git a/contrib/pngsuite/README b/contrib/pngsuite/README
index 714d12c..a7fde8f 100644
--- a/contrib/pngsuite/README
+++ b/contrib/pngsuite/README
@@ -1,41 +1,61 @@
pngsuite
--------
-(c) Willem van Schaik, 1999
+Copyright (c) Willem van Schaik, 1999, 2011, 2012
+Two images (ftbbn0g01.png and ftbbn0g02.png) are by Glenn Randers-Pehrson, 2012
-Permission to use, copy, and distribute these images for any purpose and
-without fee is hereby granted.
+Permission to use, copy, modify, and distribute these images for any
+purpose and without fee is hereby granted.
-These 15 images are part of the much larger PngSuite test-set of
-images, available for developers of PNG supporting software. The
-complete set, available at http:/www.schaik.com/pngsuite/, contains
+The 15 "bas*.png" images are part of the much larger PngSuite test-set of
+images, available for developers of PNG supporting software. The
+complete set, available at http:/www.schaik.com/pngsuite/, contains
a variety of images to test interlacing, gamma settings, ancillary
chunks, etc.
+The "ft*.png" images are "free/libre" replacements for the transparent
+corresponding t*.png images in the PngSuite.
+
The images in this directory represent the basic PNG color-types:
grayscale (1-16 bit deep), full color (8 or 16 bit), paletted
(1-8 bit) and grayscale or color images with alpha channel. You
can use them to test the proper functioning of PNG software.
- filename depth type
+ filename depth type
------------ ------ --------------
- basn0g01.png 1-bit grayscale
- basn0g02.png 2-bit grayscale
- basn0g04.png 4-bit grayscale
- basn0g08.png 8-bit grayscale
- basn0g16.png 16-bit grayscale
- basn2c08.png 8-bit truecolor
- basn2c16.png 16-bit truecolor
- basn3p01.png 1-bit paletted
- basn3p02.png 2-bit paletted
- basn3p04.png 4-bit paletted
- basn3p08.png 8-bit paletted
- basn4a08.png 8-bit gray with alpha
- basn4a16.png 16-bit gray with alpha
- basn6a08.png 8-bit RGBA
- basn6a16.png 16-bit RGBA
+ basn0g01.png 1-bit grayscale
+ basn0g02.png 2-bit grayscale
+ basn0g04.png 4-bit grayscale
+ basn0g08.png 8-bit grayscale
+ basn0g16.png 16-bit grayscale
+ basn2c08.png 8-bit truecolor
+ basn2c16.png 16-bit truecolor
+ basn3p01.png 1-bit paletted
+ basn3p02.png 2-bit paletted
+ basn3p04.png 4-bit paletted
+ basn3p08.png 8-bit paletted
+ basn4a08.png 8-bit gray with alpha
+ basn4a16.png 16-bit gray with alpha
+ basn6a08.png 8-bit RGBA
+ basn6a16.png 16-bit RGBA
-Here is the correct result of typing "pngtest -m *.png" in
+ ftbbn0g01.png 1-bit grayscale, black bKGD
+ ftbbn0g02.png 2-bit grayscale, black bKGD
+ ftbbn0g04.png 4-bit grayscale, black bKGD
+ ftbbn2c16.png 16-bit truecolor, black bKGD
+ ftbbn3p08.png 8-bit paletted, black bKGD
+ ftbgn2c16.png 16-bit truecolor, gray bKGD
+ ftbgn3p08.png 8-bit paletted, gray bKGD
+ ftbrn2c08.png 8-bit truecolor, red bKGD
+ ftbwn0g16.png 16-bit gray, white bKGD
+ ftbwn3p08.png 8-bit paletted, white bKGD
+ ftbyn3p08.png 8-bit paletted, yellow bKGD
+ ftp0n0g08.png 8-bit grayscale, opaque
+ ftp0n2c08.png 8-bit truecolor, opaque
+ ftp0n3p08.png 8-bit paletted, opaque
+ ftp1n3p08.png 8-bit paletted, no bKGD
+
+Here is the correct result of typing "pngtest -m bas*.png" in
this directory:
Testing basn0g01.png: PASS (524 zero samples)
diff --git a/contrib/pngsuite/ftbbn0g01.png b/contrib/pngsuite/ftbbn0g01.png
new file mode 100644
index 0000000..ba746ff
--- /dev/null
+++ b/contrib/pngsuite/ftbbn0g01.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn0g02.png b/contrib/pngsuite/ftbbn0g02.png
new file mode 100644
index 0000000..3d83bd6
--- /dev/null
+++ b/contrib/pngsuite/ftbbn0g02.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn0g04.png b/contrib/pngsuite/ftbbn0g04.png
new file mode 100644
index 0000000..39a7050
--- /dev/null
+++ b/contrib/pngsuite/ftbbn0g04.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn2c16.png b/contrib/pngsuite/ftbbn2c16.png
new file mode 100644
index 0000000..dd3168e
--- /dev/null
+++ b/contrib/pngsuite/ftbbn2c16.png
Binary files differ
diff --git a/contrib/pngsuite/ftbbn3p08.png b/contrib/pngsuite/ftbbn3p08.png
new file mode 100644
index 0000000..0ede357
--- /dev/null
+++ b/contrib/pngsuite/ftbbn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbgn2c16.png b/contrib/pngsuite/ftbgn2c16.png
new file mode 100644
index 0000000..85cec39
--- /dev/null
+++ b/contrib/pngsuite/ftbgn2c16.png
Binary files differ
diff --git a/contrib/pngsuite/ftbgn3p08.png b/contrib/pngsuite/ftbgn3p08.png
new file mode 100644
index 0000000..8cf2e6f
--- /dev/null
+++ b/contrib/pngsuite/ftbgn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbrn2c08.png b/contrib/pngsuite/ftbrn2c08.png
new file mode 100644
index 0000000..5cca0d6
--- /dev/null
+++ b/contrib/pngsuite/ftbrn2c08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbwn0g16.png b/contrib/pngsuite/ftbwn0g16.png
new file mode 100644
index 0000000..99bdeed
--- /dev/null
+++ b/contrib/pngsuite/ftbwn0g16.png
Binary files differ
diff --git a/contrib/pngsuite/ftbwn3p08.png b/contrib/pngsuite/ftbwn3p08.png
new file mode 100644
index 0000000..eacab7a
--- /dev/null
+++ b/contrib/pngsuite/ftbwn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftbyn3p08.png b/contrib/pngsuite/ftbyn3p08.png
new file mode 100644
index 0000000..656db09
--- /dev/null
+++ b/contrib/pngsuite/ftbyn3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp0n0g08.png b/contrib/pngsuite/ftp0n0g08.png
new file mode 100644
index 0000000..333465f
--- /dev/null
+++ b/contrib/pngsuite/ftp0n0g08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp0n2c08.png b/contrib/pngsuite/ftp0n2c08.png
new file mode 100644
index 0000000..fc6e42c
--- /dev/null
+++ b/contrib/pngsuite/ftp0n2c08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp0n3p08.png b/contrib/pngsuite/ftp0n3p08.png
new file mode 100644
index 0000000..69a69e5
--- /dev/null
+++ b/contrib/pngsuite/ftp0n3p08.png
Binary files differ
diff --git a/contrib/pngsuite/ftp1n3p08.png b/contrib/pngsuite/ftp1n3p08.png
new file mode 100644
index 0000000..a6c9f35
--- /dev/null
+++ b/contrib/pngsuite/ftp1n3p08.png
Binary files differ
diff --git a/contrib/tools/README.txt b/contrib/tools/README.txt
new file mode 100644
index 0000000..5ddae02
--- /dev/null
+++ b/contrib/tools/README.txt
@@ -0,0 +1,26 @@
+This directory (contrib/tools) contains tools used by the authors of libpng.
+
+Code and data placed in this directory is not required to build libpng,
+however the code in this directory has been used to generate data or code in
+the body of the libpng source. The source code identifies where this has
+been done. Code in this directory may not compile on all operating systems
+that libpng supports.
+
+NO COPYRIGHT RIGHTS ARE CLAIMED TO ANY OF THE FILES IN THIS DIRECTORY.
+
+To the extent possible under law, the authors have waived all copyright and
+related or neighboring rights to this work. This work is published from:
+United States.
+
+The files may be used freely in any way.
+
+The source code and comments in this directory are the original work of the
+people named below. No other person or organization has made contributions to
+the work in this directory.
+
+ORIGINAL AUTHORS
+ The following people have contributed to the code in this directory. None
+ of the people below claim any rights with regard to the contents of this
+ directory.
+
+ John Bowler <jbowler@acm.org>
diff --git a/contrib/tools/checksum-icc.c b/contrib/tools/checksum-icc.c
new file mode 100644
index 0000000..581e708
--- /dev/null
+++ b/contrib/tools/checksum-icc.c
@@ -0,0 +1,102 @@
+/* checksum-icc.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Generate crc32 and adler32 checksums of the given input files, used to
+ * generate check-codes for use when matching ICC profiles within libpng.
+ */
+#include <stdio.h>
+
+#include <zlib.h>
+
+static int
+read_one_file(FILE *ip, const char *name)
+{
+ uLong length = 0;
+ uLong a32 = adler32(0, NULL, 0);
+ uLong c32 = crc32(0, NULL, 0);
+ Byte header[132];
+
+ for (;;)
+ {
+ int ch = getc(ip);
+ Byte b;
+
+ if (ch == EOF) break;
+
+ b = (Byte)ch;
+
+ if (length < sizeof header)
+ header[length] = b;
+
+ ++length;
+ a32 = adler32(a32, &b, 1);
+ c32 = crc32(c32, &b, 1);
+ }
+
+ if (ferror(ip))
+ return 0;
+
+ /* Success */
+ printf("PNG_ICC_CHECKSUM(0x%8.8lx, 0x%8.8lx,\n PNG_MD5("
+ "0x%2.2x%2.2x%2.2x%2.2x, 0x%2.2x%2.2x%2.2x%2.2x, 0x%2.2x%2.2x%2.2x%2.2x,"
+ " 0x%2.2x%2.2x%2.2x%2.2x), %d,\n"
+ " \"%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d\", %lu, \"%s\")\n",
+ (unsigned long)a32, (unsigned long)c32,
+ header[84], header[85], header[86], header[87],
+ header[88], header[89], header[90], header[91],
+ header[92], header[93], header[94], header[95],
+ header[96], header[97], header[98], header[99],
+# define u16(x) (header[x] * 256 + header[x+1])
+# define u32(x) (u16(x) * 65536 + u16(x+2))
+ u32(64), u16(24), u16(26), u16(28), u16(30), u16(32), u16(34),
+ (unsigned long)length, name);
+
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int err = 0;
+
+ printf("/* adler32, crc32, MD5[16], intent, date, length, file-name */\n");
+
+ if (argc > 1)
+ {
+ int i;
+
+ for (i=1; i<argc; ++i)
+ {
+ FILE *ip = fopen(argv[i], "rb");
+
+ if (ip == NULL || !read_one_file(ip, argv[i]))
+ {
+ err = 1;
+ perror(argv[i]);
+ fprintf(stderr, "%s: read error\n", argv[i]);
+ printf("/* ERROR: %s */\n", argv[i]);
+ }
+
+ (void)fclose(ip);
+ }
+ }
+
+ else
+ {
+ if (!read_one_file(stdin, "-"))
+ {
+ err = 1;
+ perror("stdin");
+ fprintf(stderr, "stdin: read error\n");
+ printf("/* ERROR: stdin */\n");
+ }
+ }
+
+ return err;
+}
diff --git a/contrib/tools/chkfmt b/contrib/tools/chkfmt
new file mode 100755
index 0000000..9da6475
--- /dev/null
+++ b/contrib/tools/chkfmt
@@ -0,0 +1,137 @@
+#!/bin/sh
+#
+# Check the format of the source files in the current directory - checks for a
+# line length of 80 characters max and no tab characters.
+#
+# Optionally arguments are files or directories to check.
+#
+# -v: output the long lines (makes fixing them easier)
+# -e: spawn an editor for each file that needs a change ($EDITOR must be
+# defined). When using -e the script MUST be run from an interactive
+# command line.
+verbose=
+edit=
+vers=
+test "$1" = "-v" && {
+ shift
+ verbose=yes
+}
+test "$1" = "-e" && {
+ shift
+ if test -n "$EDITOR"
+ then
+ edit=yes
+
+ # Copy the standard streams for the editor
+ exec 3>&0 4>&1 5>&2
+ else
+ echo "chkfmt -e: EDITOR must be defined" >&2
+ exit 1
+ fi
+}
+
+# Function to edit a single file - if the file isn't changed ask the user
+# whether or not to continue. This stuff only works if the script is run from
+# the command line (otherwise, don't specify -e or you will be sorry).
+doed(){
+ cp "$file" "$file".orig
+ "$EDITOR" "$file" 0>&3 1>&4 2>&5 3>&- 4>&- 5>&- || exit 1
+ if cmp -s "$file".orig "$file"
+ then
+ rm "$file".orig
+ echo -n "$file: file not changed, type anything to continue: " >&5
+ read ans 0>&3
+ test -n "$ans" || return 1
+ fi
+ return 0
+}
+
+# In beta versions the version string which appears in files can be a little
+# long and cause spuriously overlong lines. To avoid this subtitute the version
+# string with a 'standard' version a.b.cc before checking for long lines.
+if test -r png.h
+then
+ vers="`sed -n -e \
+ 's/^#define PNG_LIBPNG_VER_STRING .\([0-9]\.[0-9]\.[0-9][0-9a-z]*\).$/\1/p' \
+ png.h`"
+ echo "chkfmt: checking version $vers"
+fi
+if test -z "$vers"
+then
+ echo "chkfmt: png.h not found, ignoring version number" >&2
+fi
+
+test -n "$1" || set -- .
+find "$@" \( -type d \( -name '.git' -o -name '.libs' -o -name 'projects' \) \
+ -prune \) -o \( -type f \
+ ! -name '*.[oa]' ! -name '*.l[oa]' ! -name '*.png' ! -name '*.out' \
+ ! -name '*.jpg' ! -name '*.patch' ! -name '*.obj' ! -name '*.exe' \
+ ! -name '*.com' ! -name '*.tar.*' ! -name '*.zip' ! -name '*.ico' \
+ ! -name '*.res' ! -name '*.rc' ! -name '*.mms' ! -name '*.rej' \
+ ! -name '*.dsp' ! -name '*.orig' ! -name '*.dfn' ! -name '*.swp' \
+ ! -name '~*' ! -name '*.3' \
+ ! -name 'missing' ! -name 'mkinstalldirs' ! -name 'depcomp' \
+ ! -name 'aclocal.m4' ! -name 'install-sh' ! -name 'Makefile.in' \
+ ! -name 'ltmain.sh' ! -name 'config*' -print \) | {
+ st=0
+ while read file
+ do
+ case "$file" in
+ *.mak|*[Mm]akefile.*|*[Mm]akefile)
+ # Makefiles require tabs, dependency lines can be this long.
+ check_tabs=
+ line_length=100;;
+ *.awk)
+ # Includes literal tabs
+ check_tabs=
+ # The following is arbitrary
+ line_length=132;;
+ *contrib/*/*.[ch])
+ check_tabs=yes
+ line_length=96;;
+ *)
+ check_tabs=yes
+ line_length=80;;
+ esac
+
+ # Note that vers can only contain 0-9, . and a-z
+ if test -n "$vers"
+ then
+ sed -e "s/$vers/a.b.cc/g" "$file" >"$file".$$
+ else
+ cp "$file" "$file".$$
+ fi
+ splt="`fold -$line_length "$file".$$ | diff -c "$file".$$ -`"
+ rm "$file".$$
+
+ if test -n "$splt"
+ then
+ echo "$file: lines too long"
+ st=1
+ if test -n "$EDITOR" -a -n "$edit"
+ then
+ doed "$file" || exit 1
+ elif test -n "$verbose"
+ then
+ echo "$splt"
+ fi
+ fi
+ if test -n "$check_tabs"
+ then
+ tab="`tr -c -d '\t' <"$file"`"
+ if test -n "$tab"
+ then
+ echo "$file: file contains tab characters"
+ st=1
+ if test -n "$EDITOR" -a -n "$edit"
+ then
+ doed "$file" || exit 1
+ elif test -n "$verbose"
+ then
+ echo "$splt"
+ fi
+ fi
+ fi
+ done
+ exit $st
+}
diff --git a/contrib/tools/cvtcolor.c b/contrib/tools/cvtcolor.c
new file mode 100644
index 0000000..e6793c7
--- /dev/null
+++ b/contrib/tools/cvtcolor.c
@@ -0,0 +1,188 @@
+/*-
+ * convert.c
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2013.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Convert 8-bit sRGB or 16-bit linear values to another format.
+ */
+#define _ISOC99_SOURCE 1
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <fenv.h>
+
+#include "sRGB.h"
+
+static void
+usage(const char *prog)
+{
+ fprintf(stderr,
+ "%s: usage: %s [-linear|-sRGB] [-gray|-color] component{1,4}\n",
+ prog, prog);
+ exit(1);
+}
+
+unsigned long
+component(const char *prog, const char *arg, int issRGB)
+{
+ char *ep;
+ unsigned long c = strtoul(arg, &ep, 0);
+
+ if (ep <= arg || *ep || c > 65535 || (issRGB && c > 255))
+ {
+ fprintf(stderr, "%s: %s: invalid component value (%lu)\n", prog, arg, c);
+ usage(prog);
+ }
+
+ return c;
+}
+
+int
+main(int argc, const char **argv)
+{
+ const char *prog = *argv++;
+ int to_linear = 0, to_gray = 0, to_color = 0;
+ int channels = 0;
+ double c[4];
+
+ /* FE_TONEAREST is the IEEE754 round to nearest, preferring even, mode; i.e.
+ * everything rounds to the nearest value except that '.5' rounds to the
+ * nearest even value.
+ */
+ fesetround(FE_TONEAREST);
+
+ c[3] = c[2] = c[1] = c[0] = 0;
+
+ while (--argc > 0 && **argv == '-')
+ {
+ const char *arg = 1+*argv++;
+
+ if (strcmp(arg, "sRGB") == 0)
+ to_linear = 0;
+
+ else if (strcmp(arg, "linear") == 0)
+ to_linear = 1;
+
+ else if (strcmp(arg, "gray") == 0)
+ to_gray = 1, to_color = 0;
+
+ else if (strcmp(arg, "color") == 0)
+ to_gray = 0, to_color = 1;
+
+ else
+ usage(prog);
+ }
+
+ switch (argc)
+ {
+ default:
+ usage(prog);
+ break;
+
+ case 4:
+ c[3] = component(prog, argv[3], to_linear);
+ ++channels;
+ case 3:
+ c[2] = component(prog, argv[2], to_linear);
+ ++channels;
+ case 2:
+ c[1] = component(prog, argv[1], to_linear);
+ ++channels;
+ case 1:
+ c[0] = component(prog, argv[0], to_linear);
+ ++channels;
+ break;
+ }
+
+ if (to_linear)
+ {
+ int i;
+ int components = channels;
+
+ if ((components & 1) == 0)
+ --components;
+
+ for (i=0; i<components; ++i) c[i] = linear_from_sRGB(c[i] / 255);
+ if (components < channels)
+ c[components] = c[components] / 255;
+ }
+
+ else
+ {
+ int i;
+ for (i=0; i<4; ++i) c[i] /= 65535;
+
+ if ((channels & 1) == 0)
+ {
+ double alpha = c[channels-1];
+
+ if (alpha > 0)
+ for (i=0; i<channels-1; ++i) c[i] /= alpha;
+ else
+ for (i=0; i<channels-1; ++i) c[i] = 1;
+ }
+ }
+
+ if (to_gray)
+ {
+ if (channels < 3)
+ {
+ fprintf(stderr, "%s: too few channels (%d) for -gray\n",
+ prog, channels);
+ usage(prog);
+ }
+
+ c[0] = YfromRGB(c[0], c[1], c[2]);
+ channels -= 2;
+ }
+
+ if (to_color)
+ {
+ if (channels > 2)
+ {
+ fprintf(stderr, "%s: too many channels (%d) for -color\n",
+ prog, channels);
+ usage(prog);
+ }
+
+ c[3] = c[1]; /* alpha, if present */
+ c[2] = c[1] = c[0];
+ }
+
+ if (to_linear)
+ {
+ int i;
+ if ((channels & 1) == 0)
+ {
+ double alpha = c[channels-1];
+ for (i=0; i<channels-1; ++i) c[i] *= alpha;
+ }
+
+ for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 65535);
+ }
+
+ else /* to sRGB */
+ {
+ int i = (channels+1)&~1;
+ while (--i >= 0)
+ c[i] = sRGB_from_linear(c[i]);
+
+ for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 255);
+ }
+
+ {
+ int i;
+ for (i=0; i<channels; ++i) printf(" %g", c[i]);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/contrib/tools/genpng.c b/contrib/tools/genpng.c
new file mode 100644
index 0000000..ce43260
--- /dev/null
+++ b/contrib/tools/genpng.c
@@ -0,0 +1,867 @@
+/*- genpng
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2015.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Generate a PNG with an alpha channel, correctly.
+ *
+ * This is a test case generator; the resultant PNG files are only of interest
+ * to those of us who care about whether the edges of circles are green, red,
+ * or yellow.
+ *
+ * The program generates an RGB+Alpha PNG of a given size containing the given
+ * shapes on a transparent background:
+ *
+ * genpng width height { shape }
+ * shape ::= color width shape x1 y1 x2 y2
+ *
+ * 'color' is:
+ *
+ * black white red green yellow blue brown purple pink orange gray cyan
+ *
+ * The point is to have colors that are linguistically meaningful plus that old
+ * bugbear of the department store dress murders, Cyan, the only color we argue
+ * about.
+ *
+ * 'shape' is:
+ *
+ * circle: an ellipse
+ * square: a rectangle
+ * line: a straight line
+ *
+ * Each shape is followed by four numbers, these are two points in the output
+ * coordinate space (as real numbers) which describe the circle, square, or
+ * line. The shape is filled if it is preceded by 'filled' (not valid for
+ * 'line') or is drawn with a line, in which case the width of the line must
+ * precede the shape.
+ *
+ * The whole set of information can be repeated as many times as desired:
+ *
+ * shape ::= color width shape x1 y1 x2 y2
+ *
+ * color ::= black|white|red|green|yellow|blue
+ * color ::= brown|purple|pink|orange|gray|cyan
+ * width ::= filled
+ * width ::= <number>
+ * shape ::= circle|square|line
+ * x1 ::= <number>
+ * x2 ::= <number>
+ * y1 ::= <number>
+ * y2 ::= <number>
+ *
+ * The output PNG is generated by down-sampling a 4x supersampled image using
+ * a bi-cubic filter. The bi-cubic has a 2 (output) pixel width, so an 8x8
+ * array of super-sampled points contribute to each output pixel. The value of
+ * a super-sampled point is found using an unfiltered, aliased, infinite
+ * precision image: Each shape from the last to the first is checked to see if
+ * the point is in the drawn area and, if it is, the color of the point is the
+ * color of the shape and the alpha is 1, if not the previous shape is checked.
+ *
+ * This is an aliased algorithm because no filtering is done; a point is either
+ * inside or outside each shape and 'close' points do not contribute to the
+ * sample. The down-sampling is relied on to correct the error of not using
+ * a filter.
+ *
+ * The line end-caps are 'flat'; they go through the points. The square line
+ * joins are mitres; the outside of the lines are continued to the point of
+ * intersection.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+/* Normally use <png.h> here to get the installed libpng, but this is done to
+ * ensure the code picks up the local libpng implementation:
+ */
+#include "../../png.h"
+
+#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
+
+static const struct color
+{
+ const char *name;
+ double red;
+ double green;
+ double blue;
+} colors[] =
+/* color ::= black|white|red|green|yellow|blue
+ * color ::= brown|purple|pink|orange|gray|cyan
+ */
+{
+ { "black", 0, 0, 0 },
+ { "white", 1, 1, 1 },
+ { "red", 1, 0, 0 },
+ { "green", 0, 1, 0 },
+ { "yellow", 1, 1, 0 },
+ { "blue", 0, 0, 1 },
+ { "brown", .5, .125, 0 },
+ { "purple", 1, 0, 1 },
+ { "pink", 1, .5, .5 },
+ { "orange", 1, .5, 0 },
+ { "gray", 0, .5, .5 },
+ { "cyan", 0, 1, 1 }
+};
+#define color_count ((sizeof colors)/(sizeof colors[0]))
+
+static const struct color *
+color_of(const char *arg)
+{
+ int icolor = color_count;
+
+ while (--icolor >= 0)
+ {
+ if (strcmp(colors[icolor].name, arg) == 0)
+ return colors+icolor;
+ }
+
+ fprintf(stderr, "genpng: invalid color %s\n", arg);
+ exit(1);
+}
+
+static double
+width_of(const char *arg)
+{
+ if (strcmp(arg, "filled") == 0)
+ return 0;
+
+ else
+ {
+ char *ep = NULL;
+ double w = strtod(arg, &ep);
+
+ if (ep != NULL && *ep == 0 && w > 0)
+ return w;
+ }
+
+ fprintf(stderr, "genpng: invalid line width %s\n", arg);
+ exit(1);
+}
+
+static double
+coordinate_of(const char *arg)
+{
+ char *ep = NULL;
+ double w = strtod(arg, &ep);
+
+ if (ep != NULL && *ep == 0)
+ return w;
+
+ fprintf(stderr, "genpng: invalid coordinate value %s\n", arg);
+ exit(1);
+}
+
+struct arg; /* forward declaration */
+
+typedef int (*shape_fn_ptr)(const struct arg *arg, double x, double y);
+ /* A function to determine if (x,y) is inside the shape.
+ *
+ * There are two implementations:
+ *
+ * inside_fn: returns true if the point is inside
+ * check_fn: returns;
+ * -1: the point is outside the shape by more than the filter width (2)
+ * 0: the point may be inside the shape
+ * +1: the point is inside the shape by more than the filter width
+ */
+#define OUTSIDE (-1)
+#define INSIDE (1)
+
+struct arg
+{
+ const struct color *color;
+ shape_fn_ptr inside_fn;
+ shape_fn_ptr check_fn;
+ double width; /* line width, 0 for 'filled' */
+ double x1, y1, x2, y2;
+};
+
+/* IMPLEMENTATION NOTE:
+ *
+ * We want the contribution of each shape to the sample corresponding to each
+ * pixel. This could be obtained by super sampling the image to infinite
+ * dimensions, finding each point within the shape and assigning that a value
+ * '1' while leaving every point outside the shape with value '0' then
+ * downsampling to the image size with sinc; computationally very expensive.
+ *
+ * Approximations are as follows:
+ *
+ * 1) If the pixel coordinate is within the shape assume the sample has the
+ * shape color and is opaque, else assume there is no contribution from
+ * the shape.
+ *
+ * This is the equivalent of aliased rendering or resampling an image with
+ * a block filter. The maximum error in the calculated alpha (which will
+ * always be 0 or 1) is 0.5.
+ *
+ * 2) If the shape is within a square of size 1x1 centered on the pixel assume
+ * that the shape obscures an amount of the pixel equal to its area within
+ * that square.
+ *
+ * This is the equivalent of 'pixel coverage' alpha calculation or resampling
+ * an image with a bi-linear filter. The maximum error is over 0.2, but the
+ * results are often acceptable.
+ *
+ * This can be approximated by applying (1) to a super-sampled image then
+ * downsampling with a bi-linear filter. The error in the super-sampled
+ * image is 0.5 per sample, but the resampling reduces this.
+ *
+ * 3) Use a better filter with a super-sampled image; in the limit this is the
+ * sinc() approach.
+ *
+ * 4) Do the geometric calculation; a bivariate definite integral across the
+ * shape, unfortunately this means evaluating Si(x), the integral of sinc(x),
+ * which is still a lot of math.
+ *
+ * This code uses approach (3) with a bi-cubic filter and 8x super-sampling
+ * and method (1) for the super-samples. This means that the sample is either
+ * 0 or 1, depending on whether the sub-pixel is within or outside the shape.
+ * The bi-cubic weights are also fixed and the 16 required weights are
+ * pre-computed here (note that the 'scale' setting will need to be changed if
+ * 'super' is increased).
+ *
+ * The code also calculates a sum to the edge of the filter. This is not
+ * currently used by could be used to optimize the calculation.
+ */
+#if 0 /* bc code */
+scale=10
+super=8
+define bicubic(x) {
+ if (x <= 1) return (1.5*x - 2.5)*x*x + 1;
+ if (x < 2) return (((2.5 - 0.5*x)*x - 4)*x + 2);
+ return 0;
+}
+define sum(x) {
+ auto s;
+ s = 0;
+ while (x < 2*super) {
+ s = s + bicubic(x/super);
+ x = x + 1;
+ }
+ return s;
+}
+define results(x) {
+ auto b, s;
+ b = bicubic(x/super);
+ s = sum(x);
+
+ print " /*", x, "*/ { ", b, ", ", s, " }";
+ return 1;
+}
+x=0
+while (x<2*super) {
+ x = x + results(x)
+ if (x < 2*super) print ","
+ print "\n"
+}
+quit
+#endif
+
+#define BICUBIC1(x) /* |x| <= 1 */ ((1.5*(x)* - 2.5)*(x)*(x) + 1)
+#define BICUBIC2(x) /* 1 < |x| < 2 */ (((2.5 - 0.5*(x))*(x) - 4)*(x) + 2)
+#define FILTER_WEIGHT 9 /* Twice the first sum below */
+#define FILTER_WIDTH 2 /* Actually half the width; -2..+2 */
+#define FILTER_STEPS 8 /* steps per filter unit */
+static const double
+bicubic[16][2] =
+{
+ /* These numbers are exact; the weight for the filter is 1/9, but this
+ * would make the numbers inexact, so it is not included here.
+ */
+ /* bicubic sum */
+ /* 0*/ { 1.0000000000, 4.5000000000 },
+ /* 1*/ { .9638671875, 3.5000000000 },
+ /* 2*/ { .8671875000, 2.5361328125 },
+ /* 3*/ { .7275390625, 1.6689453125 },
+ /* 4*/ { .5625000000, .9414062500 },
+ /* 5*/ { .3896484375, .3789062500 },
+ /* 6*/ { .2265625000, -.0107421875 },
+ /* 7*/ { .0908203125, -.2373046875 },
+ /* 8*/ { 0, -.3281250000 },
+ /* 9*/ { -.0478515625, -.3281250000 },
+ /*10*/ { -.0703125000, -.2802734375 },
+ /*11*/ { -.0732421875, -.2099609375 },
+ /*12*/ { -.0625000000, -.1367187500 },
+ /*13*/ { -.0439453125, -.0742187500 },
+ /*14*/ { -.0234375000, -.0302734375 },
+ /*15*/ { -.0068359375, -.0068359375 }
+};
+
+static double
+alpha_calc(const struct arg *arg, double x, double y)
+{
+ /* For [x-2..x+2],[y-2,y+2] calculate the weighted bicubic given a function
+ * which tells us whether a point is inside or outside the shape. First
+ * check if we need to do this at all:
+ */
+ switch (arg->check_fn(arg, x, y))
+ {
+ case OUTSIDE:
+ return 0; /* all samples outside the shape */
+
+ case INSIDE:
+ return 1; /* all samples inside the shape */
+
+ default:
+ {
+ int dy;
+ double alpha = 0;
+
+# define FILTER_D (FILTER_WIDTH*FILTER_STEPS-1)
+ for (dy=-FILTER_D; dy<=FILTER_D; ++dy)
+ {
+ double wy = bicubic[abs(dy)][0];
+
+ if (wy != 0)
+ {
+ double alphay = 0;
+ int dx;
+
+ for (dx=-FILTER_D; dx<=FILTER_D; ++dx)
+ {
+ double wx = bicubic[abs(dx)][0];
+
+ if (wx != 0 && arg->inside_fn(arg, x+dx/16, y+dy/16))
+ alphay += wx;
+ }
+
+ alpha += wy * alphay;
+ }
+ }
+
+ /* This needs to be weighted for each dimension: */
+ return alpha / (FILTER_WEIGHT*FILTER_WEIGHT);
+ }
+ }
+}
+
+/* These are the shape functions. */
+/* "square",
+ * { inside_square_filled, check_square_filled },
+ * { inside_square, check_square }
+ */
+static int
+square_check(double x, double y, double x1, double y1, double x2, double y2)
+ /* Is x,y inside the square (x1,y1)..(x2,y2)? */
+{
+ /* Do a modified Cohen-Sutherland on one point, bit patterns that indicate
+ * 'outside' are:
+ *
+ * x<x1 | x<y1 | x<x2 | x<y2
+ * 0 x 0 x To the right
+ * 1 x 1 x To the left
+ * x 0 x 0 Below
+ * x 1 x 1 Above
+ *
+ * So 'inside' is (x<x1) != (x<x2) && (y<y1) != (y<y2);
+ */
+ return ((x<x1) ^ (x<x2)) & ((y<y1) ^ (y<y2));
+}
+
+static int
+inside_square_filled(const struct arg *arg, double x, double y)
+{
+ return square_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
+}
+
+static int
+square_check_line(const struct arg *arg, double x, double y, double w)
+ /* Check for a point being inside the boundaries implied by the given arg
+ * and assuming a width 2*w each side of the boundaries. This returns the
+ * 'check' INSIDE/OUTSIDE/0 result but note the semantics:
+ *
+ * +--------------+
+ * | | OUTSIDE
+ * | INSIDE |
+ * | |
+ * +--------------+
+ *
+ * And '0' means within the line boundaries.
+ */
+{
+ double cx = (arg->x1+arg->x2)/2;
+ double wx = fabs(arg->x1-arg->x2)/2;
+ double cy = (arg->y1+arg->y2)/2;
+ double wy = fabs(arg->y1-arg->y2)/2;
+
+ if (square_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
+ {
+ /* Inside, but maybe too far; check for the redundant case where
+ * the lines overlap:
+ */
+ wx -= w;
+ wy -= w;
+ if (wx > 0 && wy > 0 && square_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
+ return INSIDE; /* between (inside) the boundary lines. */
+
+ return 0; /* inside the lines themselves. */
+ }
+
+ return OUTSIDE; /* outside the boundary lines. */
+}
+
+static int
+check_square_filled(const struct arg *arg, double x, double y)
+{
+ /* The filter extends +/-FILTER_WIDTH each side of each output point, so
+ * the check has to expand and contract the square by that amount; '0'
+ * means close enough to the edge of the square that the bicubic filter has
+ * to be run, OUTSIDE means alpha==0, INSIDE means alpha==1.
+ */
+ return square_check_line(arg, x, y, FILTER_WIDTH);
+}
+
+static int
+inside_square(const struct arg *arg, double x, double y)
+{
+ /* Return true if within the drawn lines, else false, no need to distinguish
+ * INSIDE vs OUTSIDE here:
+ */
+ return square_check_line(arg, x, y, arg->width/2) == 0;
+}
+
+static int
+check_square(const struct arg *arg, double x, double y)
+{
+ /* So for this function a result of 'INSIDE' means inside the actual lines.
+ */
+ double w = arg->width/2;
+
+ if (square_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
+ {
+ /* Somewhere close to the boundary lines. If far enough inside one of
+ * them then we can return INSIDE:
+ */
+ w -= FILTER_WIDTH;
+
+ if (w > 0 && square_check_line(arg, x, y, w) == 0)
+ return INSIDE;
+
+ /* Point is somewhere in the filter region: */
+ return 0;
+ }
+
+ else /* Inside or outside the square by more than w+FILTER_WIDTH. */
+ return OUTSIDE;
+}
+
+/* "circle",
+ * { inside_circle_filled, check_circle_filled },
+ * { inside_circle, check_circle }
+ *
+ * The functions here are analoguous to the square ones; however, they check
+ * the corresponding ellipse as opposed to the rectangle.
+ */
+static int
+circle_check(double x, double y, double x1, double y1, double x2, double y2)
+{
+ if (square_check(x, y, x1, y1, x2, y2))
+ {
+ /* Inside the square, so maybe inside the circle too: */
+ const double cx = (x1 + x2)/2;
+ const double cy = (y1 + y2)/2;
+ const double dx = x1 - x2;
+ const double dy = y1 - y2;
+
+ x = (x - cx)/dx;
+ y = (y - cy)/dy;
+
+ /* It is outside if the distance from the center is more than half the
+ * diameter:
+ */
+ return x*x+y*y < .25;
+ }
+
+ return 0; /* outside */
+}
+
+static int
+inside_circle_filled(const struct arg *arg, double x, double y)
+{
+ return circle_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2);
+}
+
+static int
+circle_check_line(const struct arg *arg, double x, double y, double w)
+ /* Check for a point being inside the boundaries implied by the given arg
+ * and assuming a width 2*w each side of the boundaries. This function has
+ * the same semantic as square_check_line but tests the circle.
+ */
+{
+ double cx = (arg->x1+arg->x2)/2;
+ double wx = fabs(arg->x1-arg->x2)/2;
+ double cy = (arg->y1+arg->y2)/2;
+ double wy = fabs(arg->y1-arg->y2)/2;
+
+ if (circle_check(x, y, cx-wx-w, cy-wy-w, cx+wx+w, cy+wy+w))
+ {
+ /* Inside, but maybe too far; check for the redundant case where
+ * the lines overlap:
+ */
+ wx -= w;
+ wy -= w;
+ if (wx > 0 && wy > 0 && circle_check(x, y, cx-wx, cy-wy, cx+wx, cy+wy))
+ return INSIDE; /* between (inside) the boundary lines. */
+
+ return 0; /* inside the lines themselves. */
+ }
+
+ return OUTSIDE; /* outside the boundary lines. */
+}
+
+static int
+check_circle_filled(const struct arg *arg, double x, double y)
+{
+ return circle_check_line(arg, x, y, FILTER_WIDTH);
+}
+
+static int
+inside_circle(const struct arg *arg, double x, double y)
+{
+ return circle_check_line(arg, x, y, arg->width/2) == 0;
+}
+
+static int
+check_circle(const struct arg *arg, double x, double y)
+{
+ /* Exactly as the 'square' code. */
+ double w = arg->width/2;
+
+ if (circle_check_line(arg, x, y, w+FILTER_WIDTH) == 0)
+ {
+ w -= FILTER_WIDTH;
+
+ if (w > 0 && circle_check_line(arg, x, y, w) == 0)
+ return INSIDE;
+
+ /* Point is somewhere in the filter region: */
+ return 0;
+ }
+
+ else /* Inside or outside the square by more than w+FILTER_WIDTH. */
+ return OUTSIDE;
+}
+
+/* "line",
+ * { NULL, NULL }, There is no 'filled' line.
+ * { inside_line, check_line }
+ */
+static int
+line_check(double x, double y, double x1, double y1, double x2, double y2,
+ double w, double expand)
+{
+ /* Shift all the points to (arg->x1, arg->y1) */
+ double lx = x2 - x1;
+ double ly = y2 - y1;
+ double len2 = lx*lx + ly*ly;
+ double cross, dot;
+
+ x -= x1;
+ y -= y1;
+
+ /* The dot product is the distance down the line, the cross product is
+ * the distance away from the line:
+ *
+ * distance = |cross| / sqrt(len2)
+ */
+ cross = x * ly - y * lx;
+
+ /* If 'distance' is more than w the point is definitely outside the line:
+ *
+ * distance >= w
+ * |cross| >= w * sqrt(len2)
+ * cross^2 >= w^2 * len2:
+ */
+ if (cross*cross >= (w+expand)*(w+expand)*len2)
+ return 0; /* outside */
+
+ /* Now find the distance *along* the line; this comes from the dot product
+ * lx.x+ly.y. The actual distance (in pixels) is:
+ *
+ * distance = dot / sqrt(len2)
+ */
+ dot = lx * x + ly * y;
+
+ /* The test for 'outside' is:
+ *
+ * distance < 0 || distance > sqrt(len2)
+ * -> dot / sqrt(len2) > sqrt(len2)
+ * -> dot > len2
+ *
+ * But 'expand' is used for the filter width and needs to be handled too:
+ */
+ return dot > -expand && dot < len2+expand;
+}
+
+static int
+inside_line(const struct arg *arg, double x, double y)
+{
+ return line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2, 0);
+}
+
+static int
+check_line(const struct arg *arg, double x, double y)
+{
+ /* The end caps of the line must be checked too; it's not enough just to
+ * widen the line by FILTER_WIDTH; 'expand' exists for this purpose:
+ */
+ if (line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
+ FILTER_WIDTH))
+ {
+ /* Inside the line+filter; far enough inside that the filter isn't
+ * required?
+ */
+ if (arg->width > 2*FILTER_WIDTH &&
+ line_check(x, y, arg->x1, arg->y1, arg->x2, arg->y2, arg->width/2,
+ -FILTER_WIDTH))
+ return INSIDE;
+
+ return 0;
+ }
+
+ return OUTSIDE;
+}
+
+static const struct
+{
+ const char *name;
+ shape_fn_ptr function[2/*fill,line*/][2];
+# define FN_INSIDE 0
+# define FN_CHECK 1
+} shape_defs[] =
+{
+ { "square",
+ { { inside_square_filled, check_square_filled },
+ { inside_square, check_square } }
+ },
+ { "circle",
+ { { inside_circle_filled, check_circle_filled },
+ { inside_circle, check_circle } }
+ },
+ { "line",
+ { { NULL, NULL },
+ { inside_line, check_line } }
+ }
+};
+
+#define shape_count ((sizeof shape_defs)/(sizeof shape_defs[0]))
+
+static shape_fn_ptr
+shape_of(const char *arg, double width, int f)
+{
+ unsigned int i;
+
+ for (i=0; i<shape_count; ++i) if (strcmp(shape_defs[i].name, arg) == 0)
+ {
+ shape_fn_ptr fn = shape_defs[i].function[width != 0][f];
+
+ if (fn != NULL)
+ return fn;
+
+ fprintf(stderr, "genpng: %s %s not supported\n",
+ width == 0 ? "filled" : "unfilled", arg);
+ exit(1);
+ }
+
+ fprintf(stderr, "genpng: %s: not a valid shape name\n", arg);
+ exit(1);
+}
+
+static void
+parse_arg(struct arg *arg, const char **argv/*7 arguments*/)
+{
+ /* shape ::= color width shape x1 y1 x2 y2 */
+ arg->color = color_of(argv[0]);
+ arg->width = width_of(argv[1]);
+ arg->inside_fn = shape_of(argv[2], arg->width, FN_INSIDE);
+ arg->check_fn = shape_of(argv[2], arg->width, FN_CHECK);
+ arg->x1 = coordinate_of(argv[3]);
+ arg->y1 = coordinate_of(argv[4]);
+ arg->x2 = coordinate_of(argv[5]);
+ arg->y2 = coordinate_of(argv[6]);
+}
+
+static png_uint_32
+read_wh(const char *name, const char *str)
+ /* read a PNG width or height */
+{
+ char *ep = NULL;
+ unsigned long ul = strtoul(str, &ep, 10);
+
+ if (ep != NULL && *ep == 0 && ul > 0 && ul <= 0x7fffffff)
+ return (png_uint_32)/*SAFE*/ul;
+
+ fprintf(stderr, "genpng: %s: invalid number %s\n", name, str);
+ exit(1);
+}
+
+static void
+pixel(png_uint_16p p, struct arg *args, int nargs, double x, double y)
+{
+ /* Fill in the pixel by checking each shape (args[nargs]) for effects on
+ * the corresponding sample:
+ */
+ double r=0, g=0, b=0, a=0;
+
+ while (--nargs >= 0 && a != 1)
+ {
+ /* NOTE: alpha_calc can return a value outside the range 0..1 with the
+ * bicubic filter.
+ */
+ const double alpha = alpha_calc(args+nargs, x, y) * (1-a);
+
+ r += alpha * args[nargs].color->red;
+ g += alpha * args[nargs].color->green;
+ b += alpha * args[nargs].color->blue;
+ a += alpha;
+ }
+
+ /* 'a' may be negative or greater than 1; if it is, negative clamp the
+ * pixel to 0 if >1 clamp r/g/b:
+ */
+ if (a > 0)
+ {
+ if (a > 1)
+ {
+ if (r > 1) r = 1;
+ if (g > 1) g = 1;
+ if (b > 1) b = 1;
+ a = 1;
+ }
+
+ /* And fill in the pixel: */
+ p[0] = (png_uint_16)/*SAFE*/round(r * 65535);
+ p[1] = (png_uint_16)/*SAFE*/round(g * 65535);
+ p[2] = (png_uint_16)/*SAFE*/round(b * 65535);
+ p[3] = (png_uint_16)/*SAFE*/round(a * 65535);
+ }
+
+ else
+ p[3] = p[2] = p[1] = p[0] = 0;
+}
+
+int
+main(int argc, const char **argv)
+{
+ int convert_to_8bit = 0;
+
+ /* There is one option: --8bit: */
+ if (argc > 1 && strcmp(argv[1], "--8bit") == 0)
+ --argc, ++argv, convert_to_8bit = 1;
+
+ if (argc >= 3)
+ {
+ png_uint_16p buffer;
+ int nshapes;
+ png_image image;
+# define max_shapes 256
+ struct arg arg_list[max_shapes];
+
+ /* The libpng Simplified API write code requires a fully initialized
+ * structure.
+ */
+ memset(&image, 0, sizeof image);
+ image.version = PNG_IMAGE_VERSION;
+ image.opaque = NULL;
+ image.width = read_wh("width", argv[1]);
+ image.height = read_wh("height", argv[2]);
+ image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+ image.flags = 0;
+ image.colormap_entries = 0;
+
+ /* Check the remainder of the arguments */
+ for (nshapes=0; 3+7*(nshapes+1) <= argc && nshapes < max_shapes;
+ ++nshapes)
+ parse_arg(arg_list+nshapes, argv+3+7*nshapes);
+
+ if (3+7*nshapes != argc)
+ {
+ fprintf(stderr, "genpng: %s: too many arguments\n", argv[3+7*nshapes]);
+ return 1;
+ }
+
+ /* Create the buffer: */
+ buffer = malloc(PNG_IMAGE_SIZE(image));
+
+ if (buffer != NULL)
+ {
+ png_uint_32 y;
+
+ /* Write each row... */
+ for (y=0; y<image.height; ++y)
+ {
+ png_uint_32 x;
+
+ /* Each pixel in each row: */
+ for (x=0; x<image.width; ++x)
+ pixel(buffer + 4*(x + y*image.width), arg_list, nshapes, x, y);
+ }
+
+ /* Write the result (to stdout) */
+ if (png_image_write_to_stdio(&image, stdout, convert_to_8bit,
+ buffer, 0/*row_stride*/, NULL/*colormap*/))
+ {
+ free(buffer);
+ return 0; /* success */
+ }
+
+ else
+ fprintf(stderr, "genpng: write stdout: %s\n", image.message);
+
+ free(buffer);
+ }
+
+ else
+ fprintf(stderr, "genpng: out of memory: %lu bytes\n",
+ (unsigned long)PNG_IMAGE_SIZE(image));
+ }
+
+ else
+ {
+ /* Wrong number of arguments */
+ fprintf(stderr, "genpng: usage: genpng [--8bit] width height {shape}\n"
+ " Generate a transparent PNG in RGBA (truecolor+alpha) format\n"
+ " containing the given shape or shapes. Shapes are defined:\n"
+ "\n"
+ " shape ::= color width shape x1 y1 x2 y2\n"
+ " color ::= black|white|red|green|yellow|blue\n"
+ " color ::= brown|purple|pink|orange|gray|cyan\n"
+ " width ::= filled|<number>\n"
+ " shape ::= circle|square|line\n"
+ " x1,x2 ::= <number>\n"
+ " y1,y2 ::= <number>\n"
+ "\n"
+ " Numbers are floating point numbers describing points relative to\n"
+ " the top left of the output PNG as pixel coordinates. The 'width'\n"
+ " parameter is either the width of the line (in output pixels) used\n"
+ " to draw the shape or 'filled' to indicate that the shape should\n"
+ " be filled with the color.\n"
+ "\n"
+ " Colors are interpreted loosely to give access to the eight full\n"
+ " intensity RGB values:\n"
+ "\n"
+ " black, red, green, blue, yellow, cyan, purple, white,\n"
+ "\n"
+ " Cyan is full intensity blue+green; RGB(0,1,1), plus the following\n"
+ " lower intensity values:\n"
+ "\n"
+ " brown: red+orange: RGB(0.5, 0.125, 0) (dark red+orange)\n"
+ " pink: red+white: RGB(1.0, 0.5, 0.5)\n"
+ " orange: red+yellow: RGB(1.0, 0.5, 0)\n"
+ " gray: black+white: RGB(0.5, 0.5, 0.5)\n"
+ "\n"
+ " The RGB values are selected to make detection of aliasing errors\n"
+ " easy. The names are selected to make the description of errors\n"
+ " easy.\n"
+ "\n"
+ " The PNG is written to stdout, if --8bit is given a 32bpp RGBA sRGB\n"
+ " file is produced, otherwise a 64bpp RGBA linear encoded file is\n"
+ " written.\n");
+ }
+
+ return 1;
+}
+#endif /* SIMPLIFIED_WRITE && STDIO */
diff --git a/contrib/tools/intgamma.sh b/contrib/tools/intgamma.sh
new file mode 100755
index 0000000..41c5d6d
--- /dev/null
+++ b/contrib/tools/intgamma.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+#
+# intgamma.sh
+#
+# Last changed in libpng 1.6.0 [February 14, 2013]
+#
+# COPYRIGHT: Written by John Cunningham Bowler, 2013.
+# To the extent possible under law, the author has waived all copyright and
+# related or neighboring rights to this work. This work is published from:
+# United States.
+#
+# Shell script to generate png.c 8-bit and 16-bit log tables (see the code in
+# png.c for details).
+#
+# This script uses the "bc" arbitrary precision calculator to calculate 32-bit
+# fixed point values of logarithms appropriate to finding the log of an 8-bit
+# (0..255) value and a similar table for the exponent calculation.
+#
+# "bc" must be on the path when the script is executed, and the math library
+# (-lm) must be available
+#
+# function to print out a list of numbers as integers; the function truncates
+# the integers which must be one-per-line
+function print(){
+ awk 'BEGIN{
+ str = ""
+ }
+ {
+ sub("\\.[0-9]*$", "")
+ if ($0 == "")
+ $0 = "0"
+
+ if (str == "")
+ t = " " $0 "U"
+ else
+ t = str ", " $0 "U"
+
+ if (length(t) >= 80) {
+ print str ","
+ str = " " $0 "U"
+ } else
+ str = t
+ }
+ END{
+ print str
+ }'
+}
+#
+# The logarithm table.
+cat <<END
+/* 8-bit log table: png_8bit_l2[128]
+ * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
+ * 255, so it's the base 2 logarithm of a normalized 8-bit floating point
+ * mantissa. The numbers are 32-bit fractions.
+ */
+static const png_uint_32
+png_8bit_l2[128] =
+{
+END
+#
+bc -lqws <<END | print
+f=65536*65536/l(2)
+for (i=128;i<256;++i) { .5 - l(i/255)*f; }
+END
+echo '};'
+echo
+#
+# The exponent table.
+cat <<END
+/* The 'exp()' case must invert the above, taking a 20-bit fixed point
+ * logarithmic value and returning a 16 or 8-bit number as appropriate. In
+ * each case only the low 16 bits are relevant - the fraction - since the
+ * integer bits (the top 4) simply determine a shift.
+ *
+ * The worst case is the 16-bit distinction between 65535 and 65534; this
+ * requires perhaps spurious accuracy in the decoding of the logarithm to
+ * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance
+ * of getting this accuracy in practice.
+ *
+ * To deal with this the following exp() function works out the exponent of the
+ * frational part of the logarithm by using an accurate 32-bit value from the
+ * top four fractional bits then multiplying in the remaining bits.
+ */
+static const png_uint_32
+png_32bit_exp[16] =
+{
+END
+#
+bc -lqws <<END | print
+f=l(2)/16
+for (i=0;i<16;++i) {
+ x = .5 + e(-i*f)*2^32;
+ if (x >= 2^32) x = 2^32-1;
+ x;
+}
+END
+echo '};'
+echo
+#
+# And the table of adjustment values.
+cat <<END
+/* Adjustment table; provided to explain the numbers in the code below. */
+#if 0
+END
+bc -lqws <<END | awk '{ printf "%5d %s\n", 12-NR, $0 }'
+for (i=11;i>=0;--i){
+ (1 - e(-(2^i)/65536*l(2))) * 2^(32-i)
+}
+END
+echo '#endif'
diff --git a/contrib/tools/makesRGB.c b/contrib/tools/makesRGB.c
new file mode 100644
index 0000000..e66c9f1
--- /dev/null
+++ b/contrib/tools/makesRGB.c
@@ -0,0 +1,430 @@
+/* makesRGB.c -- build sRGB-to-linear and linear-to-sRGB conversion tables
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2013.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Make a table to convert 8-bit sRGB encoding values into the closest 16-bit
+ * linear value.
+ *
+ * Make two tables to take a linear value scaled to 255*65535 and return an
+ * approximation to the 8-bit sRGB encoded value. Calculate the error in these
+ * tables and display it.
+ */
+#define _C99_SOURCE 1
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+/* pngpriv.h includes the definition of 'PNG_sRGB_FROM_LINEAR' which is required
+ * to verify the actual code.
+ */
+#include "../../pngpriv.h"
+
+#include "sRGB.h"
+
+/* The tables are declared 'const' in pngpriv.h, so this redefines the tables to
+ * be used.
+ */
+#define png_sRGB_table sRGB_table
+#define png_sRGB_base sRGB_base
+#define png_sRGB_delta sRGB_delta
+
+static png_uint_16 png_sRGB_table[256];
+static png_uint_16 png_sRGB_base[512];
+static png_byte png_sRGB_delta[512];
+
+static const unsigned int max_input = 255*65535;
+
+double
+fsRGB(double l)
+{
+ return sRGB_from_linear(l/max_input);
+}
+
+double
+sRGB(unsigned int i)
+{
+ return fsRGB(i);
+}
+
+double
+finvsRGB(unsigned int i)
+{
+ return 65535 * linear_from_sRGB(i/255.);
+}
+
+png_uint_16
+invsRGB(unsigned int i)
+{
+ unsigned int x = nearbyint(finvsRGB(i));
+
+ if (x > 65535)
+ {
+ fprintf(stderr, "invsRGB(%u) overflows to %u\n", i, x);
+ exit(1);
+ }
+
+ return (png_uint_16)x;
+}
+
+int
+main(int argc, char **argv)
+{
+ unsigned int i, i16, ibase;
+ double min_error = 0;
+ double max_error = 0;
+ double min_error16 = 0;
+ double max_error16 = 0;
+ double adjust;
+ double adjust_lo = 0.4, adjust_hi = 0.6, adjust_mid = 0.5;
+ unsigned int ec_lo = 0, ec_hi = 0, ec_mid = 0;
+ unsigned int error_count = 0;
+ unsigned int error_count16 = 0;
+ int test_only = 0;
+
+ if (argc > 1)
+ test_only = strcmp("--test", argv[1]) == 0;
+
+ /* Initialize the encoding table first. */
+ for (i=0; i<256; ++i)
+ {
+ png_sRGB_table[i] = invsRGB(i);
+ }
+
+ /* Now work out the decoding tables (this is where the error comes in because
+ * there are 512 set points and 512 straight lines between them.)
+ */
+ for (;;)
+ {
+ if (ec_lo == 0)
+ adjust = adjust_lo;
+
+ else if (ec_hi == 0)
+ adjust = adjust_hi;
+
+ else if (ec_mid == 0)
+ adjust = adjust_mid;
+
+ else if (ec_mid < ec_hi)
+ adjust = (adjust_mid + adjust_hi)/2;
+
+ else if (ec_mid < ec_lo)
+ adjust = (adjust_mid + adjust_lo)/2;
+
+ else
+ {
+ fprintf(stderr, "not reached: %u .. %u .. %u\n", ec_lo, ec_mid, ec_hi);
+ exit(1);
+ }
+
+ /* Calculate the table using the current 'adjust' */
+ for (i=0; i<=511; ++i)
+ {
+ double lo = 255 * sRGB(i << 15);
+ double hi = 255 * sRGB((i+1) << 15);
+ unsigned int calc;
+
+ calc = nearbyint((lo+adjust) * 256);
+ if (calc > 65535)
+ {
+ fprintf(stderr, "table[%d][0]: overflow %08x (%d)\n", i, calc,
+ calc);
+ exit(1);
+ }
+ png_sRGB_base[i] = calc;
+
+ calc = nearbyint((hi-lo) * 32);
+ if (calc > 255)
+ {
+ fprintf(stderr, "table[%d][1]: overflow %08x (%d)\n", i, calc,
+ calc);
+ exit(1);
+ }
+ png_sRGB_delta[i] = calc;
+ }
+
+ /* Check the 16-bit linear values alone: */
+ error_count16 = 0;
+ for (i16=0; i16 <= 65535; ++i16)
+ {
+ unsigned int i = 255*i16;
+ unsigned int iexact = nearbyint(255*sRGB(i));
+ unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+ if (icalc != iexact)
+ ++error_count16;
+ }
+
+ /* Now try changing the adjustment. */
+ if (ec_lo == 0)
+ ec_lo = error_count16;
+
+ else if (ec_hi == 0)
+ ec_hi = error_count16;
+
+ else if (ec_mid == 0)
+ {
+ ec_mid = error_count16;
+ printf("/* initial error counts: %u .. %u .. %u */\n", ec_lo, ec_mid,
+ ec_hi);
+ }
+
+ else if (error_count16 < ec_mid)
+ {
+ printf("/* adjust (mid ): %f: %u -> %u */\n", adjust, ec_mid,
+ error_count16);
+ ec_mid = error_count16;
+ adjust_mid = adjust;
+ }
+
+ else if (adjust < adjust_mid && error_count16 < ec_lo)
+ {
+ printf("/* adjust (low ): %f: %u -> %u */\n", adjust, ec_lo,
+ error_count16);
+ ec_lo = error_count16;
+ adjust_lo = adjust;
+ }
+
+ else if (adjust > adjust_mid && error_count16 < ec_hi)
+ {
+ printf("/* adjust (high): %f: %u -> %u */\n", adjust, ec_hi,
+ error_count16);
+ ec_hi = error_count16;
+ adjust_hi = adjust;
+ }
+
+ else
+ {
+ adjust = adjust_mid;
+ printf("/* adjust: %f: %u */\n", adjust, ec_mid);
+ break;
+ }
+ }
+
+ /* For each entry in the table try to adjust it to minimize the error count
+ * in that entry. Each entry corresponds to 128 input values.
+ */
+ for (ibase=0; ibase<65536; ibase+=128)
+ {
+ png_uint_16 base = png_sRGB_base[ibase >> 7], trybase = base, ob=base;
+ png_byte delta = png_sRGB_delta[ibase >> 7], trydelta = delta, od=delta;
+ unsigned int ecbase = 0, eco;
+
+ for (;;)
+ {
+ png_sRGB_base[ibase >> 7] = trybase;
+ png_sRGB_delta[ibase >> 7] = trydelta;
+
+ /* Check the 16-bit linear values alone: */
+ error_count16 = 0;
+ for (i16=ibase; i16 < ibase+128; ++i16)
+ {
+ unsigned int i = 255*i16;
+ unsigned int iexact = nearbyint(255*sRGB(i));
+ unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+ if (icalc != iexact)
+ ++error_count16;
+ }
+
+ if (error_count16 == 0)
+ break;
+
+ if (ecbase == 0)
+ {
+ eco = ecbase = error_count16;
+ ++trybase; /* First test */
+ }
+
+ else if (error_count16 < ecbase)
+ {
+ if (trybase > base)
+ {
+ base = trybase;
+ ++trybase;
+ }
+ else if (trybase < base)
+ {
+ base = trybase;
+ --trybase;
+ }
+ else if (trydelta > delta)
+ {
+ delta = trydelta;
+ ++trydelta;
+ }
+ else if (trydelta < delta)
+ {
+ delta = trydelta;
+ --trydelta;
+ }
+ else
+ {
+ fprintf(stderr, "makesRGB: impossible\n");
+ exit(1);
+ }
+ ecbase = error_count16;
+ }
+
+ else
+ {
+ if (trybase > base)
+ trybase = base-1;
+ else if (trybase < base)
+ {
+ trybase = base;
+ ++trydelta;
+ }
+ else if (trydelta > delta)
+ trydelta = delta-1;
+ else if (trydelta < delta)
+ break; /* end of tests */
+ }
+ }
+
+ png_sRGB_base[ibase >> 7] = base;
+ png_sRGB_delta[ibase >> 7] = delta;
+ if (base != ob || delta != od)
+ {
+ printf("/* table[%u]={%u,%u} -> {%u,%u} %u -> %u errors */\n",
+ ibase>>7, ob, od, base, delta, eco, ecbase);
+ }
+ else if (0)
+ printf("/* table[%u]={%u,%u} %u errors */\n", ibase>>7, ob, od,
+ ecbase);
+ }
+
+ /* Only do the full (slow) test at the end: */
+ min_error = -.4999;
+ max_error = .4999;
+ error_count = 0;
+
+ for (i=0; i <= max_input; ++i)
+ {
+ unsigned int iexact = nearbyint(255*sRGB(i));
+ unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+ if (icalc != iexact)
+ {
+ double err = 255*sRGB(i) - icalc;
+
+ if (err > (max_error+.001) || err < (min_error-.001))
+ {
+ printf(
+ "/* 0x%08x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n",
+ i, iexact, icalc, png_sRGB_base[i>>15],
+ png_sRGB_delta[i>>15], err);
+ }
+
+ ++error_count;
+ if (err > max_error)
+ max_error = err;
+ else if (err < min_error)
+ min_error = err;
+ }
+ }
+
+ /* Re-check the 16-bit cases too, including the warning if there is an error
+ * bigger than 1.
+ */
+ error_count16 = 0;
+ max_error16 = 0;
+ min_error16 = 0;
+ for (i16=0; i16 <= 65535; ++i16)
+ {
+ unsigned int i = 255*i16;
+ unsigned int iexact = nearbyint(255*sRGB(i));
+ unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+ if (icalc != iexact)
+ {
+ double err = 255*sRGB(i) - icalc;
+
+ ++error_count16;
+ if (err > max_error16)
+ max_error16 = err;
+ else if (err < min_error16)
+ min_error16 = err;
+
+ if (abs(icalc - iexact) > 1)
+ printf(
+ "/* 0x%04x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n",
+ i16, iexact, icalc, png_sRGB_base[i>>15],
+ png_sRGB_delta[i>>15], err);
+ }
+ }
+
+ /* Check the round trip for each 8-bit sRGB value. */
+ for (i16=0; i16 <= 255; ++i16)
+ {
+ unsigned int i = 255 * png_sRGB_table[i16];
+ unsigned int iexact = nearbyint(255*sRGB(i));
+ unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
+
+ if (i16 != iexact)
+ {
+ fprintf(stderr, "8-bit rounding error: %d -> %d\n", i16, iexact);
+ exit(1);
+ }
+
+ if (icalc != i16)
+ {
+ double finv = finvsRGB(i16);
+
+ printf("/* 8-bit roundtrip error: %d -> %f -> %d(%f) */\n",
+ i16, finv, icalc, fsRGB(255*finv));
+ }
+ }
+
+
+ printf("/* error: %g - %g, %u (%g%%) of readings inexact */\n",
+ min_error, max_error, error_count, (100.*error_count)/max_input);
+ printf("/* 16-bit error: %g - %g, %u (%g%%) of readings inexact */\n",
+ min_error16, max_error16, error_count16, (100.*error_count16)/65535);
+
+ if (!test_only)
+ {
+ printf("PNG_CONST png_uint_16 png_sRGB_table[256] =\n{\n ");
+ for (i=0; i<255; )
+ {
+ do
+ {
+ printf("%d,", png_sRGB_table[i++]);
+ }
+ while ((i & 0x7) != 0 && i<255);
+ if (i<255) printf("\n ");
+ }
+ printf("%d\n};\n\n", png_sRGB_table[i]);
+
+
+ printf("PNG_CONST png_uint_16 png_sRGB_base[512] =\n{\n ");
+ for (i=0; i<511; )
+ {
+ do
+ {
+ printf("%d,", png_sRGB_base[i++]);
+ }
+ while ((i & 0x7) != 0 && i<511);
+ if (i<511) printf("\n ");
+ }
+ printf("%d\n};\n\n", png_sRGB_base[i]);
+
+ printf("PNG_CONST png_byte png_sRGB_delta[512] =\n{\n ");
+ for (i=0; i<511; )
+ {
+ do
+ {
+ printf("%d,", png_sRGB_delta[i++]);
+ }
+ while ((i & 0xf) != 0 && i<511);
+ if (i<511) printf("\n ");
+ }
+ printf("%d\n};\n\n", png_sRGB_delta[i]);
+ }
+
+ return 0;
+}
diff --git a/contrib/tools/png-fix-itxt.c b/contrib/tools/png-fix-itxt.c
new file mode 100644
index 0000000..c7654c1
--- /dev/null
+++ b/contrib/tools/png-fix-itxt.c
@@ -0,0 +1,164 @@
+
+/* png-fix-itxt version 1.0.0
+ *
+ * Copyright 2015 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.18 [July 23, 2015]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Usage:
+ *
+ * png-fix-itxt.exe < bad.png > good.png
+ *
+ * Fixes a PNG file written with libpng-1.6.0 or 1.6.1 that has one or more
+ * uncompressed iTXt chunks. Assumes that the actual length is greater
+ * than or equal to the value in the length byte, and that the CRC is
+ * correct for the actual length. This program hunts for the CRC and
+ * adjusts the length byte accordingly. It is not an error to process a
+ * PNG file that has no iTXt chunks or one that has valid iTXt chunks;
+ * such files will simply be copied.
+ *
+ * Requires zlib (for crc32 and Z_NULL); build with
+ *
+ * gcc -O -o png-fix-itxt png-fix-itxt.c -lz
+ *
+ * If you need to handle iTXt chunks larger than 500000 kbytes you must
+ * rebuild png-fix-itxt with a larger values of MAX_LENGTH (or a smaller value
+ * if you know you will never encounter such huge iTXt chunks).
+ */
+
+#include <stdio.h>
+#include <zlib.h>
+
+#define MAX_LENGTH 500000
+
+/* Read one character (inchar), also return octet (c), break if EOF */
+#define GETBREAK inchar=getchar(); \
+ c=(inchar & 0xffU);\
+ if (inchar != c) break
+int
+main(void)
+{
+ unsigned int i;
+ unsigned char buf[MAX_LENGTH];
+ unsigned long crc;
+ unsigned char c;
+ int inchar;
+
+/* Skip 8-byte signature */
+ for (i=8; i; i--)
+ {
+ GETBREAK;
+ putchar(c);
+ }
+
+if (inchar == c) /* !EOF */
+for (;;)
+ {
+ /* Read the length */
+ unsigned long length; /* must be 32 bits! */
+ GETBREAK; buf[0] = c; length = c; length <<= 8;
+ GETBREAK; buf[1] = c; length += c; length <<= 8;
+ GETBREAK; buf[2] = c; length += c; length <<= 8;
+ GETBREAK; buf[3] = c; length += c;
+
+ /* Read the chunkname */
+ GETBREAK; buf[4] = c;
+ GETBREAK; buf[5] = c;
+ GETBREAK; buf[6] = c;
+ GETBREAK; buf[7] = c;
+
+
+ /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */
+ if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116)
+ {
+ if (length >= MAX_LENGTH-12)
+ break; /* To do: handle this more gracefully */
+
+ /* Initialize the CRC */
+ crc = crc32(0, Z_NULL, 0);
+
+ /* Copy the data bytes */
+ for (i=8; i < length + 12; i++)
+ {
+ GETBREAK; buf[i] = c;
+ }
+
+ if (inchar != c) /* EOF */
+ break;
+
+ /* Calculate the CRC */
+ crc = crc32(crc, buf+4, (uInt)length+4);
+
+ for (;;)
+ {
+ /* Check the CRC */
+ if (((crc >> 24) & 0xffU) == buf[length+8] &&
+ ((crc >> 16) & 0xffU) == buf[length+9] &&
+ ((crc >> 8) & 0xffU) == buf[length+10] &&
+ ((crc ) & 0xffU) == buf[length+11])
+ break;
+
+ length++;
+
+ if (length >= MAX_LENGTH-12)
+ break;
+
+ GETBREAK;
+ buf[length+11] = c;
+
+ /* Update the CRC */
+ crc = crc32(crc, buf+7+length, 1);
+ }
+
+ if (inchar != c) /* EOF */
+ break;
+
+ /* Update length bytes */
+ buf[0] = (unsigned char)((length >> 24) & 0xffU);
+ buf[1] = (unsigned char)((length >> 16) & 0xffU);
+ buf[2] = (unsigned char)((length >> 8) & 0xffU);
+ buf[3] = (unsigned char)((length ) & 0xffU);
+
+ /* Write the fixed iTXt chunk (length, name, data, crc) */
+ for (i=0; i<length+12; i++)
+ putchar(buf[i]);
+ }
+
+ else
+ {
+ if (inchar != c) /* EOF */
+ break;
+
+ /* Copy bytes that were already read (length and chunk name) */
+ for (i=0; i<8; i++)
+ putchar(buf[i]);
+
+ /* Copy data bytes and CRC */
+ for (i=8; i< length+12; i++)
+ {
+ GETBREAK;
+ putchar(c);
+ }
+
+ if (inchar != c) /* EOF */
+ {
+ break;
+ }
+
+ /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */
+ if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
+ break;
+ }
+
+ if (inchar != c) /* EOF */
+ break;
+
+ if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
+ break;
+ }
+
+ return 0;
+}
diff --git a/contrib/tools/pngfix.c b/contrib/tools/pngfix.c
new file mode 100644
index 0000000..7fde31c
--- /dev/null
+++ b/contrib/tools/pngfix.c
@@ -0,0 +1,4049 @@
+/* pngfix.c
+ *
+ * Copyright (c) 2014-2015 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.20 [December 3, 2015]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Tool to check and fix the zlib inflate 'too far back' problem.
+ * See the usage message for more information.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#define implies(x,y) assert(!(x) || (y))
+
+#ifdef __GNUC__
+ /* This is used to fix the error:
+ *
+ * pngfix.c:
+ * In function 'zlib_advance':
+ * pngfix.c:181:13: error: assuming signed overflow does not
+ * occur when simplifying conditional to constant [-Werror=strict-overflow]
+ */
+# define FIX_GCC volatile
+#else
+# define FIX_GCC
+#endif
+
+#define PROGRAM_NAME "pngfix"
+
+/* Define the following to use this program against your installed libpng,
+ * rather than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+#if PNG_LIBPNG_VER < 10603 /* 1.6.3 */
+# error "pngfix will not work with libpng prior to 1.6.3"
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#include <setjmp.h>
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_EASY_ACCESS_SUPPORTED) &&\
+ (defined(PNG_READ_DEINTERLACE_SUPPORTED) ||\
+ defined(PNG_READ_INTERLACING_SUPPORTED))
+
+/* zlib.h defines the structure z_stream, an instance of which is included
+ * in this structure and is required for decompressing the LZ compressed
+ * data in PNG files.
+ */
+#ifndef ZLIB_CONST
+ /* We must ensure that zlib uses 'const' in declarations. */
+# define ZLIB_CONST
+#endif
+#include <zlib.h>
+#ifdef const
+ /* zlib.h sometimes #defines const to nothing, undo this. */
+# undef const
+#endif
+
+/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
+ * with older builds.
+ */
+#if ZLIB_VERNUM < 0x1260
+# define PNGZ_MSG_CAST(s) constcast(char*,s)
+# define PNGZ_INPUT_CAST(b) constcast(png_bytep,b)
+#else
+# define PNGZ_MSG_CAST(s) (s)
+# define PNGZ_INPUT_CAST(b) (b)
+#endif
+
+#ifndef PNG_MAXIMUM_INFLATE_WINDOW
+# error "pngfix not supported in this libpng version"
+#endif
+
+#if ZLIB_VERNUM >= 0x1240
+
+/* Copied from pngpriv.h */
+#ifdef __cplusplus
+# define voidcast(type, value) static_cast<type>(value)
+# define constcast(type, value) const_cast<type>(value)
+# define aligncast(type, value) \
+ static_cast<type>(static_cast<void*>(value))
+# define aligncastconst(type, value) \
+ static_cast<type>(static_cast<const void*>(value))
+#else
+# define voidcast(type, value) (value)
+# define constcast(type, value) ((type)(value))
+# define aligncast(type, value) ((void*)(value))
+# define aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+#if PNG_LIBPNG_VER < 10700
+/* Chunk tags (copied from pngpriv.h) */
+#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
+#define PNG_U32(b1,b2,b3,b4) \
+ (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
+
+/* Constants for known chunk types. */
+#define png_IDAT PNG_U32( 73, 68, 65, 84)
+#define png_IEND PNG_U32( 73, 69, 78, 68)
+#define png_IHDR PNG_U32( 73, 72, 68, 82)
+#define png_PLTE PNG_U32( 80, 76, 84, 69)
+#define png_bKGD PNG_U32( 98, 75, 71, 68)
+#define png_cHRM PNG_U32( 99, 72, 82, 77)
+#define png_fRAc PNG_U32(102, 82, 65, 99) /* registered, not defined */
+#define png_gAMA PNG_U32(103, 65, 77, 65)
+#define png_gIFg PNG_U32(103, 73, 70, 103)
+#define png_gIFt PNG_U32(103, 73, 70, 116) /* deprecated */
+#define png_gIFx PNG_U32(103, 73, 70, 120)
+#define png_hIST PNG_U32(104, 73, 83, 84)
+#define png_iCCP PNG_U32(105, 67, 67, 80)
+#define png_iTXt PNG_U32(105, 84, 88, 116)
+#define png_oFFs PNG_U32(111, 70, 70, 115)
+#define png_pCAL PNG_U32(112, 67, 65, 76)
+#define png_pHYs PNG_U32(112, 72, 89, 115)
+#define png_sBIT PNG_U32(115, 66, 73, 84)
+#define png_sCAL PNG_U32(115, 67, 65, 76)
+#define png_sPLT PNG_U32(115, 80, 76, 84)
+#define png_sRGB PNG_U32(115, 82, 71, 66)
+#define png_sTER PNG_U32(115, 84, 69, 82)
+#define png_tEXt PNG_U32(116, 69, 88, 116)
+#define png_tIME PNG_U32(116, 73, 77, 69)
+#define png_tRNS PNG_U32(116, 82, 78, 83)
+#define png_zTXt PNG_U32(122, 84, 88, 116)
+#endif
+
+/* The 8-byte signature as a pair of 32-bit quantities */
+#define sig1 PNG_U32(137, 80, 78, 71)
+#define sig2 PNG_U32( 13, 10, 26, 10)
+
+/* Is the chunk critical? */
+#define CRITICAL(chunk) (((chunk) & PNG_U32(32,0,0,0)) == 0)
+
+/* Is it safe to copy? */
+#define SAFE_TO_COPY(chunk) (((chunk) & PNG_U32(0,0,0,32)) != 0)
+
+/* Fix ups for builds with limited read support */
+#ifndef PNG_ERROR_TEXT_SUPPORTED
+# define png_error(a,b) png_err(a)
+#endif
+
+/********************************* UTILITIES **********************************/
+/* UNREACHED is a value to cause an assert to fail. Because of the way the
+ * assert macro is written the string "UNREACHED" is produced in the error
+ * message.
+ */
+#define UNREACHED 0
+
+/* 80-bit number handling - a PNG image can be up to (2^31-1)x(2^31-1) 8-byte
+ * (16-bit RGBA) pixels in size; that's less than 2^65 bytes or 2^68 bits, so
+ * arithmetic of 80-bit numbers is sufficient. This representation uses an
+ * arbitrary length array of png_uint_16 digits (0..65535). The representation
+ * is little endian.
+ *
+ * The arithmetic functions take zero to two uarb values together with the
+ * number of digits in those values and write the result to the given uarb
+ * (always the first argument) returning the number of digits in the result.
+ * If the result is negative the return value is also negative (this would
+ * normally be an error).
+ */
+typedef png_uint_16 udigit; /* A 'unum' is an array of these */
+typedef png_uint_16p uarb;
+typedef png_const_uint_16p uarbc;
+
+#define UDIGITS(unum) ((sizeof unum)/(sizeof (udigit))
+ /* IMPORTANT: only apply this to an array, applied to a pointer the result
+ * will typically be '2', which is not useful.
+ */
+
+static int
+uarb_set(uarb result, png_alloc_size_t val)
+ /* Set (initialize) 'result' to 'val'. The size required for 'result' must
+ * be determined by the caller from a knowledge of the maximum for 'val'.
+ */
+{
+ int ndigits = 0;
+
+ while (val > 0)
+ {
+ result[ndigits++] = (png_uint_16)(val & 0xffff);
+ val >>= 16;
+ }
+
+ return ndigits;
+}
+
+static int
+uarb_copy(uarb to, uarb from, int idigits)
+ /* Copy a uarb, may reduce the digit count */
+{
+ int d, odigits;
+
+ for (d=odigits=0; d<idigits; ++d)
+ if ((to[d] = from[d]) != 0)
+ odigits = d+1;
+
+ return odigits;
+}
+
+static int
+uarb_inc(uarb num, int in_digits, png_int_32 add)
+ /* This is a signed 32-bit add, except that to avoid overflow the value added
+ * or subtracted must be no more than 2^31-65536. A negative result
+ * indicates a negative number (which is an error below). The size of
+ * 'num' should be max(in_digits+1,2) for arbitrary 'add' but can be just
+ * in_digits+1 if add is known to be in the range -65535..65535.
+ */
+{
+ FIX_GCC int out_digits = 0;
+
+ while (out_digits < in_digits)
+ {
+ add += num[out_digits];
+ num[out_digits++] = (png_uint_16)(add & 0xffff);
+ add >>= 16;
+ }
+
+ while (add != 0 && add != (-1))
+ {
+ num[out_digits++] = (png_uint_16)(add & 0xffff);
+ add >>= 16;
+ }
+
+ if (add == 0)
+ {
+ while (out_digits > 0 && num[out_digits-1] == 0)
+ --out_digits;
+ return out_digits; /* may be 0 */
+ }
+
+ else /* negative result */
+ {
+ while (out_digits > 1 && num[out_digits-1] == 0xffff)
+ --out_digits;
+
+ return -out_digits;
+ }
+}
+
+static int
+uarb_add32(uarb num, int in_digits, png_uint_32 add)
+ /* As above but this works with any 32-bit value and only does 'add' */
+{
+ if (in_digits > 0)
+ {
+ in_digits = uarb_inc(num, in_digits, add & 0xffff);
+ return uarb_inc(num+1, in_digits-1, add >> 16)+1;
+ }
+
+ return uarb_set(num, add);
+}
+
+static int
+uarb_mult_digit(uarb acc, int a_digits, uarb num, FIX_GCC int n_digits,
+ png_uint_16 val)
+ /* Primitive one-digit multiply - 'val' must be 0..65535. Note that this
+ * primitive is a multiply and accumulate - the result of *num * val is added
+ * to *acc.
+ *
+ * This is a one-digit multiply, so the product may be up to one digit longer
+ * than 'num', however the add to 'acc' means that the caller must ensure
+ * that 'acc' is at least one digit longer than this *and* at least one digit
+ * longer than the current length of 'acc'. (Or the caller must otherwise
+ * ensure 'adigits' is adequate from knowledge of the values.)
+ */
+{
+ /* The digits in *acc, *num and val are in the range 0..65535, so the
+ * result below is at most (65535*65535)+2*65635 = 65535*(65535+2), which is
+ * exactly 0xffffffff.
+ */
+ if (val > 0 && n_digits > 0) /* Else the product is 0 */
+ {
+ png_uint_32 carry = 0;
+ int out_digits = 0;
+
+ while (out_digits < n_digits || carry > 0)
+ {
+ if (out_digits < a_digits)
+ carry += acc[out_digits];
+
+ if (out_digits < n_digits)
+ carry += (png_uint_32)num[out_digits] * val;
+
+ acc[out_digits++] = (png_uint_16)(carry & 0xffff);
+ carry >>= 16;
+ }
+
+ /* So carry is 0 and all the input digits have been consumed. This means
+ * that it is possible to skip any remaining digits in acc.
+ */
+ if (out_digits > a_digits)
+ return out_digits;
+ }
+
+ return a_digits;
+}
+
+static int
+uarb_mult32(uarb acc, int a_digits, uarb num, int n_digits, png_uint_32 val)
+ /* calculate acc += num * val, 'val' may be any 32-bit value, 'acc' and 'num'
+ * may be any value, returns the number of digits in 'acc'.
+ */
+{
+ if (n_digits > 0 && val > 0)
+ {
+ a_digits = uarb_mult_digit(acc, a_digits, num, n_digits,
+ (png_uint_16)(val & 0xffff));
+
+ /* Because n_digits and val are >0 the following must be true: */
+ assert(a_digits > 0);
+
+ val >>= 16;
+ if (val > 0)
+ a_digits = uarb_mult_digit(acc+1, a_digits-1, num, n_digits,
+ (png_uint_16)val) + 1;
+ }
+
+ return a_digits;
+}
+
+static int
+uarb_shift(uarb inout, int ndigits, unsigned int right_shift)
+ /* Shift inout right by right_shift bits, right_shift must be in the range
+ * 1..15
+ */
+{
+ FIX_GCC int i = ndigits;
+ png_uint_16 carry = 0;
+
+ assert(right_shift >= 1 && right_shift <= 15);
+
+ while (--i >= 0)
+ {
+ png_uint_16 temp = (png_uint_16)(carry | (inout[i] >> right_shift));
+
+ /* Bottom bits to top bits of carry */
+ carry = (png_uint_16)((inout[i] << (16-right_shift)) & 0xffff);
+
+ inout[i] = temp;
+
+ /* The shift may reduce ndigits */
+ if (i == ndigits-1 && temp == 0)
+ ndigits = i;
+ }
+
+ return ndigits;
+}
+
+static int
+uarb_cmp(uarb a, int adigits, uarb b, int bdigits)
+ /* Return -1/0/+1 according as a<b/a==b/a>b */
+{
+ if (adigits < bdigits)
+ return -1;
+
+ if (adigits > bdigits)
+ return 1;
+
+ while (adigits-- > 0)
+ if (a[adigits] < b[adigits])
+ return -1;
+
+ else if (a[adigits] > b[adigits])
+ return 1;
+
+ return 0;
+}
+
+#if 0 /*UNUSED*/
+static int
+uarb_eq32(uarb num, int digits, png_uint_32 val)
+ /* Return true if the uarb is equal to 'val' */
+{
+ switch (digits)
+ {
+ case 0: return val == 0;
+ case 1: return val == num[0];
+ case 2: return (val & 0xffff) == num[0] && (val >> 16) == num[1];
+ default: return 0;
+ }
+}
+#endif
+
+static void
+uarb_printx(uarb num, int digits, FILE *out)
+ /* Print 'num' as a hexadecimal number (easier than decimal!) */
+{
+ while (digits > 0)
+ if (num[--digits] > 0)
+ {
+ fprintf(out, "0x%x", num[digits]);
+
+ while (digits > 0)
+ fprintf(out, "%.4x", num[--digits]);
+ }
+
+ else if (digits == 0) /* the number is 0 */
+ fputs("0x0", out);
+}
+
+static void
+uarb_print(uarb num, int digits, FILE *out)
+ /* Prints 'num' as a decimal if it will fit in an unsigned long, else as a
+ * hexadecimal number. Notice that the results vary for images over 4GByte
+ * in a system dependent way, and the hexadecimal form doesn't work very well
+ * in awk script input.
+ *
+ *
+ * TODO: write uarb_div10
+ */
+{
+ if (digits * sizeof (udigit) > sizeof (unsigned long))
+ uarb_printx(num, digits, out);
+
+ else
+ {
+ unsigned long n = 0;
+
+ while (digits > 0)
+ n = (n << 16) + num[--digits];
+
+ fprintf(out, "%lu", n);
+ }
+}
+
+/* Generate random bytes. This uses a boring repeatable algorithm and it
+ * is implemented here so that it gives the same set of numbers on every
+ * architecture. It's a linear congruential generator (Knuth or Sedgewick
+ * "Algorithms") but it comes from the 'feedback taps' table in Horowitz and
+ * Hill, "The Art of Electronics" (Pseudo-Random Bit Sequences and Noise
+ * Generation.)
+ *
+ * (Copied from contrib/libtests/pngvalid.c)
+ */
+static void
+make_random_bytes(png_uint_32* seed, void* pv, size_t size)
+{
+ png_uint_32 u0 = seed[0], u1 = seed[1];
+ png_bytep bytes = voidcast(png_bytep, pv);
+
+ /* There are thirty-three bits; the next bit in the sequence is bit-33 XOR
+ * bit-20. The top 1 bit is in u1, the bottom 32 are in u0.
+ */
+ size_t i;
+ for (i=0; i<size; ++i)
+ {
+ /* First generate 8 new bits then shift them in at the end. */
+ png_uint_32 u = ((u0 >> (20-8)) ^ ((u1 << 7) | (u0 >> (32-7)))) & 0xff;
+ u1 <<= 8;
+ u1 |= u0 >> 24;
+ u0 <<= 8;
+ u0 |= u;
+ *bytes++ = (png_byte)u;
+ }
+
+ seed[0] = u0;
+ seed[1] = u1;
+}
+
+/* Clear an object to a random value. */
+static void
+clear(void *pv, size_t size)
+{
+ static png_uint_32 clear_seed[2] = { 0x12345678, 0x9abcdef0 };
+ make_random_bytes(clear_seed, pv, size);
+}
+
+#define CLEAR(object) clear(&(object), sizeof (object))
+
+/* Copied from unreleased 1.7 code.
+ *
+ * CRC checking uses a local pre-built implementation of the Ethernet CRC32.
+ * This is to avoid a function call to the zlib DLL and to optimize the
+ * byte-by-byte case.
+ */
+static png_uint_32 crc_table[256] =
+{
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+ 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+ 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+ 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+ 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+ 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+ 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+ 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+ 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+ 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+ 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+ 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+ 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+ 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+ 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+ 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+ 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+ 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+ 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+ 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+ 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+ 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+ 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+ 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+ 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+ 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+ 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+ 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+ 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+ 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+ 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+ 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+ 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+ 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+ 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+ 0x2d02ef8d
+};
+
+/* The CRC calculated here *IS* conditioned, the corresponding value used by
+ * zlib and the result value is obtained by XORing with CRC_INIT, which is also
+ * the first value that must be passed in (for the first byte) to crc_one_byte.
+ */
+#define CRC_INIT 0xffffffff
+
+static png_uint_32
+crc_one_byte(png_uint_32 crc, int b)
+{
+ return crc_table[(crc ^ b) & 0xff] ^ (crc >> 8);
+}
+
+static png_uint_32
+crc_init_4(png_uint_32 value)
+{
+ /* This is an alternative to the algorithm used in zlib, which requires four
+ * separate tables to parallelize the four byte operations, it only works for
+ * a CRC of the first four bytes of the stream, but this is what happens in
+ * the parser below where length+chunk-name is read and chunk-name used to
+ * initialize the CRC. Notice that the calculation here avoids repeated
+ * conditioning (xor with 0xffffffff) by storing the conditioned value.
+ */
+ png_uint_32 crc = crc_table[(~value >> 24)] ^ 0xffffff;
+
+ crc = crc_table[(crc ^ (value >> 16)) & 0xff] ^ (crc >> 8);
+ crc = crc_table[(crc ^ (value >> 8)) & 0xff] ^ (crc >> 8);
+ return crc_table[(crc ^ value) & 0xff] ^ (crc >> 8);
+}
+
+static int
+chunk_type_valid(png_uint_32 c)
+ /* Bit whacking approach to chunk name validation that is intended to avoid
+ * branches. The cost is that it uses a lot of 32-bit constants, which might
+ * be bad on some architectures.
+ */
+{
+ png_uint_32 t;
+
+ /* Remove bit 5 from all but the reserved byte; this means every
+ * 8-bit unit must be in the range 65-90 to be valid. So bit 5
+ * must be zero, bit 6 must be set and bit 7 zero.
+ */
+ c &= ~PNG_U32(32,32,0,32);
+ t = (c & ~0x1f1f1f1f) ^ 0x40404040;
+
+ /* Subtract 65 for each 8-bit quantity, this must not overflow
+ * and each byte must then be in the range 0-25.
+ */
+ c -= PNG_U32(65,65,65,65);
+ t |=c ;
+
+ /* Subtract 26, handling the overflow which should set the top
+ * three bits of each byte.
+ */
+ c -= PNG_U32(25,25,25,26);
+ t |= ~c;
+
+ return (t & 0xe0e0e0e0) == 0;
+}
+
+/**************************** CONTROL INFORMATION *****************************/
+
+/* Information about a sequence of IDAT chunks, the chunks have been re-synced
+ * using sync_stream below and the new lengths are recorded here. Because the
+ * number of chunks is unlimited this is handled using a linked list of these
+ * structures.
+ */
+struct IDAT_list
+{
+ struct IDAT_list *next; /* Linked list */
+ unsigned int length; /* Actual length of the array below */
+ unsigned int count; /* Number of entries that are valid */
+# define IDAT_INIT_LENGTH 16
+ png_uint_32 lengths[IDAT_INIT_LENGTH];
+};
+
+static void
+IDAT_list_init(struct IDAT_list *list)
+{
+ CLEAR(*list);
+
+ list->next = NULL;
+ list->length = IDAT_INIT_LENGTH;
+}
+
+static size_t
+IDAT_list_size(struct IDAT_list *list, unsigned int length)
+ /* Return the size in bytes of an IDAT_list of the given length. */
+{
+ if (list != NULL)
+ length = list->length;
+
+ return sizeof *list - sizeof list->lengths +
+ length * sizeof list->lengths[0];
+}
+
+static void
+IDAT_list_end(struct IDAT_list *IDAT_list)
+{
+ struct IDAT_list *list = IDAT_list->next;
+
+ CLEAR(*IDAT_list);
+
+ while (list != NULL)
+ {
+ struct IDAT_list *next = list->next;
+
+ clear(list, IDAT_list_size(list, 0));
+ free(list);
+ list = next;
+ }
+}
+
+static struct IDAT_list *
+IDAT_list_extend(struct IDAT_list *tail)
+{
+ /* Use the previous cached value if available. */
+ struct IDAT_list *next = tail->next;
+
+ if (next == NULL)
+ {
+ /* Insert a new, malloc'ed, block of IDAT information buffers, this
+ * one twice as large as the previous one:
+ */
+ unsigned int length = 2 * tail->length;
+
+ if (length < tail->length) /* arithmetic overflow */
+ length = tail->length;
+
+ next = voidcast(IDAT_list*, malloc(IDAT_list_size(NULL, length)));
+ CLEAR(*next);
+
+ /* The caller must handle this: */
+ if (next == NULL)
+ return NULL;
+
+ next->next = NULL;
+ next->length = length;
+ tail->next = next;
+ }
+
+ return next;
+}
+
+/* GLOBAL CONTROL STRUCTURE */
+struct global
+{
+ /* PUBLIC GLOBAL VARIABLES: OWNER INITIALIZE */
+ unsigned int errors :1; /* print file errors to stderr */
+ unsigned int warnings :1; /* print libpng warnings to stderr */
+ unsigned int optimize_zlib :1; /* Run optimization search */
+ unsigned int quiet :2; /* don't output summaries */
+ unsigned int verbose :3; /* various internal tracking */
+ unsigned int skip :3; /* Non-critical chunks to skip */
+# define SKIP_NONE 0
+# define SKIP_BAD_CRC 1 /* Chunks with a bad CRC */
+# define SKIP_UNSAFE 2 /* Chunks not safe to copy */
+# define SKIP_UNUSED 3 /* Chunks not used by libpng */
+# define SKIP_TRANSFORM 4 /* Chunks only used in transforms */
+# define SKIP_COLOR 5 /* Everything but tRNS, sBIT, gAMA and sRGB */
+# define SKIP_ALL 6 /* Everything but tRNS and sBIT */
+
+ png_uint_32 idat_max; /* 0 to perform no re-chunking */
+
+ int status_code; /* Accumulated status code */
+# define TOO_FAR_BACK 0x01 /* found a too-far-back error */
+# define CRC_ERROR 0x02 /* fixed an invalid CRC */
+# define STREAM_ERROR 0x04 /* damaged PNG stream (may be fixable) */
+# define TRUNCATED 0x08 /* truncated but still readable */
+# define FILE_ERROR 0x10 /* could not read the file */
+# define WRITE_ERROR 0x20 /* write error (this terminates the read) */
+# define INTERNAL_ERROR 0x40 /* internal limits/errors encountered */
+
+ /* PUBLIC GLOBAL VARIABLES: USED INTERNALLY BY IDAT READ CODE */
+ struct IDAT_list idat_cache; /* Cache of file IDAT information buffers */
+ /* The structure is shared across all uses of this global control
+ * structure to avoid reallocation between IDAT streams.
+ */
+};
+
+static int
+global_end(struct global *global)
+{
+
+ int rc;
+
+ IDAT_list_end(&global->idat_cache);
+ rc = global->status_code;
+ CLEAR(*global);
+ return rc;
+}
+
+static void
+global_init(struct global *global)
+ /* Call this once (and only once) to initialize the control */
+{
+ CLEAR(*global);
+
+ /* Globals */
+ global->errors = 0;
+ global->warnings = 0;
+ global->quiet = 0;
+ global->verbose = 0;
+ global->idat_max = 0; /* no re-chunking of IDAT */
+ global->optimize_zlib = 0;
+ global->skip = SKIP_NONE;
+ global->status_code = 0;
+
+ IDAT_list_init(&global->idat_cache);
+}
+
+static int
+skip_chunk_type(const struct global *global, png_uint_32 type)
+ /* Return true if this chunk is to be skipped according to the --strip
+ * option. This code needs to recognize all known ancillary chunks in order
+ * to handle the --strip=unsafe option.
+ */
+{
+ /* Never strip critical chunks: */
+ if (CRITICAL(type))
+ return 0;
+
+ switch (type)
+ {
+ /* Chunks that are treated as, effectively, critical because they affect
+ * correct interpretation of the pixel values:
+ */
+ case png_tRNS: case png_sBIT:
+ return 0;
+
+ /* Chunks that specify gamma encoding which should therefore only be
+ * removed the the user insists:
+ */
+ case png_gAMA: case png_sRGB:
+ if (global->skip >= SKIP_ALL)
+ return 1;
+ return 0;
+
+ /* Chunks that affect color interpretation - not used by libpng and rarely
+ * used by applications, but technically still required for correct
+ * interpretation of the image data:
+ */
+ case png_cHRM: case png_iCCP:
+ if (global->skip >= SKIP_COLOR)
+ return 1;
+ return 0;
+
+ /* Other chunks that are used by libpng in image transformations (as
+ * opposed to known chunks that have get/set APIs but are not otherwise
+ * used.)
+ */
+ case png_bKGD:
+ if (global->skip >= SKIP_TRANSFORM)
+ return 1;
+ return 0;
+
+ /* All other chunks that libpng knows about and affect neither image
+ * interpretation nor libpng transforms - chunks that are effectively
+ * unused by libpng even though libpng might recognize and store them.
+ */
+ case png_fRAc: case png_gIFg: case png_gIFt: case png_gIFx: case png_hIST:
+ case png_iTXt: case png_oFFs: case png_pCAL: case png_pHYs: case png_sCAL:
+ case png_sPLT: case png_sTER: case png_tEXt: case png_tIME: case png_zTXt:
+ if (global->skip >= SKIP_UNUSED)
+ return 1;
+ return 0;
+
+ /* Chunks that libpng does not know about (notice that this depends on the
+ * list above including all known chunks!) The decision here depends on
+ * whether the safe-to-copy bit is set in the chunk type.
+ */
+ default:
+ if (SAFE_TO_COPY(type))
+ {
+ if (global->skip >= SKIP_UNUSED) /* as above */
+ return 1;
+ }
+
+ else if (global->skip >= SKIP_UNSAFE)
+ return 1;
+
+ return 0;
+ }
+}
+
+/* PER-FILE CONTROL STRUCTURE */
+struct chunk;
+struct IDAT;
+struct file
+{
+ /* ANCESTORS */
+ struct global *global;
+
+ /* PUBLIC PER-FILE VARIABLES: CALLER INITIALIZE */
+ const char * file_name;
+ const char * out_name; /* Name of output file (if required) */
+
+ /* PUBLIC PER-FILE VARIABLES: SET BY PNG READ CODE */
+ /* File specific result codes */
+ int status_code; /* Set to a bit mask of the following: */
+ int read_errno; /* Records a read error errno */
+ int write_errno; /* Records a write error errno */
+
+ /* IHDR information */
+ png_uint_32 width;
+ png_uint_32 height;
+ png_byte bit_depth;
+ png_byte color_type;
+ png_byte compression_method;
+ png_byte filter_method;
+ png_byte interlace_method;
+
+ udigit image_bytes[5];
+ int image_digits;
+
+ /* PROTECTED PER-FILE VARIABLES: USED BY THE READ CODE */
+ FILE * file; /* Original PNG file */
+ FILE * out; /* If a new one is being written */
+ jmp_buf jmpbuf; /* Set while reading a PNG */
+
+ /* PROTECTED CHUNK SPECIFIC VARIABLES: USED BY CHUNK CODE */
+ /* The following variables are used during reading to record the length, type
+ * and data position of the *next* chunk or, right at the start, the
+ * signature (in length,type).
+ *
+ * When a chunk control structure is instantiated these values are copied
+ * into the structure and can then be overritten with the data for the next
+ * chunk.
+ */
+ fpos_t data_pos; /* Position of first byte of chunk data */
+ png_uint_32 length; /* First word (length or signature start) */
+ png_uint_32 type; /* Second word (type or signature end) */
+ png_uint_32 crc; /* Running chunk CRC (used by read_chunk) */
+
+ /* These counts are maintained by the read and write routines below and are
+ * reset by the chunk handling code. They record the total number of bytes
+ * read or written for the chunk, including the header (length,type) bytes.
+ */
+ png_uint_32 read_count; /* Count of bytes read (in the chunk) */
+ png_uint_32 write_count; /* Count of bytes written (in the chunk) */
+ int state; /* As defined here: */
+# define STATE_SIGNATURE 0 /* The signature is being written */
+# define STATE_CHUNKS 1 /* Non-IDAT chunks are being written */
+# define STATE_IDAT 2 /* An IDAT stream is being written */
+
+ /* Two pointers used to enable clean-up in the event of fatal errors and to
+ * hold state about the parser process (only one of each at present.)
+ */
+ struct chunk * chunk;
+ struct IDAT * idat;
+
+ /* Interface to allocate a new chunk or IDAT control structure. The result
+ * is returned by setting one or other of the above variables. Note that the
+ * relevant initializer is called by the allocator function. The alloc_ptr
+ * is used only by the implementation of the allocate function.
+ */
+ void * alloc_ptr;
+ void (*alloc)(struct file*,int idat);
+ /* idat: allocate IDAT not chunk */
+};
+
+/* Valid longjmp (stop) codes are: */
+#define LIBPNG_WARNING_CODE 1 /* generic png_error */
+#define LIBPNG_ERROR_CODE 2 /* generic png_error */
+#define ZLIB_ERROR_CODE 3 /* generic zlib error */
+#define INVALID_ERROR_CODE 4 /* detected an invalid PNG */
+#define READ_ERROR_CODE 5 /* read failed */
+#define WRITE_ERROR_CODE 6 /* error in write */
+#define UNEXPECTED_ERROR_CODE 7 /* unexpected (internal?) error */
+
+static void
+emit_string(const char *str, FILE *out)
+ /* Print a string with spaces replaced by '_' and non-printing characters by
+ * an octal escape.
+ */
+{
+ for (; *str; ++str)
+ if (isgraph(UCHAR_MAX & *str))
+ putc(*str, out);
+
+ else if (isspace(UCHAR_MAX & *str))
+ putc('_', out);
+
+ else
+ fprintf(out, "\\%.3o", *str);
+}
+
+static const char *
+strcode(int code)
+{
+ switch (code)
+ {
+ case LIBPNG_WARNING_CODE: return "warning";
+ case LIBPNG_ERROR_CODE: return "libpng";
+ case ZLIB_ERROR_CODE: return "zlib";
+ case INVALID_ERROR_CODE: return "invalid";
+ case READ_ERROR_CODE: return "read";
+ case WRITE_ERROR_CODE: return "write";
+ case UNEXPECTED_ERROR_CODE: return "unexpected";
+ default: return "INVALID";
+ }
+}
+
+static void
+emit_error(struct file *file, int code, const char *what)
+ /* Generic error message routine, takes a 'stop' code but can be used
+ * elsewhere. Always outputs a message.
+ */
+{
+ const char *reason;
+ int err = 0;
+
+ switch (code)
+ {
+ case LIBPNG_WARNING_CODE: reason = "libpng warning:"; break;
+ case LIBPNG_ERROR_CODE: reason = "libpng error:"; break;
+ case ZLIB_ERROR_CODE: reason = "zlib error:"; break;
+ case INVALID_ERROR_CODE: reason = "invalid"; break;
+ case READ_ERROR_CODE: reason = "read failure:";
+ err = file->read_errno;
+ break;
+ case WRITE_ERROR_CODE: reason = "write error";
+ err = file->write_errno;
+ break;
+ case UNEXPECTED_ERROR_CODE: reason = "unexpected error:";
+ err = file->read_errno;
+ if (err == 0)
+ err = file->write_errno;
+ break;
+ default: reason = "INVALID (internal error):"; break;
+ }
+
+ if (err != 0)
+ fprintf(stderr, "%s: %s %s [%s]\n", file->file_name, reason, what,
+ strerror(err));
+
+ else
+ fprintf(stderr, "%s: %s %s\n", file->file_name, reason, what);
+}
+
+static void chunk_end(struct chunk **);
+static void IDAT_end(struct IDAT **);
+
+static int
+file_end(struct file *file)
+{
+ int rc;
+
+ /* If either of the chunk pointers are set end them here, the IDAT structure
+ * must be deallocated first as it may deallocate the chunk structure.
+ */
+ if (file->idat != NULL)
+ IDAT_end(&file->idat);
+
+ if (file->chunk != NULL)
+ chunk_end(&file->chunk);
+
+ rc = file->status_code;
+
+ if (file->file != NULL)
+ (void)fclose(file->file);
+
+ if (file->out != NULL)
+ {
+ /* NOTE: this is bitwise |, all the following functions must execute and
+ * must succeed.
+ */
+ if (ferror(file->out) | fflush(file->out) | fclose(file->out))
+ {
+ perror(file->out_name);
+ emit_error(file, READ_ERROR_CODE, "output write error");
+ rc |= WRITE_ERROR;
+ }
+ }
+
+ /* Accumulate the result codes */
+ file->global->status_code |= rc;
+
+ CLEAR(*file);
+
+ return rc; /* status code: non-zero on read or write error */
+}
+
+static int
+file_init(struct file *file, struct global *global, const char *file_name,
+ const char *out_name, void *alloc_ptr, void (*alloc)(struct file*,int))
+ /* Initialize a file control structure. This will open the given files as
+ * well. The status code returned is 0 on success, non zero (using the flags
+ * above) on a file open error.
+ */
+{
+ CLEAR(*file);
+ file->global = global;
+
+ file->file_name = file_name;
+ file->out_name = out_name;
+ file->status_code = 0;
+ file->read_errno = 0;
+ file->write_errno = 0;
+
+ file->file = NULL;
+ file->out = NULL;
+ /* jmpbuf is garbage: must be set by read_png */
+
+ file->read_count = 0;
+ file->state = STATE_SIGNATURE;
+
+ file->chunk = NULL;
+ file->idat = NULL;
+
+ file->alloc_ptr = alloc_ptr;
+ file->alloc = alloc;
+
+ /* Open the files: */
+ assert(file_name != NULL);
+ file->file = fopen(file_name, "rb");
+
+ if (file->file == NULL)
+ {
+ file->read_errno = errno;
+ file->status_code |= FILE_ERROR;
+ /* Always output: please give a readable file! */
+ perror(file_name);
+ return FILE_ERROR;
+ }
+
+ if (out_name != NULL)
+ {
+ file->out = fopen(out_name, "wb");
+
+ if (file->out == NULL)
+ {
+ file->write_errno = errno;
+ file->status_code |= WRITE_ERROR;
+ perror(out_name);
+ return WRITE_ERROR;
+ }
+ }
+
+ return 0;
+}
+
+static void
+log_error(struct file *file, int code, const char *what)
+ /* Like emit_error but checks the global 'errors' flag */
+{
+ if (file->global->errors)
+ emit_error(file, code, what);
+}
+
+static char
+type_char(png_uint_32 v)
+{
+ /* In fact because chunk::chunk_type is validated prior to any call to this
+ * function it will always return a-zA-Z, but the extra codes are just there
+ * to help in finding internal (programming) errors. Note that the code only
+ * ever considers the low 7 bits of the value (so it is not necessary for the
+ * type_name function to mask of the byte.)
+ */
+ if (v & 32)
+ return "!abcdefghijklmnopqrstuvwxyz56789"[(v-96)&31];
+
+ else
+ return "@ABCDEFGHIJKLMNOPQRSTUVWXYZ01234"[(v-64)&31];
+}
+
+static void
+type_name(png_uint_32 type, FILE *out)
+{
+ putc(type_char(type >> 24), out);
+ putc(type_char(type >> 16), out);
+ putc(type_char(type >> 8), out);
+ putc(type_char(type ), out);
+}
+
+static void
+type_sep(FILE *out)
+{
+ putc(':', out);
+ putc(' ', out);
+}
+
+static png_uint_32 current_type(struct file *file, int code);
+
+PNG_NORETURN static void
+stop(struct file *file, int code, const char *what)
+ /* Return control when a PNG file cannot be read. This outputs an 'ERR'
+ * summary line too.
+ */
+{
+ log_error(file, code, what);
+
+ /* The chunk being read is typically identified by file->chunk or, if this is
+ * NULL, by file->type. This may be wrong if libpng reads ahead, but this
+ * only happens with IDAT where libpng reads the header then jumps around
+ * finding errors in the previous chunks. We know that is happening because
+ * we are at the start of the IDAT (i.e. no IDAT data has yet been written.)
+ *
+ * SUMMARY FORMAT (stop):
+ *
+ * IDAT ERR status code read-errno write-errno message file
+ *
+ * 'uncompressed' will be 0 if there was a problem in the IHDR. The errno
+ * values are emit_string(strerror(errno)).
+ */
+ if (file->global->quiet < 2) /* need two quiets to stop this. */
+ {
+ png_uint_32 type;
+
+ if (file->chunk != NULL)
+ type = current_type(file, code); /* Gropes in struct chunk and IDAT */
+
+ else
+ type = file->type;
+
+ if (type)
+ type_name(type, stdout);
+
+ else /* magic: an IDAT header, produces bogons for too many IDATs */
+ fputs("HEAD", stdout); /* not a registered chunk! */
+
+ printf(" ERR %.2x %s ", file->status_code, strcode(code));
+ /* This only works one strerror at a time, because of the way strerror is
+ * implemented.
+ */
+ emit_string(strerror(file->read_errno), stdout);
+ putc(' ', stdout);
+ emit_string(strerror(file->write_errno), stdout);
+ putc(' ', stdout);
+ emit_string(what, stdout);
+ putc(' ', stdout);
+ fputs(file->file_name, stdout);
+ putc('\n', stdout);
+ }
+
+ file->status_code |= FILE_ERROR;
+ longjmp(file->jmpbuf, code);
+}
+
+PNG_NORETURN static void
+stop_invalid(struct file *file, const char *what)
+{
+ stop(file, INVALID_ERROR_CODE, what);
+}
+
+static void
+type_message(struct file *file, png_uint_32 type, const char *what)
+ /* Error message for a chunk; the chunk name comes from 'type' */
+{
+ if (file->global->errors)
+ {
+ fputs(file->file_name, stderr);
+ type_sep(stderr);
+ type_name(type, stderr);
+ type_sep(stderr);
+ fputs(what, stderr);
+ putc('\n', stderr);
+ }
+}
+
+/* Input file positioning - we jump around in the input file while reading
+ * stuff, these wrappers deal with the error handling.
+ */
+static void
+file_getpos(struct file *file, fpos_t *pos)
+{
+ if (fgetpos(file->file, pos))
+ {
+ /* This is unexpected, so perror it */
+ perror(file->file_name);
+ stop(file, READ_ERROR_CODE, "fgetpos");
+ }
+}
+
+static void
+file_setpos(struct file *file, const fpos_t *pos)
+{
+ if (fsetpos(file->file, pos))
+ {
+ perror(file->file_name);
+ stop(file, READ_ERROR_CODE, "fsetpos");
+ }
+}
+
+static void
+getpos(struct file *file)
+ /* Get the current position and store it in 'data_pos'. The corresponding
+ * setpos() function is chunk specific because it uses the copy of the
+ * position for the specific chunk.
+ */
+{
+ file_getpos(file, &file->data_pos);
+}
+
+
+/* Read utility - read a single byte, returns a value in the range 0..255 or EOF
+ * on a read error. In the latter case status_code and read_errno are updated
+ * appropriately.
+ */
+static int
+read_byte(struct file *file)
+{
+ int ch = getc(file->file);
+
+ if (ch >= 0 && ch <= 255)
+ {
+ ++(file->read_count);
+ return ch;
+ }
+
+ else if (ch != EOF)
+ {
+ file->status_code |= INTERNAL_ERROR;
+ file->read_errno = ERANGE; /* out of range character */
+
+ /* This is very unexpected; an error message is always output: */
+ emit_error(file, UNEXPECTED_ERROR_CODE, "file read");
+ }
+
+# ifdef EINTR
+ else if (errno == EINTR) /* Interrupted, try again */
+ {
+ errno = 0;
+ return read_byte(file);
+ }
+# endif
+
+ else
+ {
+ /* An error, it doesn't really matter what the error is but it gets
+ * recorded anyway.
+ */
+ if (ferror(file->file))
+ file->read_errno = errno;
+
+ else if (feof(file->file))
+ file->read_errno = 0; /* I.e. a regular EOF, no error */
+
+ else /* unexpected */
+ file->read_errno = EDOM;
+ }
+
+ /* 'TRUNCATED' is used for all cases of failure to read a byte, because of
+ * the way libpng works a byte read is never attempted unless the byte is
+ * expected to be there, so EOF should not occur.
+ */
+ file->status_code |= TRUNCATED;
+ return EOF;
+}
+
+static png_byte
+reread_byte(struct file *file)
+ /* Read a byte when an error is not expected to happen because the byte has
+ * been read before without error.
+ */
+{
+ int ch = getc(file->file);
+
+ if (errno != 0)
+ file->read_errno = errno;
+
+ if (ch < 0 || ch > 255)
+ stop(file, UNEXPECTED_ERROR_CODE, "reread");
+
+ return (png_byte)ch;
+}
+
+static png_uint_32
+reread_4(struct file *file)
+ /* The same but for a four byte quantity */
+{
+ png_uint_32 result = 0;
+ int i = 0;
+
+ while (++i <= 4)
+ result = (result << 8) + reread_byte(file);
+
+ return result;
+}
+
+static void
+skip_12(struct file *file)
+ /* Skip exactly 12 bytes in the input stream - used to skip a CRC and chunk
+ * header that has been read before.
+ */
+{
+ /* Since the chunks were read before this shouldn't fail: */
+ if (fseek(file->file, 12, SEEK_CUR) != 0)
+ {
+ if (errno != 0)
+ file->read_errno = errno;
+
+ stop(file, UNEXPECTED_ERROR_CODE, "reskip");
+ }
+}
+
+static void
+write_byte(struct file *file, int b)
+ /* Write one byte to the output - this causes a fatal error if the write
+ * fails and the read of this PNG file immediately terminates. Just
+ * increments the write count if there is no output file.
+ */
+{
+ if (file->out != NULL)
+ {
+ if (putc(b, file->out) != b)
+ {
+ file->write_errno = errno;
+ file->status_code |= WRITE_ERROR;
+ stop(file, WRITE_ERROR_CODE, "write byte");
+ }
+ }
+
+ ++(file->write_count);
+}
+
+/* Derivatives of the read/write functions. */
+static unsigned int
+read_4(struct file *file, png_uint_32 *pu)
+ /* Read four bytes, returns the number of bytes read successfully and, if all
+ * four bytes are read, assigns the result to *pu.
+ */
+{
+ unsigned int i = 0;
+ png_uint_32 val = 0;
+
+ do
+ {
+ int ch = read_byte(file);
+
+ if (ch == EOF)
+ return i;
+
+ val = (val << 8) + ch;
+ } while (++i < 4);
+
+ *pu = val;
+ return i;
+}
+
+/* CRC handling - read but calculate the CRC while doing so. */
+static int
+crc_read_many(struct file *file, png_uint_32 length)
+ /* Reads 'length' bytes and updates the CRC, returns true on success, false
+ * if the input is truncated.
+ */
+{
+ if (length > 0)
+ {
+ png_uint_32 crc = file->crc;
+
+ do
+ {
+ int ch = read_byte(file);
+
+ if (ch == EOF)
+ return 0; /* Truncated */
+
+ crc = crc_one_byte(crc, ch);
+ }
+ while (--length > 0);
+
+ file->crc = crc;
+ }
+
+ return 1; /* OK */
+}
+
+static int
+calc_image_size(struct file *file)
+ /* Fill in the image_bytes field given the IHDR information, calls stop on
+ * error.
+ */
+{
+ png_uint_16 pd = file->bit_depth;
+
+ switch (file->color_type)
+ {
+ default:
+ stop_invalid(file, "IHDR: colour type");
+
+ invalid_bit_depth:
+ stop_invalid(file, "IHDR: bit depth");
+
+ case 0: /* g */
+ if (pd != 1 && pd != 2 && pd != 4 && pd != 8 && pd != 16)
+ goto invalid_bit_depth;
+ break;
+
+ case 3:
+ if (pd != 1 && pd != 2 && pd != 4 && pd != 8)
+ goto invalid_bit_depth;
+ break;
+
+ case 2: /* rgb */
+ if (pd != 8 && pd != 16)
+ goto invalid_bit_depth;
+
+ pd = (png_uint_16)(pd * 3);
+ break;
+
+ case 4: /* ga */
+ if (pd != 8 && pd != 16)
+ goto invalid_bit_depth;
+
+ pd = (png_uint_16)(pd * 2);
+ break;
+
+ case 6: /* rgba */
+ if (pd != 8 && pd != 16)
+ goto invalid_bit_depth;
+
+ pd = (png_uint_16)(pd * 4);
+ break;
+ }
+
+ if (file->width < 1 || file->width > 0x7fffffff)
+ stop_invalid(file, "IHDR: width");
+
+ else if (file->height < 1 || file->height > 0x7fffffff)
+ stop_invalid(file, "IHDR: height");
+
+ else if (file->compression_method != 0)
+ stop_invalid(file, "IHDR: compression method");
+
+ else if (file->filter_method != 0)
+ stop_invalid(file, "IHDR: filter method");
+
+ else switch (file->interlace_method)
+ {
+ case PNG_INTERLACE_ADAM7:
+ /* Interlacing makes the image larger because of the replication of
+ * both the filter byte and the padding to a byte boundary.
+ */
+ {
+ int pass;
+ int image_digits = 0;
+ udigit row_width[2], row_bytes[3];
+
+ for (pass=0; pass<=6; ++pass)
+ {
+ png_uint_32 pw = PNG_PASS_COLS(file->width, pass);
+
+ if (pw > 0)
+ {
+ int digits;
+
+ /* calculate 1+((pw*pd+7)>>3) in row_bytes */
+ digits = uarb_mult_digit(row_bytes, uarb_set(row_bytes, 7),
+ row_width, uarb_set(row_width, pw), pd);
+ digits = uarb_shift(row_bytes, digits, 3);
+ digits = uarb_inc(row_bytes, digits, 1);
+
+ /* Add row_bytes * pass-height to the file image_bytes field
+ */
+ image_digits = uarb_mult32(file->image_bytes, image_digits,
+ row_bytes, digits,
+ PNG_PASS_ROWS(file->height, pass));
+ }
+ }
+
+ file->image_digits = image_digits;
+ }
+ break;
+
+ case PNG_INTERLACE_NONE:
+ {
+ int digits;
+ udigit row_width[2], row_bytes[3];
+
+ /* As above, but use image_width in place of the pass width: */
+ digits = uarb_mult_digit(row_bytes, uarb_set(row_bytes, 7),
+ row_width, uarb_set(row_width, file->width), pd);
+ digits = uarb_shift(row_bytes, digits, 3);
+ digits = uarb_inc(row_bytes, digits, 1);
+
+ /* Set row_bytes * image-height to the file image_bytes field */
+ file->image_digits = uarb_mult32(file->image_bytes, 0,
+ row_bytes, digits, file->height);
+ }
+ break;
+
+ default:
+ stop_invalid(file, "IHDR: interlace method");
+ }
+
+ assert(file->image_digits >= 1 && file->image_digits <= 5);
+ return 1;
+}
+
+/* PER-CHUNK CONTROL STRUCTURE
+ * This structure is instantiated for each chunk, except for the IDAT chunks
+ * where one chunk control structure is used for the whole of a single stream of
+ * IDAT chunks (see the IDAT control structure below).
+ */
+struct chunk
+{
+ /* ANCESTORS */
+ struct file * file;
+ struct global * global;
+
+ /* PUBLIC IDAT INFORMATION: SET BY THE ZLIB CODE */
+ udigit uncompressed_bytes[5];
+ int uncompressed_digits;
+ udigit compressed_bytes[5];
+ int compressed_digits;
+
+ /* PUBLIC PER-CHUNK INFORMATION: USED BY CHUNK READ CODE */
+ /* This information is filled in by chunk_init from the data in the file
+ * control structure, but chunk_length may be changed later.
+ */
+ fpos_t chunk_data_pos; /* Position of first byte of chunk data */
+ png_uint_32 chunk_length; /* From header (or modified below) */
+ png_uint_32 chunk_type; /* From header */
+
+ /* PUBLIC PER-CHUNK INFORMATION: FOR THE CHUNK WRITE CODE */
+ png_uint_32 write_crc; /* Output CRC (may differ from read_crc) */
+ png_uint_32 rewrite_offset; /* Count of bytes before rewrite. */
+ int rewrite_length; /* Number of bytes left to change */
+ png_byte rewrite_buffer[2]; /* Buffer of new byte values */
+};
+
+static void
+chunk_message(struct chunk *chunk, const char *message)
+{
+ type_message(chunk->file, chunk->chunk_type, message);
+}
+
+static void
+chunk_end(struct chunk **chunk_var)
+{
+ struct chunk *chunk = *chunk_var;
+
+ *chunk_var = NULL;
+ CLEAR(*chunk);
+}
+
+static void
+chunk_init(struct chunk * const chunk, struct file * const file)
+ /* When a chunk is initialized the file length/type/pos are copied into the
+ * corresponding chunk fields and the new chunk is registered in the file
+ * structure. There can only be one chunk at a time.
+ *
+ * NOTE: this routine must onely be called from the file alloc routine!
+ */
+{
+ assert(file->chunk == NULL);
+
+ CLEAR(*chunk);
+
+ chunk->file = file;
+ chunk->global = file->global;
+
+ chunk->chunk_data_pos = file->data_pos;
+ chunk->chunk_length = file->length;
+ chunk->chunk_type = file->type;
+
+ /* Compresssed/uncompressed size information (from the zlib control structure
+ * that is used to check the compressed data in a chunk.)
+ */
+ chunk->uncompressed_digits = 0;
+ chunk->compressed_digits = 0;
+
+ file->chunk = chunk;
+}
+
+static png_uint_32
+current_type(struct file *file, int code)
+ /* Guess the actual chunk type that causes a stop() */
+{
+ /* This may return png_IDAT for errors detected (late) in the header; that
+ * includes any inter-chunk consistency check that libpng performs. Assume
+ * that if the chunk_type is png_IDAT and the file write count is 8 this is
+ * what is happening.
+ */
+ if (file->chunk != NULL)
+ {
+ png_uint_32 type = file->chunk->chunk_type;
+
+ /* This is probably wrong for the excess IDATs case, because then libpng
+ * whines about too many of them (apparently in some cases erroneously)
+ * when the header is read.
+ */
+ if (code <= LIBPNG_ERROR_CODE && type == png_IDAT &&
+ file->write_count == 8)
+ type = 0; /* magic */
+
+ return type;
+ }
+
+ else
+ return file->type;
+}
+
+static void
+setpos(struct chunk *chunk)
+ /* Reset the position to 'chunk_data_pos' - the start of the data for this
+ * chunk. As a side effect the read_count in the file is reset to 8, just
+ * after the length/type header.
+ */
+{
+ chunk->file->read_count = 8;
+ file_setpos(chunk->file, &chunk->chunk_data_pos);
+}
+
+/* Specific chunk handling - called for each chunk header, all special chunk
+ * processing is initiated in these functions.
+ */
+/* The next functions handle special processing for those chunks with LZ data,
+ * the data is identified and checked for validity. If there are problems which
+ * cannot be corrected the routines return false, otherwise true (although
+ * modification to the zlib header may be required.)
+ *
+ * The compressed data is in zlib format (RFC1950) and consequently has a
+ * minimum length of 7 bytes.
+ */
+static int zlib_check(struct file *file, png_uint_32 offset);
+
+static int
+process_zTXt_iCCP(struct file *file)
+ /* zTXt and iCCP have exactly the same form - keyword, null, compression
+ * method then compressed data.
+ */
+{
+ struct chunk *chunk = file->chunk;
+ png_uint_32 length;
+ png_uint_32 index = 0;
+
+ assert(chunk != NULL && file->idat == NULL);
+ length = chunk->chunk_length;
+ setpos(chunk);
+
+ while (length >= 9)
+ {
+ --length;
+ ++index;
+ if (reread_byte(file) == 0) /* keyword null terminator */
+ {
+ --length;
+ ++index;
+ (void)reread_byte(file); /* compression method */
+ return zlib_check(file, index);
+ }
+ }
+
+ chunk_message(chunk, "too short");
+ return 0; /* skip */
+}
+
+static int
+process_iTXt(struct file *file)
+{
+ /* Like zTXt but more fields. */
+ struct chunk *chunk = file->chunk;
+ png_uint_32 length;
+ png_uint_32 index = 0;
+
+ assert(chunk != NULL && file->idat == NULL);
+ length = chunk->chunk_length;
+ setpos(chunk);
+
+ while (length >= 5)
+ {
+ --length;
+ ++index;
+ if (reread_byte(file) == 0) /* keyword null terminator */
+ {
+ --length;
+ ++index;
+ if (reread_byte(file) == 0) /* uncompressed text */
+ return 1; /* nothing to check */
+
+ --length;
+ ++index;
+ (void)reread_byte(file); /* compression method */
+
+ /* Skip the language tag (null terminated). */
+ while (length >= 9)
+ {
+ --length;
+ ++index;
+ if (reread_byte(file) == 0) /* terminator */
+ {
+ /* Skip the translated keyword */
+ while (length >= 8)
+ {
+ --length;
+ ++index;
+ if (reread_byte(file) == 0) /* terminator */
+ return zlib_check(file, index);
+ }
+ }
+ }
+
+ /* Ran out of bytes in the compressed case. */
+ break;
+ }
+ }
+
+ log_error(file, INVALID_ERROR_CODE, "iTXt chunk length");
+
+ return 0; /* skip */
+}
+
+/* IDAT READ/WRITE CONTROL STRUCTURE */
+struct IDAT
+{
+ /* ANCESTORS */
+ struct file * file;
+ struct global * global;
+
+ /* PROTECTED IDAT INFORMATION: SET BY THE IDAT READ CODE */
+ struct IDAT_list *idat_list_head; /* START of the list of IDAT information */
+ struct IDAT_list *idat_list_tail; /* *END* of the list of IDAT information */
+
+ /* PROTECTED IDAT INFORMATION: USED BY THE IDAT WRITE CODE */
+ struct IDAT_list *idat_cur; /* Current list entry */
+ unsigned int idat_count; /* And the *current* index into the list */
+ png_uint_32 idat_index; /* Index of *next* input byte to write */
+ png_uint_32 idat_length; /* Cache of current chunk length */
+};
+
+/* NOTE: there is currently no IDAT_reset, so a stream cannot contain more than
+ * one IDAT sequence (i.e. MNG is not supported).
+ */
+
+static void
+IDAT_end(struct IDAT **idat_var)
+{
+ struct IDAT *idat = *idat_var;
+ struct file *file = idat->file;
+
+ *idat_var = NULL;
+
+ CLEAR(*idat);
+
+ assert(file->chunk != NULL);
+ chunk_end(&file->chunk);
+
+ /* Regardless of why the IDAT was killed set the state back to CHUNKS (it may
+ * already be CHUNKS because the state isn't changed until process_IDAT
+ * returns; a stop will cause IDAT_end to be entered in state CHUNKS!)
+ */
+ file->state = STATE_CHUNKS;
+}
+
+static void
+IDAT_init(struct IDAT * const idat, struct file * const file)
+ /* When the chunk is png_IDAT instantiate an IDAT control structure in place
+ * of a chunk control structure. The IDAT will instantiate a chunk control
+ * structure using the file alloc routine.
+ *
+ * NOTE: this routine must only be called from the file alloc routine!
+ */
+{
+ assert(file->chunk == NULL);
+ assert(file->idat == NULL);
+
+ CLEAR(*idat);
+
+ idat->file = file;
+ idat->global = file->global;
+
+ /* Initialize the tail to the pre-allocated buffer and set the count to 0
+ * (empty.)
+ */
+ idat->global->idat_cache.count = 0;
+ idat->idat_list_head = idat->idat_list_tail = &idat->global->idat_cache;
+
+ /* Now the chunk. The allocator calls the initializer of the new chunk and
+ * stores the result in file->chunk:
+ */
+ file->alloc(file, 0/*chunk*/);
+ assert(file->chunk != NULL);
+
+ /* And store this for cleanup (and to check for double alloc or failure to
+ * free.)
+ */
+ file->idat = idat;
+}
+
+static png_uint_32
+rechunk_length(struct IDAT *idat)
+ /* Return the length for the next IDAT chunk, taking into account
+ * rechunking.
+ */
+{
+ png_uint_32 len = idat->global->idat_max;
+
+ if (len == 0) /* use original chunk lengths */
+ {
+ const struct IDAT_list *cur;
+ unsigned int count;
+
+ if (idat->idat_index == 0) /* at the new chunk (first time) */
+ return idat->idat_length; /* use the cache */
+
+ /* Otherwise rechunk_length is called at the end of a chunk for the length
+ * of the next one.
+ */
+ cur = idat->idat_cur;
+ count = idat->idat_count;
+
+ assert(idat->idat_index == idat->idat_length &&
+ idat->idat_length == cur->lengths[count]);
+
+ /* Return length of the *next* chunk */
+ if (++count < cur->count)
+ return cur->lengths[count];
+
+ /* End of this list */
+ assert(cur != idat->idat_list_tail);
+ cur = cur->next;
+ assert(cur != NULL && cur->count > 0);
+ return cur->lengths[0];
+ }
+
+ else /* rechunking */
+ {
+ /* The chunk size is the lesser of file->idat_max and the number
+ * of remaining bytes.
+ */
+ png_uint_32 have = idat->idat_length - idat->idat_index;
+
+ if (len > have)
+ {
+ struct IDAT_list *cur = idat->idat_cur;
+ unsigned int j = idat->idat_count+1; /* the next IDAT in the list */
+
+ do
+ {
+ /* Add up the remaining bytes. This can't overflow because the
+ * individual lengths are always <= 0x7fffffff, so when we add two
+ * of them overflow is not possible.
+ */
+ assert(cur != NULL);
+
+ for (;;)
+ {
+ /* NOTE: IDAT_list::count here, not IDAT_list::length */
+ for (; j < cur->count; ++j)
+ {
+ have += cur->lengths[j];
+ if (len <= have)
+ return len;
+ }
+
+ /* If this was the end return the count of the available bytes */
+ if (cur == idat->idat_list_tail)
+ return have;
+
+ cur = cur->next;
+ j = 0;
+ }
+ }
+ while (len > have);
+ }
+
+ return len;
+ }
+}
+
+static int
+process_IDAT(struct file *file)
+ /* Process the IDAT stream, this is the more complex than the preceding
+ * cases because the compressed data is spread across multiple IDAT chunks
+ * (typically). Rechunking of the data is not handled here; all this
+ * function does is establish whether the zlib header needs to be modified.
+ *
+ * Initially the function returns false, indicating that the chunk should not
+ * be written. It does this until the last IDAT chunk is passed in, then it
+ * checks the zlib data and returns true.
+ *
+ * It does not return false on a fatal error; it calls stop instead.
+ *
+ * The caller must have an instantiated (IDAT) control structure and it must
+ * have extent over the whole read of the IDAT stream. For a PNG this means
+ * the whole PNG read, for MNG it could have lesser extent.
+ */
+{
+ struct IDAT_list *list;
+
+ assert(file->idat != NULL && file->chunk != NULL);
+
+ /* We need to first check the entire sequence of IDAT chunks to ensure the
+ * stream is in sync. Do this by building a list of all the chunks and
+ * recording the length of each because the length may have been fixed up by
+ * sync_stream below.
+ *
+ * At the end of the list of chunks, where the type of the next chunk is not
+ * png_IDAT, process the whole stream using the list data to check validity
+ * then return control to the start and rewrite everything.
+ */
+ list = file->idat->idat_list_tail;
+
+ if (list->count == list->length)
+ {
+ list = IDAT_list_extend(list);
+
+ if (list == NULL)
+ stop(file, READ_ERROR_CODE, "out of memory");
+
+ /* Move to the next block */
+ list->count = 0;
+ file->idat->idat_list_tail = list;
+ }
+
+ /* And fill in the next IDAT information buffer. */
+ list->lengths[(list->count)++] = file->chunk->chunk_length;
+
+ /* The type of the next chunk was recorded in the file control structure by
+ * the caller, if this is png_IDAT return 'skip' to the caller.
+ */
+ if (file->type == png_IDAT)
+ return 0; /* skip this for the moment */
+
+ /* This is the final IDAT chunk, so run the tests to check for the too far
+ * back error and possibly optimize the window bits. This means going back
+ * to the start of the first chunk data, which is stored in the original
+ * chunk allocation.
+ */
+ setpos(file->chunk);
+
+ if (zlib_check(file, 0))
+ {
+ struct IDAT *idat;
+ int cmp;
+
+ /* The IDAT stream was successfully uncompressed; see whether it
+ * contained the correct number of bytes of image data.
+ */
+ cmp = uarb_cmp(file->image_bytes, file->image_digits,
+ file->chunk->uncompressed_bytes, file->chunk->uncompressed_digits);
+
+ if (cmp < 0)
+ type_message(file, png_IDAT, "extra uncompressed data");
+
+ else if (cmp > 0)
+ stop(file, LIBPNG_ERROR_CODE, "IDAT: uncompressed data too small");
+
+ /* Return the stream to the start of the first IDAT chunk; the length
+ * is set in the write case below but the input chunk variables must be
+ * set (once) here:
+ */
+ setpos(file->chunk);
+
+ idat = file->idat;
+ idat->idat_cur = idat->idat_list_head;
+ idat->idat_length = idat->idat_cur->lengths[0];
+ idat->idat_count = 0; /* Count of chunks read in current list */
+ idat->idat_index = 0; /* Index into chunk data */
+
+ /* Update the chunk length to the correct value for the IDAT chunk: */
+ file->chunk->chunk_length = rechunk_length(idat);
+
+ /* Change the state to writing IDAT chunks */
+ file->state = STATE_IDAT;
+
+ return 1;
+ }
+
+ else /* Failure to decompress the IDAT stream; give up. */
+ stop(file, ZLIB_ERROR_CODE, "could not uncompress IDAT");
+}
+
+/* ZLIB CONTROL STRUCTURE */
+struct zlib
+{
+ /* ANCESTORS */
+ struct IDAT * idat; /* NOTE: May be NULL */
+ struct chunk * chunk;
+ struct file * file;
+ struct global *global;
+
+ /* GLOBAL ZLIB INFORMATION: SET BY THE CALLER */
+ png_uint_32 rewrite_offset;
+
+ /* GLOBAL ZLIB INFORMATION: SET BY THE ZLIB READ CODE */
+ udigit compressed_bytes[5];
+ int compressed_digits;
+ udigit uncompressed_bytes[5];
+ int uncompressed_digits;
+ int file_bits; /* window bits from the file */
+ int ok_bits; /* Set <16 on a successful read */
+ int cksum; /* Set on a checksum error */
+
+ /* PROTECTED ZLIB INFORMATION: USED BY THE ZLIB ROUTINES */
+ z_stream z;
+ png_uint_32 extra_bytes; /* Count of extra compressed bytes */
+ int state;
+ int rc; /* Last return code */
+ int window_bits; /* 0 if no change */
+ png_byte header[2];
+};
+
+static const char *
+zlib_flevel(struct zlib *zlib)
+{
+ switch (zlib->header[1] >> 6)
+ {
+ case 0: return "supfast";
+ case 1: return "stdfast";
+ case 2: return "default";
+ case 3: return "maximum";
+ default: assert(UNREACHED);
+ }
+
+ return "COMPILER BUG";
+}
+
+static const char *
+zlib_rc(struct zlib *zlib)
+ /* Return a string for the zlib return code */
+{
+ switch (zlib->rc)
+ {
+ case Z_OK: return "Z_OK";
+ case Z_STREAM_END: return "Z_STREAM_END";
+ case Z_NEED_DICT: return "Z_NEED_DICT";
+ case Z_ERRNO: return "Z_ERRNO";
+ case Z_STREAM_ERROR: return "Z_STREAM_ERROR";
+ case Z_DATA_ERROR: return "Z_DATA_ERROR";
+ case Z_MEM_ERROR: return "Z_MEM_ERROR";
+ case Z_BUF_ERROR: return "Z_BUF_ERROR";
+ case Z_VERSION_ERROR: return "Z_VERSION_ERROR";
+ default: return "Z_*INVALID_RC*";
+ }
+}
+
+static void
+zlib_message(struct zlib *zlib, int unexpected)
+ /* Output a message given a zlib rc */
+{
+ if (zlib->global->errors)
+ {
+ const char *reason = zlib->z.msg;
+
+ if (reason == NULL)
+ reason = "[no message]";
+
+ fputs(zlib->file->file_name, stderr);
+ type_sep(stderr);
+ type_name(zlib->chunk->chunk_type, stderr);
+ fprintf(stderr, ": %szlib error: %d (%s) (%s)\n",
+ unexpected ? "unexpected " : "", zlib->rc, zlib_rc(zlib), reason);
+ }
+}
+
+static void
+zlib_end(struct zlib *zlib)
+{
+ /* Output the summary line now; this ensures a summary line always gets
+ * output regardless of the manner of exit.
+ */
+ if (!zlib->global->quiet)
+ {
+ if (zlib->ok_bits < 16) /* stream was read ok */
+ {
+ const char *reason;
+
+ if (zlib->cksum)
+ reason = "CHK"; /* checksum error */
+
+ else if (zlib->ok_bits > zlib->file_bits)
+ reason = "TFB"; /* fixing a too-far-back error */
+
+ else if (zlib->ok_bits == zlib->file_bits)
+ reason = "OK ";
+
+ else
+ reason = "OPT"; /* optimizing window bits */
+
+ /* SUMMARY FORMAT (for a successful zlib inflate):
+ *
+ * IDAT reason flevel file-bits ok-bits compressed uncompressed file
+ */
+ type_name(zlib->chunk->chunk_type, stdout);
+ printf(" %s %s %d %d ", reason, zlib_flevel(zlib), zlib->file_bits,
+ zlib->ok_bits);
+ uarb_print(zlib->compressed_bytes, zlib->compressed_digits, stdout);
+ putc(' ', stdout);
+ uarb_print(zlib->uncompressed_bytes, zlib->uncompressed_digits,
+ stdout);
+ putc(' ', stdout);
+ fputs(zlib->file->file_name, stdout);
+ putc('\n', stdout);
+ }
+
+ else
+ {
+ /* This is a zlib read error; the chunk will be skipped. For an IDAT
+ * stream this will also cause a fatal read error (via stop()).
+ *
+ * SUMMARY FORMAT:
+ *
+ * IDAT SKP flevel file-bits z-rc compressed message file
+ *
+ * z-rc is the zlib failure code; message is the error message with
+ * spaces replaced by '-'. The compressed byte count indicates where
+ * in the zlib stream the error occurred.
+ */
+ type_name(zlib->chunk->chunk_type, stdout);
+ printf(" SKP %s %d %s ", zlib_flevel(zlib), zlib->file_bits,
+ zlib_rc(zlib));
+ uarb_print(zlib->compressed_bytes, zlib->compressed_digits, stdout);
+ putc(' ', stdout);
+ emit_string(zlib->z.msg ? zlib->z.msg : "[no_message]", stdout);
+ putc(' ', stdout);
+ fputs(zlib->file->file_name, stdout);
+ putc('\n', stdout);
+ }
+ }
+
+ if (zlib->state >= 0)
+ {
+ zlib->rc = inflateEnd(&zlib->z);
+
+ if (zlib->rc != Z_OK)
+ zlib_message(zlib, 1/*unexpected*/);
+ }
+
+ CLEAR(*zlib);
+}
+
+static int
+zlib_reset(struct zlib *zlib, int window_bits)
+ /* Reinitializes a zlib with a different window_bits */
+{
+ assert(zlib->state >= 0); /* initialized by zlib_init */
+
+ zlib->z.next_in = Z_NULL;
+ zlib->z.avail_in = 0;
+ zlib->z.next_out = Z_NULL;
+ zlib->z.avail_out = 0;
+
+ zlib->window_bits = window_bits;
+ zlib->compressed_digits = 0;
+ zlib->uncompressed_digits = 0;
+
+ zlib->state = 0; /* initialized, once */
+ zlib->rc = inflateReset2(&zlib->z, 0);
+ if (zlib->rc != Z_OK)
+ {
+ zlib_message(zlib, 1/*unexpected*/);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+zlib_init(struct zlib *zlib, struct IDAT *idat, struct chunk *chunk,
+ int window_bits, png_uint_32 offset)
+ /* Initialize a zlib_control; the result is true/false */
+{
+ CLEAR(*zlib);
+
+ zlib->idat = idat;
+ zlib->chunk = chunk;
+ zlib->file = chunk->file;
+ zlib->global = chunk->global;
+ zlib->rewrite_offset = offset; /* never changed for this zlib */
+
+ /* *_out does not need to be set: */
+ zlib->z.next_in = Z_NULL;
+ zlib->z.avail_in = 0;
+ zlib->z.zalloc = Z_NULL;
+ zlib->z.zfree = Z_NULL;
+ zlib->z.opaque = Z_NULL;
+
+ zlib->state = -1;
+ zlib->window_bits = window_bits;
+
+ zlib->compressed_digits = 0;
+ zlib->uncompressed_digits = 0;
+
+ /* These values are sticky across reset (in addition to the stuff in the
+ * first block, which is actually constant.)
+ */
+ zlib->file_bits = 24;
+ zlib->ok_bits = 16; /* unset */
+ zlib->cksum = 0; /* set when a checksum error is detected */
+
+ /* '0' means use the header; inflateInit2 should always succeed because it
+ * does nothing apart from allocating the internal zstate.
+ */
+ zlib->rc = inflateInit2(&zlib->z, 0);
+ if (zlib->rc != Z_OK)
+ {
+ zlib_message(zlib, 1/*unexpected*/);
+ return 0;
+ }
+
+ else
+ {
+ zlib->state = 0; /* initialized */
+ return 1;
+ }
+}
+
+static int
+max_window_bits(uarbc size, int ndigits)
+ /* Return the zlib stream window bits required for data of the given size. */
+{
+ png_uint_16 cb;
+
+ if (ndigits > 1)
+ return 15;
+
+ cb = size[0];
+
+ if (cb > 16384) return 15;
+ if (cb > 8192) return 14;
+ if (cb > 4096) return 13;
+ if (cb > 2048) return 12;
+ if (cb > 1024) return 11;
+ if (cb > 512) return 10;
+ if (cb > 256) return 9;
+ return 8;
+}
+
+static int
+zlib_advance(struct zlib *zlib, png_uint_32 nbytes)
+ /* Read nbytes compressed bytes; the stream will be initialized if required.
+ * Bytes are always being reread and errors are fatal. The return code is as
+ * follows:
+ *
+ * -1: saw the "too far back" error
+ * 0: ok, keep going
+ * 1: saw Z_STREAM_END (zlib->extra_bytes indicates too much data)
+ * 2: a zlib error that cannot be corrected (error message already
+ * output if required.)
+ */
+# define ZLIB_TOO_FAR_BACK (-1)
+# define ZLIB_OK 0
+# define ZLIB_STREAM_END 1
+# define ZLIB_FATAL 2
+{
+ int state = zlib->state;
+ int endrc = ZLIB_OK;
+ png_uint_32 in_bytes = 0;
+ struct file *file = zlib->file;
+
+ assert(state >= 0);
+
+ while (in_bytes < nbytes && endrc == ZLIB_OK)
+ {
+ png_uint_32 out_bytes;
+ int flush;
+ png_byte bIn = reread_byte(file);
+ png_byte bOut;
+
+ switch (state)
+ {
+ case 0: /* first header byte */
+ {
+ int file_bits = 8+(bIn >> 4);
+ int new_bits = zlib->window_bits;
+
+ zlib->file_bits = file_bits;
+
+ /* Check against the existing value - it may not need to be
+ * changed. Note that a bogus file_bits is allowed through once,
+ * to see if it works, but the window_bits value is set to 15,
+ * the maximum.
+ */
+ if (new_bits == 0) /* no change */
+ zlib->window_bits = ((file_bits > 15) ? 15 : file_bits);
+
+ else if (new_bits != file_bits) /* rewrite required */
+ bIn = (png_byte)((bIn & 0xf) + ((new_bits-8) << 4));
+ }
+
+ zlib->header[0] = bIn;
+ zlib->state = state = 1;
+ break;
+
+ case 1: /* second header byte */
+ {
+ int b2 = bIn & 0xe0; /* top 3 bits */
+
+ /* The checksum calculation, on the first 11 bits: */
+ b2 += 0x1f - ((zlib->header[0] << 8) + b2) % 0x1f;
+
+ /* Update the checksum byte if required: */
+ if (bIn != b2)
+ {
+ /* If the first byte wasn't changed this indicates an error in
+ * the checksum calculation; signal this by setting 'cksum'.
+ */
+ if (zlib->file_bits == zlib->window_bits)
+ zlib->cksum = 1;
+
+ bIn = (png_byte)b2;
+ }
+ }
+
+ zlib->header[1] = bIn;
+ zlib->state = state = 2;
+ break;
+
+ default: /* After the header bytes */
+ break;
+ }
+
+ /* For some streams, perhaps only those compressed with 'superfast
+ * compression' (which results in a lot of copying) Z_BUF_ERROR can happen
+ * immediately after all output has been flushed on the next input byte.
+ * This is handled below when Z_BUF_ERROR is detected by adding an output
+ * byte.
+ */
+ zlib->z.next_in = &bIn;
+ zlib->z.avail_in = 1;
+ zlib->z.next_out = &bOut;
+ zlib->z.avail_out = 0; /* Initially */
+
+ /* Initially use Z_NO_FLUSH in an attempt to persuade zlib to look at this
+ * byte without confusing what is going on with output.
+ */
+ flush = Z_NO_FLUSH;
+ out_bytes = 0;
+
+ /* NOTE: expression 3 is only evaluted on 'continue', because of the
+ * 'break' at the end of this loop below.
+ */
+ for (;endrc == ZLIB_OK;
+ flush = Z_SYNC_FLUSH,
+ zlib->z.next_out = &bOut,
+ zlib->z.avail_out = 1,
+ ++out_bytes)
+ {
+ zlib->rc = inflate(&zlib->z, flush);
+ out_bytes -= zlib->z.avail_out;
+
+ switch (zlib->rc)
+ {
+ case Z_BUF_ERROR:
+ if (zlib->z.avail_out == 0)
+ continue; /* Try another output byte. */
+
+ if (zlib->z.avail_in == 0)
+ break; /* Try another input byte */
+
+ /* Both avail_out and avail_in are 1 yet zlib returned a code
+ * indicating no progress was possible. This is unexpected.
+ */
+ zlib_message(zlib, 1/*unexpected*/);
+ endrc = ZLIB_FATAL; /* stop processing */
+ break;
+
+ case Z_OK:
+ /* Zlib is supposed to have made progress: */
+ assert(zlib->z.avail_out == 0 || zlib->z.avail_in == 0);
+ continue;
+
+ case Z_STREAM_END:
+ /* This is the successful end. */
+ zlib->state = 3; /* end of stream */
+ endrc = ZLIB_STREAM_END;
+ break;
+
+ case Z_NEED_DICT:
+ zlib_message(zlib, 0/*stream error*/);
+ endrc = ZLIB_FATAL;
+ break;
+
+ case Z_DATA_ERROR:
+ /* The too far back error can be corrected, others cannot: */
+ if (zlib->z.msg != NULL &&
+ strcmp(zlib->z.msg, "invalid distance too far back") == 0)
+ {
+ endrc = ZLIB_TOO_FAR_BACK;
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ zlib_message(zlib, 0/*stream error*/);
+ endrc = ZLIB_FATAL;
+ break;
+ } /* switch (inflate rc) */
+
+ /* Control gets here when further output is not possible; endrc may
+ * still be ZLIB_OK if more input is required.
+ */
+ break;
+ } /* for (output bytes) */
+
+ /* Keep a running count of output byte produced: */
+ zlib->uncompressed_digits = uarb_add32(zlib->uncompressed_bytes,
+ zlib->uncompressed_digits, out_bytes);
+
+ /* Keep going, the loop will terminate when endrc is no longer set to
+ * ZLIB_OK or all the input bytes have been consumed; meanwhile keep
+ * adding input bytes.
+ */
+ assert(zlib->z.avail_in == 0 || endrc != ZLIB_OK);
+
+ in_bytes += 1 - zlib->z.avail_in;
+ } /* while (input bytes) */
+
+ assert(in_bytes == nbytes || endrc != ZLIB_OK);
+
+ /* Update the running total of input bytes consumed */
+ zlib->compressed_digits = uarb_add32(zlib->compressed_bytes,
+ zlib->compressed_digits, in_bytes - zlib->z.avail_in);
+
+ /* At the end of the stream update the chunk with the accumulated
+ * information if it is an improvement:
+ */
+ if (endrc == ZLIB_STREAM_END && zlib->window_bits < zlib->ok_bits)
+ {
+ struct chunk *chunk = zlib->chunk;
+
+ chunk->uncompressed_digits = uarb_copy(chunk->uncompressed_bytes,
+ zlib->uncompressed_bytes, zlib->uncompressed_digits);
+ chunk->compressed_digits = uarb_copy(chunk->compressed_bytes,
+ zlib->compressed_bytes, zlib->compressed_digits);
+ chunk->rewrite_buffer[0] = zlib->header[0];
+ chunk->rewrite_buffer[1] = zlib->header[1];
+
+ if (zlib->window_bits != zlib->file_bits || zlib->cksum)
+ {
+ /* A rewrite is required */
+ chunk->rewrite_offset = zlib->rewrite_offset;
+ chunk->rewrite_length = 2;
+ }
+
+ else
+ {
+ chunk->rewrite_offset = 0;
+ chunk->rewrite_length = 0;
+ }
+
+ if (in_bytes < nbytes)
+ chunk_message(chunk, "extra compressed data");
+
+ zlib->extra_bytes = nbytes - in_bytes;
+ zlib->ok_bits = zlib->window_bits;
+ }
+
+ return endrc;
+}
+
+static int
+zlib_run(struct zlib *zlib)
+ /* Like zlib_advance but also handles a stream of IDAT chunks. */
+{
+ /* The 'extra_bytes' field is set by zlib_advance if there is extra
+ * compressed data in the chunk it handles (if it sees Z_STREAM_END before
+ * all the input data has been used.) This function uses the value to update
+ * the correct chunk length, so the problem should only ever be detected once
+ * for each chunk. zlib_advance outputs the error message, though see the
+ * IDAT specific check below.
+ */
+ zlib->extra_bytes = 0;
+
+ if (zlib->idat != NULL)
+ {
+ struct IDAT_list *list = zlib->idat->idat_list_head;
+ struct IDAT_list *last = zlib->idat->idat_list_tail;
+ int skip = 0;
+
+ /* 'rewrite_offset' is the offset of the LZ data within the chunk, for
+ * IDAT it should be 0:
+ */
+ assert(zlib->rewrite_offset == 0);
+
+ /* Process each IDAT_list in turn; the caller has left the stream
+ * positioned at the start of the first IDAT chunk data.
+ */
+ for (;;)
+ {
+ const unsigned int count = list->count;
+ unsigned int i;
+
+ for (i = 0; i<count; ++i)
+ {
+ int rc;
+
+ if (skip > 0) /* Skip CRC and next IDAT header */
+ skip_12(zlib->file);
+
+ skip = 12; /* for the next time */
+
+ rc = zlib_advance(zlib, list->lengths[i]);
+
+ switch (rc)
+ {
+ case ZLIB_OK: /* keep going */
+ break;
+
+ case ZLIB_STREAM_END: /* stop */
+ /* There may be extra chunks; if there are and one of them is
+ * not zero length output the 'extra data' message. Only do
+ * this check if errors are being output.
+ */
+ if (zlib->global->errors && zlib->extra_bytes == 0)
+ {
+ struct IDAT_list *check = list;
+ int j = i+1, jcount = count;
+
+ for (;;)
+ {
+ for (; j<jcount; ++j)
+ if (check->lengths[j] > 0)
+ {
+ chunk_message(zlib->chunk,
+ "extra compressed data");
+ goto end_check;
+ }
+
+ if (check == last)
+ break;
+
+ check = check->next;
+ jcount = check->count;
+ j = 0;
+ }
+ }
+
+ end_check:
+ /* Terminate the list at the current position, reducing the
+ * length of the last IDAT too if required.
+ */
+ list->lengths[i] -= zlib->extra_bytes;
+ list->count = i+1;
+ zlib->idat->idat_list_tail = list;
+ /* FALL THROUGH */
+
+ default:
+ return rc;
+ }
+ }
+
+ /* At the end of the compressed data and Z_STREAM_END was not seen. */
+ if (list == last)
+ return ZLIB_OK;
+
+ list = list->next;
+ }
+ }
+
+ else
+ {
+ struct chunk *chunk = zlib->chunk;
+ int rc;
+
+ assert(zlib->rewrite_offset < chunk->chunk_length);
+
+ rc = zlib_advance(zlib, chunk->chunk_length - zlib->rewrite_offset);
+
+ /* The extra bytes in the chunk are handled now by adjusting the chunk
+ * length to exclude them; the zlib data is always stored at the end of
+ * the PNG chunk (although clearly this is not necessary.) zlib_advance
+ * has already output a warning message.
+ */
+ chunk->chunk_length -= zlib->extra_bytes;
+ return rc;
+ }
+}
+
+static int /* global function; not a member function */
+zlib_check(struct file *file, png_uint_32 offset)
+ /* Check the stream of zlib compressed data in either idat (if given) or (if
+ * not) chunk. In fact it is zlib_run that handles the difference in reading
+ * a single chunk and a list of IDAT chunks.
+ *
+ * In either case the input file must be positioned at the first byte of zlib
+ * compressed data (the first header byte).
+ *
+ * The return value is true on success, including the case where the zlib
+ * header may need to be rewritten, and false on an unrecoverable error.
+ *
+ * In the case of IDAT chunks 'offset' should be 0.
+ */
+{
+ fpos_t start_pos;
+ struct zlib zlib;
+
+ /* Record the start of the LZ data to allow a re-read. */
+ file_getpos(file, &start_pos);
+
+ /* First test the existing (file) window bits: */
+ if (zlib_init(&zlib, file->idat, file->chunk, 0/*window bits*/, offset))
+ {
+ int min_bits, max_bits, rc;
+
+ /* The first run using the existing window bits. */
+ rc = zlib_run(&zlib);
+
+ switch (rc)
+ {
+ case ZLIB_TOO_FAR_BACK:
+ /* too far back error */
+ file->status_code |= TOO_FAR_BACK;
+ min_bits = zlib.window_bits + 1;
+ max_bits = 15;
+ break;
+
+ case ZLIB_STREAM_END:
+ if (!zlib.global->optimize_zlib &&
+ zlib.window_bits == zlib.file_bits && !zlib.cksum)
+ {
+ /* The trivial case where the stream is ok and optimization was
+ * not requested.
+ */
+ zlib_end(&zlib);
+ return 1;
+ }
+
+ max_bits = max_window_bits(zlib.uncompressed_bytes,
+ zlib.uncompressed_digits);
+ if (zlib.ok_bits < max_bits)
+ max_bits = zlib.ok_bits;
+ min_bits = 8;
+
+ /* cksum is set if there is an error in the zlib header checksum
+ * calculation in the original file (and this may be the only reason
+ * a rewrite is required). We can't rely on the file window bits in
+ * this case, so do the optimization anyway.
+ */
+ if (zlib.cksum)
+ chunk_message(zlib.chunk, "zlib checkum");
+ break;
+
+
+ case ZLIB_OK:
+ /* Truncated stream; unrecoverable, gets converted to ZLIB_FATAL */
+ zlib.z.msg = PNGZ_MSG_CAST("[truncated]");
+ zlib_message(&zlib, 0/*expected*/);
+ /* FALL THROUGH */
+
+ default:
+ /* Unrecoverable error; skip the chunk; a zlib_message has already
+ * been output.
+ */
+ zlib_end(&zlib);
+ return 0;
+ }
+
+ /* Optimize window bits or fix a too-far-back error. min_bits and
+ * max_bits have been set appropriately, ok_bits records the bit value
+ * known to work.
+ */
+ while (min_bits < max_bits || max_bits < zlib.ok_bits/*if 16*/)
+ {
+ int test_bits = (min_bits + max_bits) >> 1;
+
+ if (zlib_reset(&zlib, test_bits))
+ {
+ file_setpos(file, &start_pos);
+ rc = zlib_run(&zlib);
+
+ switch (rc)
+ {
+ case ZLIB_TOO_FAR_BACK:
+ min_bits = test_bits+1;
+ if (min_bits > max_bits)
+ {
+ /* This happens when the stream really is damaged and it
+ * contains a distance code that addresses bytes before
+ * the start of the uncompressed data.
+ */
+ assert(test_bits == 15);
+
+ /* Output the error that wasn't output before: */
+ if (zlib.z.msg == NULL)
+ zlib.z.msg = PNGZ_MSG_CAST(
+ "invalid distance too far back");
+ zlib_message(&zlib, 0/*stream error*/);
+ zlib_end(&zlib);
+ return 0;
+ }
+ break;
+
+ case ZLIB_STREAM_END: /* success */
+ max_bits = test_bits;
+ break;
+
+ default:
+ /* A fatal error; this happens if a too-far-back error was
+ * hiding a more serious error, zlib_advance has already
+ * output a zlib_message.
+ */
+ zlib_end(&zlib);
+ return 0;
+ }
+ }
+
+ else /* inflateReset2 failed */
+ {
+ zlib_end(&zlib);
+ return 0;
+ }
+ }
+
+ /* The loop guarantees this */
+ assert(zlib.ok_bits == max_bits);
+ zlib_end(&zlib);
+ return 1;
+ }
+
+ else /* zlib initialization failed - skip the chunk */
+ {
+ zlib_end(&zlib);
+ return 0;
+ }
+}
+
+/***************************** LIBPNG CALLBACKS *******************************/
+/* The strategy here is to run a regular libpng PNG file read but examine the
+ * input data (from the file) before passing it to libpng so as to be aware of
+ * the state we expect libpng to be in. Warning and error callbacks are also
+ * intercepted so that they can be quieted and interpreted. Interpretation
+ * depends on a somewhat risky string match for known error messages; let us
+ * hope that this can be fixed in the next version of libpng.
+ *
+ * The control structure is pointed to by the libpng error pointer. It contains
+ * that set of structures which must persist across multiple read callbacks,
+ * which is pretty much everything except the 'zlib' control structure.
+ *
+ * The file structure is instantiated in the caller of the per-file routine, but
+ * the per-file routine contains the chunk and IDAT control structures.
+ */
+/* The three routines read_chunk, process_chunk and sync_stream can only be
+ * called via a call to read_chunk and only exit at a return from process_chunk.
+ * These routines could have been written as one confusing large routine,
+ * instead this code relies on the compiler to do tail call elimination. The
+ * possible calls are as follows:
+ *
+ * read_chunk
+ * -> sync_stream
+ * -> process_chunk
+ * -> process_chunk
+ * -> read_chunk
+ * returns
+ */
+static void read_chunk(struct file *file);
+static void
+process_chunk(struct file *file, png_uint_32 file_crc, png_uint_32 next_length,
+ png_uint_32 next_type)
+ /* Called when the chunk data has been read, next_length and next_type
+ * will be set for the next chunk (or 0 if this is IEND).
+ *
+ * When this routine returns, chunk_length and chunk_type will be set for the
+ * next chunk to write because if a chunk is skipped this return calls back
+ * to read_chunk.
+ */
+{
+ const png_uint_32 type = file->type;
+
+ if (file->global->verbose > 1)
+ {
+ fputs(" ", stderr);
+ type_name(file->type, stderr);
+ fprintf(stderr, " %lu 0x%.8x 0x%.8x\n", (unsigned long)file->length,
+ file->crc ^ 0xffffffff, file_crc);
+ }
+
+ /* The basic structure seems correct but the CRC may not match, in this
+ * case assume that it is simply a bad CRC, either wrongly calculated or
+ * because of damaged stream data.
+ */
+ if ((file->crc ^ 0xffffffff) != file_crc)
+ {
+ /* The behavior is set by the 'skip' setting; if it is anything other
+ * than SKIP_BAD_CRC ignore the bad CRC and return the chunk, with a
+ * corrected CRC and possibly processed, to libpng. Otherwise skip the
+ * chunk, which will result in a fatal error if the chunk is critical.
+ */
+ file->status_code |= CRC_ERROR;
+
+ /* Ignore the bad CRC */
+ if (file->global->skip != SKIP_BAD_CRC)
+ type_message(file, type, "bad CRC");
+
+ /* This will cause an IEND with a bad CRC to stop */
+ else if (CRITICAL(type))
+ stop(file, READ_ERROR_CODE, "bad CRC in critical chunk");
+
+ else
+ {
+ type_message(file, type, "skipped: bad CRC");
+
+ /* NOTE: this cannot be reached for IEND because it is critical. */
+ goto skip_chunk;
+ }
+ }
+
+ /* Check for other 'skip' cases and handle these; these only apply to
+ * ancillary chunks (and not tRNS, which should probably have been a critical
+ * chunk.)
+ */
+ if (skip_chunk_type(file->global, type))
+ goto skip_chunk;
+
+ /* The chunk may still be skipped if problems are detected in the LZ data,
+ * however the LZ data check requires a chunk. Handle this by instantiating
+ * a chunk unless an IDAT is already instantiated (IDAT control structures
+ * instantiate their own chunk.)
+ */
+ if (type != png_IDAT)
+ file->alloc(file, 0/*chunk*/);
+
+ else if (file->idat == NULL)
+ file->alloc(file, 1/*IDAT*/);
+
+ else
+ {
+ /* The chunk length must be updated for process_IDAT */
+ assert(file->chunk != NULL);
+ assert(file->chunk->chunk_type == png_IDAT);
+ file->chunk->chunk_length = file->length;
+ }
+
+ /* Record the 'next' information too, now that the original values for
+ * this chunk have been copied. Notice that the IDAT chunks only make a
+ * copy of the position of the first chunk, this is fine - process_IDAT does
+ * not need the position of this chunk.
+ */
+ file->length = next_length;
+ file->type = next_type;
+ getpos(file);
+
+ /* Do per-type processing, note that if this code does not return from the
+ * function the chunk will be skipped. The rewrite is cancelled here so that
+ * it can be set in the per-chunk processing.
+ */
+ file->chunk->rewrite_length = 0;
+ file->chunk->rewrite_offset = 0;
+ switch (type)
+ {
+ default:
+ return;
+
+ case png_IHDR:
+ /* Read this now and update the control structure with the information
+ * it contains. The header is validated completely to ensure this is a
+ * PNG.
+ */
+ {
+ struct chunk *chunk = file->chunk;
+
+ if (chunk->chunk_length != 13)
+ stop_invalid(file, "IHDR length");
+
+ /* Read all the IHDR information and validate it. */
+ setpos(chunk);
+ file->width = reread_4(file);
+ file->height = reread_4(file);
+ file->bit_depth = reread_byte(file);
+ file->color_type = reread_byte(file);
+ file->compression_method = reread_byte(file);
+ file->filter_method = reread_byte(file);
+ file->interlace_method = reread_byte(file);
+
+ /* This validates all the fields, and calls stop_invalid if
+ * there is a problem.
+ */
+ calc_image_size(file);
+ }
+ return;
+
+ /* Ancillary chunks that require further processing: */
+ case png_zTXt: case png_iCCP:
+ if (process_zTXt_iCCP(file))
+ return;
+ chunk_end(&file->chunk);
+ file_setpos(file, &file->data_pos);
+ break;
+
+ case png_iTXt:
+ if (process_iTXt(file))
+ return;
+ chunk_end(&file->chunk);
+ file_setpos(file, &file->data_pos);
+ break;
+
+ case png_IDAT:
+ if (process_IDAT(file))
+ return;
+ /* First pass: */
+ assert(next_type == png_IDAT);
+ break;
+ }
+
+ /* Control reaches this point if the chunk must be skipped. For chunks other
+ * than IDAT this means that the zlib compressed data is fatally damanged and
+ * the chunk will not be passed to libpng. For IDAT it means that the end of
+ * the IDAT stream has not yet been reached and we must handle the next
+ * (IDAT) chunk. If the LZ data in an IDAT stream cannot be read 'stop' must
+ * be used to halt parsing of the PNG.
+ */
+ read_chunk(file);
+ return;
+
+ /* This is the generic code to skip the current chunk; simply jump to the
+ * next one.
+ */
+skip_chunk:
+ file->length = next_length;
+ file->type = next_type;
+ getpos(file);
+ read_chunk(file);
+}
+
+static png_uint_32
+get32(png_bytep buffer, int offset)
+ /* Read a 32-bit value from an 8-byte circular buffer (used only below).
+ */
+{
+ return
+ (buffer[ offset & 7] << 24) +
+ (buffer[(offset+1) & 7] << 16) +
+ (buffer[(offset+2) & 7] << 8) +
+ (buffer[(offset+3) & 7] );
+}
+
+static void
+sync_stream(struct file *file)
+ /* The stream seems to be messed up, attempt to resync from the current chunk
+ * header. Executes stop on a fatal error, otherwise calls process_chunk.
+ */
+{
+ png_uint_32 file_crc;
+
+ file->status_code |= STREAM_ERROR;
+
+ if (file->global->verbose)
+ {
+ fputs(" SYNC ", stderr);
+ type_name(file->type, stderr);
+ putc('\n', stderr);
+ }
+
+ /* Return to the start of the chunk data */
+ file_setpos(file, &file->data_pos);
+ file->read_count = 8;
+
+ if (read_4(file, &file_crc) == 4) /* else completely truncated */
+ {
+ /* Ignore the recorded chunk length, proceed through the data looking for
+ * a leading sequence of bytes that match the CRC in the following four
+ * bytes. Each time a match is found check the next 8 bytes for a valid
+ * length, chunk-type pair.
+ */
+ png_uint_32 length;
+ png_uint_32 type = file->type;
+ png_uint_32 crc = crc_init_4(type);
+ png_byte buffer[8];
+ unsigned int nread = 0, nused = 0;
+
+ for (length=0; length <= 0x7fffffff; ++length)
+ {
+ int ch;
+
+ if ((crc ^ 0xffffffff) == file_crc)
+ {
+ /* A match on the CRC; for IEND this is sufficient, but for anything
+ * else expect a following chunk header.
+ */
+ if (type == png_IEND)
+ {
+ file->length = length;
+ process_chunk(file, file_crc, 0, 0);
+ return;
+ }
+
+ else
+ {
+ /* Need 8 bytes */
+ while (nread < 8+nused)
+ {
+ ch = read_byte(file);
+ if (ch == EOF)
+ goto truncated;
+ buffer[(nread++) & 7] = (png_byte)ch;
+ }
+
+ /* Prevent overflow */
+ nread -= nused & ~7;
+ nused -= nused & ~7; /* or, nused &= 7 ;-) */
+
+ /* Examine the 8 bytes for a valid chunk header. */
+ {
+ png_uint_32 next_length = get32(buffer, nused);
+
+ if (next_length < 0x7fffffff)
+ {
+ png_uint_32 next_type = get32(buffer, nused+4);
+
+ if (chunk_type_valid(next_type))
+ {
+ file->read_count -= 8;
+ process_chunk(file, file_crc, next_length, next_type);
+ return;
+ }
+ }
+
+ /* Not valid, keep going. */
+ }
+ }
+ }
+
+ /* This catches up with the circular buffer which gets filled above
+ * while checking a chunk header. This code is slightly tricky - if
+ * the chunk_type is IEND the buffer will never be used, if it is not
+ * the code will always read ahead exactly 8 bytes and pass this on to
+ * process_chunk. So the invariant that IEND leaves the file position
+ * after the IEND CRC and other chunk leave it after the *next* chunk
+ * header is not broken.
+ */
+ if (nread <= nused)
+ {
+ ch = read_byte(file);
+
+ if (ch == EOF)
+ goto truncated;
+ }
+
+ else
+ ch = buffer[(++nused) & 7];
+
+ crc = crc_one_byte(crc, file_crc >> 24);
+ file_crc = (file_crc << 8) + ch;
+ }
+
+ /* Control gets to here if when 0x7fffffff bytes (plus 8) have been read,
+ * ok, treat this as a damaged stream too:
+ */
+ }
+
+truncated:
+ stop(file, READ_ERROR_CODE, "damaged PNG stream");
+}
+
+static void
+read_chunk(struct file *file)
+ /* On entry file::data_pos must be set to the position of the first byte
+ * of the chunk data *and* the input file must be at this position. This
+ * routine (via process_chunk) instantiates a chunk or IDAT control structure
+ * based on file::length and file::type and also resets these fields and
+ * file::data_pos for the chunk after this one. For an IDAT chunk the whole
+ * stream of IDATs will be read, until something other than an IDAT is
+ * encountered, and the file fields will be set for the chunk after the end
+ * of the stream of IDATs.
+ *
+ * For IEND the file::type field will be set to 0, and nothing beyond the end
+ * of the IEND chunk will have been read.
+ */
+{
+ png_uint_32 length = file->length;
+ png_uint_32 type = file->type;
+
+ /* After IEND file::type is set to 0, if libpng attempts to read
+ * more data at this point this is a bug in libpng.
+ */
+ if (type == 0)
+ stop(file, UNEXPECTED_ERROR_CODE, "read beyond IEND");
+
+ if (file->global->verbose > 2)
+ {
+ fputs(" ", stderr);
+ type_name(type, stderr);
+ fprintf(stderr, " %lu\n", (unsigned long)length);
+ }
+
+ /* Start the read_crc calculation with the chunk type, then read to the end
+ * of the chunk data (without processing it in any way) to check that it is
+ * all there and calculate the CRC.
+ */
+ file->crc = crc_init_4(type);
+ if (crc_read_many(file, length)) /* else it was truncated */
+ {
+ png_uint_32 file_crc; /* CRC read from file */
+ unsigned int nread = read_4(file, &file_crc);
+
+ if (nread == 4)
+ {
+ if (type != png_IEND) /* do not read beyond IEND */
+ {
+ png_uint_32 next_length;
+
+ nread += read_4(file, &next_length);
+ if (nread == 8 && next_length <= 0x7fffffff)
+ {
+ png_uint_32 next_type;
+
+ nread += read_4(file, &next_type);
+
+ if (nread == 12 && chunk_type_valid(next_type))
+ {
+ /* Adjust the read count back to the correct value for this
+ * chunk.
+ */
+ file->read_count -= 8;
+ process_chunk(file, file_crc, next_length, next_type);
+ return;
+ }
+ }
+ }
+
+ else /* IEND */
+ {
+ process_chunk(file, file_crc, 0, 0);
+ return;
+ }
+ }
+ }
+
+ /* Control gets to here if the the stream seems invalid or damaged in some
+ * way. Either there was a problem reading all the expected data (this
+ * chunk's data, its CRC and the length and type of the next chunk) or the
+ * next chunk length/type are invalid. Notice that the cases that end up
+ * here all correspond to cases that would otherwise terminate the read of
+ * the PNG file.
+ */
+ sync_stream(file);
+}
+
+/* This returns a file* from a png_struct in an implementation specific way. */
+static struct file *get_control(png_const_structrp png_ptr);
+
+static void PNGCBAPI
+error_handler(png_structp png_ptr, png_const_charp message)
+{
+ stop(get_control(png_ptr), LIBPNG_ERROR_CODE, message);
+}
+
+static void PNGCBAPI
+warning_handler(png_structp png_ptr, png_const_charp message)
+{
+ struct file *file = get_control(png_ptr);
+
+ if (file->global->warnings)
+ emit_error(file, LIBPNG_WARNING_CODE, message);
+}
+
+/* Read callback - this is where the work gets done to check the stream before
+ * passing it to libpng
+ */
+static void PNGCBAPI
+read_callback(png_structp png_ptr, png_bytep buffer, size_t count)
+ /* Return 'count' bytes to libpng in 'buffer' */
+{
+ struct file *file = get_control(png_ptr);
+ png_uint_32 type, length; /* For the chunk be *WRITTEN* */
+ struct chunk *chunk;
+
+ /* libpng should always ask for at least one byte */
+ if (count == 0)
+ stop(file, UNEXPECTED_ERROR_CODE, "read callback for 0 bytes");
+
+ /* The callback always reads ahead by 8 bytes - the signature or chunk header
+ * - these bytes are stored in chunk_length and chunk_type. This block is
+ * executed once for the signature and once for the first chunk right at the
+ * start.
+ */
+ if (file->read_count < 8)
+ {
+ assert(file->read_count == 0);
+ assert((file->status_code & TRUNCATED) == 0);
+
+ (void)read_4(file, &file->length);
+
+ if (file->read_count == 4)
+ (void)read_4(file, &file->type);
+
+ if (file->read_count < 8)
+ {
+ assert((file->status_code & TRUNCATED) != 0);
+ stop(file, READ_ERROR_CODE, "not a PNG (too short)");
+ }
+
+ if (file->state == STATE_SIGNATURE)
+ {
+ if (file->length != sig1 || file->type != sig2)
+ stop(file, LIBPNG_ERROR_CODE, "not a PNG (signature)");
+
+ /* Else write it (this is the initialization of write_count, prior to
+ * this it contains CLEAR garbage.)
+ */
+ file->write_count = 0;
+ }
+
+ else
+ {
+ assert(file->state == STATE_CHUNKS);
+
+ /* The first chunk must be a well formed IHDR (this could be relaxed to
+ * use the checks in process_chunk, but that seems unnecessary.)
+ */
+ if (file->length != 13 || file->type != png_IHDR)
+ stop(file, LIBPNG_ERROR_CODE, "not a PNG (IHDR)");
+
+ /* The position of the data must be stored too */
+ getpos(file);
+ }
+ }
+
+ /* Retrieve previous state (because the read callbacks are made pretty much
+ * byte-by-byte in the sequential reader prior to 1.7).
+ */
+ chunk = file->chunk;
+
+ if (chunk != NULL)
+ {
+ length = chunk->chunk_length;
+ type = chunk->chunk_type;
+ }
+
+ else
+ {
+ /* This is the signature case; for IDAT and other chunks these values will
+ * be overwritten when read_chunk is called below.
+ */
+ length = file->length;
+ type = file->type;
+ }
+
+ do
+ {
+ png_uint_32 b;
+
+ /* Complete the read of a chunk; as a side effect this also instantiates
+ * a chunk control structure and sets the file length/type/data_pos fields
+ * for the *NEXT* chunk header.
+ *
+ * NOTE: at an IDAT any following IDAT chunks will also be read and the
+ * next_ fields will refer to the chunk after the last IDAT.
+ *
+ * NOTE: read_chunk only returns when it has read a chunk that must now be
+ * written.
+ */
+ if (file->state != STATE_SIGNATURE && chunk == NULL)
+ {
+ assert(file->read_count == 8);
+ assert(file->idat == NULL);
+ read_chunk(file);
+ chunk = file->chunk;
+ assert(chunk != NULL);
+
+ /* Do the initialization that was not done before. */
+ length = chunk->chunk_length;
+ type = chunk->chunk_type;
+
+ /* And start writing the new chunk. */
+ file->write_count = 0;
+ }
+
+ /* The chunk_ fields describe a chunk that must be written, or hold the
+ * signature. Write the header first. In the signature case this
+ * rewrites the signature.
+ */
+ switch (file->write_count)
+ {
+ case 0: b = length >> 24; break;
+ case 1: b = length >> 16; break;
+ case 2: b = length >> 8; break;
+ case 3: b = length ; break;
+
+ case 4: b = type >> 24; break;
+ case 5: b = type >> 16; break;
+ case 6: b = type >> 8; break;
+ case 7: b = type ; break;
+
+ case 8:
+ /* The header has been written. If this is really the signature
+ * that's all that is required and we can go to normal chunk
+ * processing.
+ */
+ if (file->state == STATE_SIGNATURE)
+ {
+ /* The signature has been written, the tail call to read_callback
+ * below (it's just a goto to the start with a decent compiler)
+ * will read the IHDR header ahead and validate it.
+ */
+ assert(length == sig1 && type == sig2);
+ file->read_count = 0; /* Forces a header read */
+ file->state = STATE_CHUNKS; /* IHDR: checked above */
+ read_callback(png_ptr, buffer, count);
+ return;
+ }
+
+ else
+ {
+ assert(chunk != NULL);
+
+ /* Set up for write, notice that repositioning the input stream
+ * is only necessary if something is to be read from it. Also
+ * notice that for the IDAT stream this must only happen once -
+ * on the first IDAT - to get back to the start of the list and
+ * this is done inside process_IDAT:
+ */
+ chunk->write_crc = crc_init_4(type);
+ if (file->state != STATE_IDAT && length > 0)
+ setpos(chunk);
+ }
+ /* FALL THROUGH */
+
+ default:
+ assert(chunk != NULL);
+
+ /* NOTE: the arithmetic below overflows and gives a large positive
+ * png_uint_32 value until the whole chunk data has been written.
+ */
+ switch (file->write_count - length)
+ {
+ /* Write the chunk data, normally this just comes from
+ * the file. The only exception is for that part of a
+ * chunk which is zlib data and which must be rewritten,
+ * and IDAT chunks which can be completely
+ * reconstructed.
+ */
+ default:
+ if (file->state == STATE_IDAT)
+ {
+ struct IDAT *idat = file->idat;
+
+ assert(idat != NULL);
+
+ /* Read an IDAT byte from the input stream of IDAT chunks.
+ * Because the IDAT stream can be re-chunked this stream is
+ * held in the struct IDAT members. The chunk members, in
+ * particular chunk_length (and therefore the length local)
+ * refer to the output chunk.
+ */
+ while (idat->idat_index >= idat->idat_length)
+ {
+ /* Advance one chunk */
+ struct IDAT_list *cur = idat->idat_cur;
+
+ assert(idat->idat_index == idat->idat_length);
+ assert(cur != NULL && cur->count > 0);
+
+ /* NOTE: IDAT_list::count here, not IDAT_list::length */
+ if (++(idat->idat_count) >= cur->count)
+ {
+ assert(idat->idat_count == cur->count);
+
+ /* Move on to the next IDAT_list: */
+ cur = cur->next;
+
+ /* This is an internal error - read beyond the end of
+ * the pre-calculated stream.
+ */
+ if (cur == NULL || cur->count == 0)
+ stop(file, UNEXPECTED_ERROR_CODE,
+ "read beyond end of IDAT");
+
+ idat->idat_count = 0;
+ idat->idat_cur = cur;
+ }
+
+ idat->idat_index = 0;
+ /* Zero length IDAT chunks are permitted, so the length
+ * here may be 0.
+ */
+ idat->idat_length = cur->lengths[idat->idat_count];
+
+ /* And skip 12 bytes to the next chunk data */
+ skip_12(file);
+ }
+
+ /* The index is always that of the next byte, the rest of
+ * the information is always the current IDAT chunk and the
+ * current list.
+ */
+ ++(idat->idat_index);
+ }
+
+ /* Read the byte from the stream. */
+ b = reread_byte(file);
+
+ /* If the byte must be rewritten handle that here */
+ if (chunk->rewrite_length > 0)
+ {
+ if (chunk->rewrite_offset > 0)
+ --(chunk->rewrite_offset);
+
+ else
+ {
+ b = chunk->rewrite_buffer[0];
+ memmove(chunk->rewrite_buffer, chunk->rewrite_buffer+1,
+ (sizeof chunk->rewrite_buffer)-
+ (sizeof chunk->rewrite_buffer[0]));
+
+ --(chunk->rewrite_length);
+ }
+ }
+
+ chunk->write_crc = crc_one_byte(chunk->write_crc, b);
+ break;
+
+ /* The CRC is written at:
+ *
+ * chunk_write == chunk_length+8..chunk_length+11
+ *
+ * so 8 to 11. The CRC is not (yet) conditioned.
+ */
+ case 8: b = chunk->write_crc >> 24; goto write_crc;
+ case 9: b = chunk->write_crc >> 16; goto write_crc;
+ case 10: b = chunk->write_crc >> 8; goto write_crc;
+ case 11:
+ /* This must happen before the chunk_end below: */
+ b = chunk->write_crc;
+
+ if (file->global->verbose > 2)
+ {
+ fputs(" ", stderr);
+ type_name(type, stderr);
+ fprintf(stderr, " %lu 0x%.8x\n", (unsigned long)length,
+ chunk->write_crc ^ 0xffffffff);
+ }
+
+ /* The IDAT stream is written without a call to read_chunk
+ * until the end is reached. rechunk_length() calculates the
+ * length of the output chunks. Control gets to this point at
+ * the end of an *output* chunk - the length calculated by
+ * rechunk_length. If this corresponds to the end of the
+ * input stream stop writing IDAT chunks, otherwise continue.
+ */
+ if (file->state == STATE_IDAT &&
+ (file->idat->idat_index < file->idat->idat_length ||
+ 1+file->idat->idat_count < file->idat->idat_cur->count ||
+ file->idat->idat_cur != file->idat->idat_list_tail))
+ {
+ /* Write another IDAT chunk. Call rechunk_length to
+ * calculate the length required.
+ */
+ length = chunk->chunk_length = rechunk_length(file->idat);
+ assert(type == png_IDAT);
+ file->write_count = 0; /* for the new chunk */
+ --(file->write_count); /* fake out the increment below */
+ }
+
+ else
+ {
+ /* Entered at the end of a non-IDAT chunk and at the end of
+ * the IDAT stream. The rewrite should have been cleared.
+ */
+ if (chunk->rewrite_length > 0 || chunk->rewrite_offset > 0)
+ stop(file, UNEXPECTED_ERROR_CODE, "pending rewrite");
+
+ /* This is the last byte so reset chunk_read for the next
+ * chunk and move the input file to the position after the
+ * *next* chunk header if required.
+ */
+ file->read_count = 8;
+ file_setpos(file, &file->data_pos);
+
+ if (file->idat == NULL)
+ chunk_end(&file->chunk);
+
+ else
+ IDAT_end(&file->idat);
+ }
+
+ write_crc:
+ b ^= 0xff; /* conditioning */
+ break;
+ }
+ break;
+ }
+
+ /* Write one byte */
+ b &= 0xff;
+ *buffer++ = (png_byte)b;
+ --count;
+ write_byte(file, (png_byte)b); /* increments chunk_write */
+ }
+ while (count > 0);
+}
+
+/* Bundle the file and an uninitialized chunk and IDAT control structure
+ * together to allow implementation of the chunk/IDAT allocate routine.
+ */
+struct control
+{
+ struct file file;
+ struct chunk chunk;
+ struct IDAT idat;
+};
+
+static int
+control_end(struct control *control)
+{
+ return file_end(&control->file);
+}
+
+static struct file *
+get_control(png_const_structrp png_ptr)
+{
+ /* This just returns the (file*). The chunk and idat control structures
+ * don't always exist.
+ */
+ struct control *control = voidcast(struct control*,
+ png_get_error_ptr(png_ptr));
+ return &control->file;
+}
+
+static void
+allocate(struct file *file, int allocate_idat)
+{
+ struct control *control = voidcast(struct control*, file->alloc_ptr);
+
+ if (allocate_idat)
+ {
+ assert(file->idat == NULL);
+ IDAT_init(&control->idat, file);
+ }
+
+ else /* chunk */
+ {
+ assert(file->chunk == NULL);
+ chunk_init(&control->chunk, file);
+ }
+}
+
+static int
+control_init(struct control *control, struct global *global,
+ const char *file_name, const char *out_name)
+ /* This wraps file_init(&control::file) and simply returns the result from
+ * file_init.
+ */
+{
+ return file_init(&control->file, global, file_name, out_name, control,
+ allocate);
+}
+
+static int
+read_png(struct control *control)
+ /* Read a PNG, return 0 on success else an error (status) code; a bit mask as
+ * defined for file::status_code as above.
+ */
+{
+ png_structp png_ptr;
+ png_infop info_ptr = NULL;
+ volatile int rc;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, control,
+ error_handler, warning_handler);
+
+ if (png_ptr == NULL)
+ {
+ /* This is not really expected. */
+ log_error(&control->file, LIBPNG_ERROR_CODE, "OOM allocating png_struct");
+ control->file.status_code |= INTERNAL_ERROR;
+ return LIBPNG_ERROR_CODE;
+ }
+
+ rc = setjmp(control->file.jmpbuf);
+ if (rc == 0)
+ {
+# ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ /* Remove any limits on the size of PNG files that can be read,
+ * without this we may reject files based on built-in safety
+ * limits.
+ */
+ png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
+ png_set_chunk_cache_max(png_ptr, 0);
+ png_set_chunk_malloc_max(png_ptr, 0);
+# endif
+
+ png_set_read_fn(png_ptr, control, read_callback);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ if (control->file.global->verbose)
+ fprintf(stderr, " INFO\n");
+
+ png_read_info(png_ptr, info_ptr);
+
+ {
+ png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+ int passes = png_set_interlace_handling(png_ptr);
+ int pass;
+
+ png_start_read_image(png_ptr);
+
+ for (pass = 0; pass < passes; ++pass)
+ {
+ png_uint_32 y = height;
+
+ /* NOTE: this skips asking libpng to return either version of
+ * the image row, but libpng still reads the rows.
+ */
+ while (y-- > 0)
+ png_read_row(png_ptr, NULL, NULL);
+ }
+ }
+
+ if (control->file.global->verbose)
+ fprintf(stderr, " END\n");
+
+ /* Make sure to read to the end of the file: */
+ png_read_end(png_ptr, info_ptr);
+ }
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return rc;
+}
+
+static int
+one_file(struct global *global, const char *file_name, const char *out_name)
+{
+ int rc;
+ struct control control;
+
+ if (global->verbose)
+ fprintf(stderr, "FILE %s -> %s\n", file_name,
+ out_name ? out_name : "<none>");
+
+ /* Although control_init can return a failure code the structure is always
+ * initialized, so control_end can be used to accumulate any status codes.
+ */
+ rc = control_init(&control, global, file_name, out_name);
+
+ if (rc == 0)
+ rc = read_png(&control);
+
+ rc |= control_end(&control);
+
+ return rc;
+}
+
+static void
+usage(const char *prog)
+{
+ /* ANSI C-90 limits strings to 509 characters, so use a string array: */
+ size_t i;
+ static const char *usage_string[] = {
+" Tests, optimizes and optionally fixes the zlib header in PNG files.",
+" Optionally, when fixing, strips ancilliary chunks from the file.",
+0,
+"OPTIONS",
+" OPERATION",
+" By default files are just checked for readability with a summary of the",
+" of zlib issues founds for each compressed chunk and the IDAT stream in",
+" the file.",
+" --optimize (-o):",
+" Find the smallest deflate window size for the compressed data.",
+" --strip=[none|crc|unsafe|unused|transform|color|all]:",
+" none (default): Retain all chunks.",
+" crc: Remove chunks with a bad CRC.",
+" unsafe: Remove chunks that may be unsafe to retain if the image data",
+" is modified. This is set automatically if --max is given but",
+" may be cancelled by a later --strip=none.",
+" unused: Remove chunks not used by libpng when decoding an image.",
+" This retains any chunks that might be used by libpng image",
+" transformations.",
+" transform: unused+bKGD.",
+" color: transform+iCCP and cHRM.",
+" all: color+gAMA and sRGB.",
+" Only ancillary chunks are ever removed. In addition the tRNS and sBIT",
+" chunks are never removed as they affect exact interpretation of the",
+" image pixel values. The following known chunks are treated specially",
+" by the above options:",
+" gAMA, sRGB [all]: These specify the gamma encoding used for the pixel",
+" values.",
+" cHRM, iCCP [color]: These specify how colors are encoded. iCCP also",
+" specifies the exact encoding of a pixel value; however, in",
+" practice most programs will ignore it.",
+" bKGD [transform]: This is used by libpng transforms."
+" --max=<number>:",
+" Use IDAT chunks sized <number>. If no number is given the the IDAT",
+" chunks will be the maximum size permitted; 2^31-1 bytes. If the option",
+" is omitted the original chunk sizes will not be changed. When the",
+" option is given --strip=unsafe is set automatically. This may be",
+" cancelled if you know that all unknown unsafe-to-copy chunks really are",
+" safe to copy across an IDAT size change. This is true of all chunks",
+" that have ever been formally proposed as PNG extensions.",
+" MESSAGES",
+" By default the program only outputs summaries for each file.",
+" --quiet (-q):",
+" Do not output the summaries except for files that cannot be read. With",
+" two --quiets these are not output either.",
+" --errors (-e):",
+" Output errors from libpng and the program (except too-far-back).",
+" --warnings (-w):",
+" Output warnings from libpng.",
+" OUTPUT",
+" By default nothing is written.",
+" --out=<file>:",
+" Write the optimized/corrected version of the next PNG to <file>. This",
+" overrides the following two options",
+" --suffix=<suffix>:",
+" Set --out=<name><suffix> for all following files unless overridden on",
+" a per-file basis by explicit --out.",
+" --prefix=<prefix>:",
+" Set --out=<prefix><name> for all the following files unless overridden",
+" on a per-file basis by explicit --out.",
+" These two options can be used together to produce a suffix and prefix.",
+" INTERNAL OPTIONS",
+#if 0 /*NYI*/
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+" --test:",
+" Test the PNG_MAXIMUM_INFLATE_WINDOW option. Setting this disables",
+" output as this would produce a broken file.",
+#endif
+#endif
+0,
+"EXIT CODES",
+" *** SUBJECT TO CHANGE ***",
+" The program exit code is value in the range 0..127 holding a bit mask of",
+" the following codes. Notice that the results for each file are combined",
+" together - check one file at a time to get a meaningful error code!",
+" 0x01: The zlib too-far-back error existed in at least one chunk.",
+" 0x02: At least one chunk had a CRC error.",
+" 0x04: A chunk length was incorrect.",
+" 0x08: The file was truncated.",
+" Errors less than 16 are potentially recoverable, for a single file if the",
+" exit code is less than 16 the file could be read (with corrections if a",
+" non-zero code is returned).",
+" 0x10: The file could not be read, even with corrections.",
+" 0x20: The output file could not be written.",
+" 0x40: An unexpected, potentially internal, error occurred.",
+" If the command line arguments are incorrect the program exits with exit",
+" 255. Some older operating systems only support 7-bit exit codes, on those",
+" systems it is suggested that this program is first tested by supplying",
+" invalid arguments.",
+0,
+"DESCRIPTION",
+" " PROGRAM_NAME ":",
+" checks each PNG file on the command line for errors. By default errors are",
+" not output and the program just returns an exit code and prints a summary.",
+" With the --quiet (-q) option the summaries are suppressed too and the",
+" program only outputs unexpected errors (internal errors and file open",
+" errors).",
+" Various known problems in PNG files are fixed while the file is being read",
+" The exit code says what problems were fixed. In particular the zlib error:",
+0,
+" \"invalid distance too far back\"",
+0,
+" caused by an incorrect optimization of a zlib stream is fixed in any",
+" compressed chunk in which it is encountered. An integrity problem of the",
+" PNG stream caused by a bug in libpng which wrote an incorrect chunk length",
+" is also fixed. Chunk CRC errors are automatically fixed up.",
+0,
+" Setting one of the \"OUTPUT\" options causes the possibly modified file to",
+" be written to a new file.",
+0,
+" Notice that some PNG files with the zlib optimization problem can still be",
+" read by libpng under some circumstances. This program will still detect",
+" and, if requested, correct the error.",
+0,
+" The program will reliably process all files on the command line unless",
+" either an invalid argument causes the usage message (this message) to be",
+" produced or the program crashes.",
+0,
+" The summary lines describe issues encountered with the zlib compressed",
+" stream of a chunk. They have the following format, which is SUBJECT TO",
+" CHANGE in the future:",
+0,
+" chunk reason comp-level p1 p2 p3 p4 file",
+0,
+" p1 through p4 vary according to the 'reason'. There are always 8 space",
+" separated fields. Reasons specific formats are:",
+0,
+" chunk ERR status code read-errno write-errno message file",
+" chunk SKP comp-level file-bits zlib-rc compressed message file",
+" chunk ??? comp-level file-bits ok-bits compressed uncompress file",
+0,
+" The various fields are",
+0,
+"$1 chunk: The chunk type of a chunk in the file or 'HEAD' if a problem",
+" is reported by libpng at the start of the IDAT stream.",
+"$2 reason: One of:",
+" CHK: A zlib header checksum was detected and fixed.",
+" TFB: The zlib too far back error was detected and fixed.",
+" OK : No errors were detected in the zlib stream and optimization",
+" was not requested, or was not possible.",
+" OPT: The zlib stream window bits value could be improved (and was).",
+" SKP: The chunk was skipped because of a zlib issue (zlib-rc) with",
+" explanation 'message'",
+" ERR: The read of the file was aborted. The parameters explain why.",
+"$3 status: For 'ERR' the accumulated status code from 'EXIT CODES' above.",
+" This is printed as a 2 digit hexadecimal value",
+" comp-level: The recorded compression level (FLEVEL) of a zlib stream",
+" expressed as a string {supfast,stdfast,default,maximum}",
+"$4 code: The file exit code; where stop was called, as a fairly terse",
+" string {warning,libpng,zlib,invalid,read,write,unexpected}.",
+" file-bits: The zlib window bits recorded in the file.",
+"$5 read-errno: A system errno value from a read translated by strerror(3).",
+" zlib-rc: A zlib return code as a string (see zlib.h).",
+" ok-bits: The smallest zlib window bits value that works.",
+"$6 write-errno:A system errno value from a write translated by strerror(3).",
+" compressed: The count of compressed bytes in the zlib stream, when the",
+" reason is 'SKP'; this is a count of the bytes read from the",
+" stream when the fatal error was encountered.",
+"$7 message: An error message (spaces replaced by _, as in all parameters),",
+" uncompress: The count of bytes from uncompressing the zlib stream; this",
+" may not be the same as the number of bytes in the image.",
+"$8 file: The name of the file (this may contain spaces).",
+};
+
+ fprintf(stderr, "Usage: %s {[options] png-file}\n", prog);
+
+ for (i=0; i < (sizeof usage_string)/(sizeof usage_string[0]); ++i)
+ {
+ if (usage_string[i] != 0)
+ fputs(usage_string[i], stderr);
+
+ fputc('\n', stderr);
+ }
+
+ exit(255);
+}
+
+int
+main(int argc, const char **argv)
+{
+ char temp_name[FILENAME_MAX+1];
+ const char * prog = *argv;
+ const char * outfile = NULL;
+ const char * suffix = NULL;
+ const char * prefix = NULL;
+ int done = 0; /* if at least one file is processed */
+ struct global global;
+
+ global_init(&global);
+
+ while (--argc > 0)
+ {
+ ++argv;
+
+ if (strcmp(*argv, "--debug") == 0)
+ {
+ /* To help debugging problems: */
+ global.errors = global.warnings = 1;
+ global.quiet = 0;
+ global.verbose = 7;
+ }
+
+ else if (strncmp(*argv, "--max=", 6) == 0)
+ {
+ global.idat_max = (png_uint_32)atol(6+*argv);
+
+ if (global.skip < SKIP_UNSAFE)
+ global.skip = SKIP_UNSAFE;
+ }
+
+ else if (strcmp(*argv, "--max") == 0)
+ {
+ global.idat_max = 0x7fffffff;
+
+ if (global.skip < SKIP_UNSAFE)
+ global.skip = SKIP_UNSAFE;
+ }
+
+ else if (strcmp(*argv, "--optimize") == 0 || strcmp(*argv, "-o") == 0)
+ global.optimize_zlib = 1;
+
+ else if (strncmp(*argv, "--out=", 6) == 0)
+ outfile = 6+*argv;
+
+ else if (strncmp(*argv, "--suffix=", 9) == 0)
+ suffix = 9+*argv;
+
+ else if (strncmp(*argv, "--prefix=", 9) == 0)
+ prefix = 9+*argv;
+
+ else if (strcmp(*argv, "--strip=none") == 0)
+ global.skip = SKIP_NONE;
+
+ else if (strcmp(*argv, "--strip=crc") == 0)
+ global.skip = SKIP_BAD_CRC;
+
+ else if (strcmp(*argv, "--strip=unsafe") == 0)
+ global.skip = SKIP_UNSAFE;
+
+ else if (strcmp(*argv, "--strip=unused") == 0)
+ global.skip = SKIP_UNUSED;
+
+ else if (strcmp(*argv, "--strip=transform") == 0)
+ global.skip = SKIP_TRANSFORM;
+
+ else if (strcmp(*argv, "--strip=color") == 0)
+ global.skip = SKIP_COLOR;
+
+ else if (strcmp(*argv, "--strip=all") == 0)
+ global.skip = SKIP_ALL;
+
+ else if (strcmp(*argv, "--errors") == 0 || strcmp(*argv, "-e") == 0)
+ global.errors = 1;
+
+ else if (strcmp(*argv, "--warnings") == 0 || strcmp(*argv, "-w") == 0)
+ global.warnings = 1;
+
+ else if (strcmp(*argv, "--quiet") == 0 || strcmp(*argv, "-q") == 0)
+ {
+ if (global.quiet)
+ global.quiet = 2;
+
+ else
+ global.quiet = 1;
+ }
+
+ else if (strcmp(*argv, "--verbose") == 0 || strcmp(*argv, "-v") == 0)
+ ++global.verbose;
+
+#if 0
+ /* NYI */
+# ifdef PNG_MAXIMUM_INFLATE_WINDOW
+ else if (strcmp(*argv, "--test") == 0)
+ ++set_option;
+# endif
+#endif
+
+ else if ((*argv)[0] == '-')
+ usage(prog);
+
+ else
+ {
+ size_t outlen = strlen(*argv);
+
+ if (outfile == NULL) /* else this takes precedence */
+ {
+ /* Consider the prefix/suffix options */
+ if (prefix != NULL)
+ {
+ size_t prefixlen = strlen(prefix);
+
+ if (prefixlen+outlen > FILENAME_MAX)
+ {
+ fprintf(stderr, "%s: output file name too long: %s%s%s\n",
+ prog, prefix, *argv, suffix ? suffix : "");
+ global.status_code |= WRITE_ERROR;
+ continue;
+ }
+
+ memcpy(temp_name, prefix, prefixlen);
+ memcpy(temp_name+prefixlen, *argv, outlen);
+ outlen += prefixlen;
+ outfile = temp_name;
+ }
+
+ else if (suffix != NULL)
+ memcpy(temp_name, *argv, outlen);
+
+ temp_name[outlen] = 0;
+
+ if (suffix != NULL)
+ {
+ size_t suffixlen = strlen(suffix);
+
+ if (outlen+suffixlen > FILENAME_MAX)
+ {
+ fprintf(stderr, "%s: output file name too long: %s%s\n",
+ prog, *argv, suffix);
+ global.status_code |= WRITE_ERROR;
+ continue;
+ }
+
+ memcpy(temp_name+outlen, suffix, suffixlen);
+ outlen += suffixlen;
+ temp_name[outlen] = 0;
+ outfile = temp_name;
+ }
+ }
+
+ (void)one_file(&global, *argv, outfile);
+ ++done;
+ outfile = NULL;
+ }
+ }
+
+ if (!done)
+ usage(prog);
+
+ return global_end(&global);
+}
+
+#else /* ZLIB_VERNUM < 0x1240 */
+int
+main(void)
+{
+ fprintf(stderr,
+ "pngfix needs libpng with a zlib >=1.2.4 (not 0x%x)\n",
+ ZLIB_VERNUM);
+ return 77;
+}
+#endif /* ZLIB_VERNUM */
+
+#else /* No read support */
+
+int
+main(void)
+{
+ fprintf(stderr, "pngfix does not work without read deinterlace support\n");
+ return 77;
+}
+#endif /* PNG_READ_SUPPORTED && PNG_EASY_ACCESS_SUPPORTED */
+#else /* No setjmp support */
+int
+main(void)
+{
+ fprintf(stderr, "pngfix does not work without setjmp support\n");
+ return 77;
+}
+#endif /* PNG_SETJMP_SUPPORTED */
+
diff --git a/contrib/tools/sRGB.h b/contrib/tools/sRGB.h
new file mode 100644
index 0000000..22c8f7c
--- /dev/null
+++ b/contrib/tools/sRGB.h
@@ -0,0 +1,48 @@
+/*-
+ * sRGB.h
+ *
+ * Last changed in libpng 1.6.0 [February 14, 2013]
+ *
+ * COPYRIGHT: Written by John Cunningham Bowler, 2013.
+ * To the extent possible under law, the author has waived all copyright and
+ * related or neighboring rights to this work. This work is published from:
+ * United States.
+ *
+ * Utility file; not actually a header, this contains definitions of sRGB
+ * calculation functions for inclusion in those test programs that need them.
+ *
+ * All routines take and return a floating point value in the range
+ * 0 to 1.0, doing a calculation according to the sRGB specification
+ * (in fact the source of the numbers is the wikipedia article at
+ * http://en.wikipedia.org/wiki/SRGB).
+ */
+static double
+sRGB_from_linear(double l)
+{
+ if (l <= 0.0031308)
+ l *= 12.92;
+
+ else
+ l = 1.055 * pow(l, 1/2.4) - 0.055;
+
+ return l;
+}
+
+static double
+linear_from_sRGB(double s)
+{
+ if (s <= 0.04045)
+ return s / 12.92;
+
+ else
+ return pow((s+0.055)/1.055, 2.4);
+}
+
+static double
+YfromRGB(double r, double g, double b)
+{
+ /* Use the sRGB (rounded) coefficients for Rlinear, Glinear, Blinear to get
+ * the CIE Y value (also linear).
+ */
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+}
diff --git a/contrib/visupng/PngFile.c b/contrib/visupng/PngFile.c
index ff6d155..dcde18a 100644
--- a/contrib/visupng/PngFile.c
+++ b/contrib/visupng/PngFile.c
@@ -1,17 +1,19 @@
-//-------------------------------------
-// PNGFILE.C -- Image File Functions
-//-------------------------------------
-
-// Copyright 2000, Willem van Schaik.
-//
-// This code is released under the libpng license.
-// For conditions of distribution and use, see the disclaimer
-// and license in png.h
+/*-------------------------------------
+ * PNGFILE.C -- Image File Functions
+ *-------------------------------------
+ *
+ * Copyright 2000, Willem van Schaik.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
#include <windows.h>
#include <commdlg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <zlib.h>
#include "png.h"
#include "pngfile.h"
@@ -28,14 +30,14 @@
static png_infop info_ptr = NULL;
-// cexcept interface
+/* cexcept interface */
static void
png_cexcept_error(png_structp png_ptr, png_const_charp msg)
{
if(png_ptr)
;
-#ifndef PNG_NO_CONSOLE_IO
+#ifdef PNG_CONSOLE_IO_SUPPORTED
fprintf(stderr, "libpng error: %s\n", msg);
#endif
{
@@ -43,7 +45,7 @@
}
}
-// Windows open-file functions
+/* Windows open-file functions */
void PngFileInitialize (HWND hwnd)
{
@@ -57,13 +59,13 @@
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 0;
- ofn.lpstrFile = NULL; // Set in Open and Close functions
+ ofn.lpstrFile = NULL; /* Set in Open and Close functions */
ofn.nMaxFile = MAX_PATH;
- ofn.lpstrFileTitle = NULL; // Set in Open and Close functions
+ ofn.lpstrFileTitle = NULL; /* Set in Open and Close functions */
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrInitialDir = NULL;
ofn.lpstrTitle = NULL;
- ofn.Flags = 0; // Set in Open and Close functions
+ ofn.Flags = 0; /* Set in Open and Close functions */
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = TEXT ("png");
@@ -92,7 +94,7 @@
return GetSaveFileName (&ofn);
}
-// PNG image handler functions
+/* PNG image handler functions */
BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData,
int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor)
@@ -109,7 +111,7 @@
static png_byte **ppbRowPointers = NULL;
int i;
- // open the PNG input file
+ /* open the PNG input file */
if (!pstrFileName)
{
@@ -123,7 +125,7 @@
return FALSE;
}
- // first check the eight byte PNG signature
+ /* first check the eight byte PNG signature */
fread(pbSig, 1, 8, pfFile);
if (png_sig_cmp(pbSig, 0, 8))
@@ -132,7 +134,7 @@
return FALSE;
}
- // create the two png(-info) structures
+ /* create the two png(-info) structures */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
@@ -152,31 +154,37 @@
Try
{
-
- // initialize the png structure
-
-#if !defined(PNG_NO_STDIO)
+
+ /* initialize the png structure */
+
+#ifdef PNG_STDIO_SUPPORTED
png_init_io(png_ptr, pfFile);
#else
png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data);
#endif
-
+
png_set_sig_bytes(png_ptr, 8);
-
- // read all PNG info up to image data
-
+
+ /* read all PNG info up to image data */
+
png_read_info(png_ptr, info_ptr);
-
- // get width, height, bit-depth and color-type
-
+
+ /* get width, height, bit-depth and color-type */
+
png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth,
&iColorType, NULL, NULL, NULL);
-
- // expand images of all color-type and bit-depth to 3x8 bit RGB images
- // let the library process things like alpha, transparency, background
-
- if (iBitDepth == 16)
- png_set_strip_16(png_ptr);
+
+ /* expand images of all color-type and bit-depth to 3x8-bit RGB */
+ /* let the library process alpha, transparency, background, etc. */
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+ if (iBitDepth == 16)
+# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ png_set_scale_16(png_ptr);
+# else
+ png_set_strip_16(png_ptr);
+# endif
+#endif
if (iColorType == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
if (iBitDepth < 8)
@@ -186,8 +194,8 @@
if (iColorType == PNG_COLOR_TYPE_GRAY ||
iColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
-
- // set the background color to draw transparent and alpha images over.
+
+ /* set the background color to draw transparent and alpha images over */
if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
{
png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
@@ -199,30 +207,30 @@
{
pBkgColor = NULL;
}
-
- // if required set gamma conversion
+
+ /* if required set gamma conversion */
if (png_get_gAMA(png_ptr, info_ptr, &dGamma))
png_set_gamma(png_ptr, (double) 2.2, dGamma);
-
- // after the transformations have been registered update info_ptr data
-
+
+ /* after the transformations are registered, update info_ptr data */
+
png_read_update_info(png_ptr, info_ptr);
-
- // get again width, height and the new bit-depth and color-type
-
+
+ /* get again width, height and the new bit-depth and color-type */
+
png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth,
&iColorType, NULL, NULL, NULL);
-
-
- // row_bytes is the width x number of channels
-
+
+
+ /* row_bytes is the width x number of channels */
+
ulRowBytes = png_get_rowbytes(png_ptr, info_ptr);
ulChannels = png_get_channels(png_ptr, info_ptr);
-
+
*piChannels = ulChannels;
-
- // now we can allocate memory to store the image
-
+
+ /* now we can allocate memory to store the image */
+
if (pbImageData)
{
free (pbImageData);
@@ -234,34 +242,34 @@
png_error(png_ptr, "Visual PNG: out of memory");
}
*ppbImageData = pbImageData;
-
- // and allocate memory for an array of row-pointers
-
+
+ /* and allocate memory for an array of row-pointers */
+
if ((ppbRowPointers = (png_bytepp) malloc((*piHeight)
* sizeof(png_bytep))) == NULL)
{
png_error(png_ptr, "Visual PNG: out of memory");
}
-
- // set the individual row-pointers to point at the correct offsets
-
+
+ /* set the individual row-pointers to point at the correct offsets */
+
for (i = 0; i < (*piHeight); i++)
ppbRowPointers[i] = pbImageData + i * ulRowBytes;
-
- // now we can go ahead and just read the whole image
-
+
+ /* now we can go ahead and just read the whole image */
+
png_read_image(png_ptr, ppbRowPointers);
-
- // read the additional chunks in the PNG file (not really needed)
-
+
+ /* read the additional chunks in the PNG file (not really needed) */
+
png_read_end(png_ptr, NULL);
-
- // and we're done
-
+
+ /* and we're done */
+
free (ppbRowPointers);
ppbRowPointers = NULL;
-
- // yepp, done
+
+ /* yepp, done */
}
Catch (msg)
@@ -269,7 +277,7 @@
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
*ppbImageData = pbImageData = NULL;
-
+
if(ppbRowPointers)
free (ppbRowPointers);
@@ -295,7 +303,7 @@
static png_byte **ppbRowPointers = NULL;
int i;
- // open the PNG output file
+ /* open the PNG output file */
if (!pstrFileName)
return FALSE;
@@ -303,7 +311,7 @@
if (!(pfFile = fopen(pstrFileName, "wb")))
return FALSE;
- // prepare the standard PNG structures
+ /* prepare the standard PNG structures */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
@@ -322,60 +330,60 @@
Try
{
- // initialize the png structure
-
-#if !defined(PNG_NO_STDIO)
+ /* initialize the png structure */
+
+#ifdef PNG_STDIO_SUPPORTED
png_init_io(png_ptr, pfFile);
#else
png_set_write_fn(png_ptr, (png_voidp)pfFile, png_write_data, png_flush);
#endif
-
- // we're going to write a very simple 3x8 bit RGB image
-
+
+ /* we're going to write a very simple 3x8-bit RGB image */
+
png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth,
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
-
- // write the file header information
-
+
+ /* write the file header information */
+
png_write_info(png_ptr, info_ptr);
-
- // swap the BGR pixels in the DiData structure to RGB
-
+
+ /* swap the BGR pixels in the DiData structure to RGB */
+
png_set_bgr(png_ptr);
-
- // row_bytes is the width x number of channels
-
+
+ /* row_bytes is the width x number of channels */
+
ulRowBytes = iWidth * ciChannels;
-
- // we can allocate memory for an array of row-pointers
-
+
+ /* we can allocate memory for an array of row-pointers */
+
if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL)
Throw "Visualpng: Out of memory";
-
- // set the individual row-pointers to point at the correct offsets
-
+
+ /* set the individual row-pointers to point at the correct offsets */
+
for (i = 0; i < iHeight; i++)
ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2);
-
- // write out the entire image data in one call
-
+
+ /* write out the entire image data in one call */
+
png_write_image (png_ptr, ppbRowPointers);
-
- // write the additional chunks to the PNG file (not really needed)
-
+
+ /* write the additional chunks to the PNG file (not really needed) */
+
png_write_end(png_ptr, info_ptr);
-
- // and we're done
-
+
+ /* and we're done */
+
free (ppbRowPointers);
ppbRowPointers = NULL;
-
- // clean up after the write, and free any memory allocated
-
+
+ /* clean up after the write, and free any memory allocated */
+
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
-
- // yepp, done
+
+ /* yepp, done */
}
Catch (msg)
@@ -389,13 +397,13 @@
return FALSE;
}
-
+
fclose (pfFile);
-
+
return TRUE;
}
-#ifdef PNG_NO_STDIO
+#ifndef PNG_STDIO_SUPPORTED
static void
png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
@@ -437,6 +445,7 @@
#endif
-//-----------------
-// end of source
-//-----------------
+/*-----------------
+ * end of source
+ *-----------------
+ */
diff --git a/contrib/visupng/PngFile.h b/contrib/visupng/PngFile.h
index f695811..474426c 100644
--- a/contrib/visupng/PngFile.h
+++ b/contrib/visupng/PngFile.h
@@ -1,12 +1,12 @@
-//------------------------------------------
-// PNGFILE.H -- Header File for pngfile.c
-//------------------------------------------
+/*------------------------------------------*/
+/* PNGFILE.H -- Header File for pngfile.c*/
+/*------------------------------------------*/
-// Copyright 2000, Willem van Schaik.
+/* Copyright 2000, Willem van Schaik.*/
-// This code is released under the libpng license.
-// For conditions of distribution and use, see the disclaimer
-// and license in png.h
+/* This code is released under the libpng license.*/
+/* For conditions of distribution and use, see the disclaimer*/
+/* and license in png.h*/
#include <stdio.h>
#include <stdlib.h>
@@ -17,12 +17,12 @@
BOOL PngFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ;
BOOL PngFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ;
-BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData,
+BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData,
int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor);
BOOL PngSaveImage (PTSTR pstrFileName, png_byte *pDiData,
int iWidth, int iHeight, png_color BkgColor);
-#if defined(PNG_NO_STDIO)
+#ifndef PNG_STDIO_SUPPORTED
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length);
static void png_flush(png_structp png_ptr);
diff --git a/contrib/visupng/README.txt b/contrib/visupng/README.txt
index 4047119..72c5cba 100644
--- a/contrib/visupng/README.txt
+++ b/contrib/visupng/README.txt
@@ -8,8 +8,8 @@
and license in png.h
As a PNG .dll demo VisualPng is finished. More features would only hinder
-the program's objective. However, further extensions (like support for other
-graphics formats) are in development. To get these, or for pre-compiled
+the program's objective. However, further extensions (like support for other
+graphics formats) are in development. To get these, or for pre-compiled
binaries, go to "http://www.schaik.com/png/visualpng.html".
------------------------------------------------------------------------
diff --git a/contrib/visupng/VisualPng.c b/contrib/visupng/VisualPng.c
index 2018999..236525a 100644
--- a/contrib/visupng/VisualPng.c
+++ b/contrib/visupng/VisualPng.c
@@ -1,41 +1,43 @@
-//------------------------------------
-// VisualPng.C -- Shows a PNG image
-//------------------------------------
+/*------------------------------------
+ * VisualPng.C -- Shows a PNG image
+ *------------------------------------
+ *
+ * Copyright 2000, Willem van Schaik.
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
-// Copyright 2000, Willem van Schaik.
+/* switches */
-// This code is released under the libpng license.
-// For conditions of distribution and use, see the disclaimer
-// and license in png.h
-
-// switches
-
-// defines
+/* defines */
#define PROGNAME "VisualPng"
#define LONGNAME "Win32 Viewer for PNG-files"
#define VERSION "1.0 of 2000 June 07"
-// constants
+/* constants */
#define MARGIN 8
-// standard includes
+/* standard includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
+#include <zlib.h>
-// application includes
+/* application includes */
#include "png.h"
#include "pngfile.h"
#include "resource.h"
-// macros
+/* macros */
-// function prototypes
+/* function prototypes */
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
@@ -65,14 +67,14 @@
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
BOOL bStretched);
-// a few global variables
+/* a few global variables */
static char *szProgName = PROGNAME;
static char *szAppName = LONGNAME;
static char *szIconName = PROGNAME;
static char szCmdFileName [MAX_PATH];
-// MAIN routine
+/* MAIN routine */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
@@ -90,7 +92,7 @@
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (hInstance, szIconName) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
- wndclass.hbrBackground = NULL; // (HBRUSH) GetStockObject (GRAY_BRUSH);
+ wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */
wndclass.lpszMenuName = szProgName;
wndclass.lpszClassName = szProgName;
@@ -101,7 +103,7 @@
return 0;
}
- // if filename given on commandline, store it
+ /* if filename given on commandline, store it */
if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
if (szCmdLine[0] == '"')
strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
@@ -110,20 +112,20 @@
else
strcpy (szCmdFileName, "");
- // calculate size of window-borders
+ /* calculate size of window-borders */
ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
GetSystemMetrics (SM_CXDLGFRAME));
iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
GetSystemMetrics (SM_CYDLGFRAME)) +
GetSystemMetrics (SM_CYCAPTION) +
GetSystemMetrics (SM_CYMENUSIZE) +
- 1; /* WvS: don't ask me why? */
+ 1; /* WvS: don't ask me why? */
hwnd = CreateWindow (szProgName, szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
-// CW_USEDEFAULT, CW_USEDEFAULT,
+/* CW_USEDEFAULT, CW_USEDEFAULT, */
NULL, NULL, hInstance, NULL);
ShowWindow (hwnd, iCmdShow);
@@ -180,29 +182,29 @@
strcpy (szImgPathName, "");
- // in case we process file given on command-line
+ /* in case we process file given on command-line */
if (szCmdFileName[0] != '\0')
{
strcpy (szImgPathName, szCmdFileName);
- // read the other png-files in the directory for later
- // next/previous commands
+ /* read the other png-files in the directory for later */
+ /* next/previous commands */
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
&iPngFileIndex);
- // load the image from file
+ /* load the image from file */
if (!LoadImageFile (hwnd, szImgPathName,
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
return 0;
- // invalidate the client area for later update
+ /* invalidate the client area for later update */
InvalidateRect (hwnd, NULL, TRUE);
- // display the PNG into the DIBitmap
+ /* display the PNG into the DIBitmap */
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -214,11 +216,11 @@
cxWinSize = LOWORD (lParam);
cyWinSize = HIWORD (lParam);
- // invalidate the client area for later update
+ /* invalidate the client area for later update */
InvalidateRect (hwnd, NULL, TRUE);
- // display the PNG into the DIBitmap
+ /* display the PNG into the DIBitmap */
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -242,28 +244,28 @@
{
case IDM_FILE_OPEN:
- // show the File Open dialog box
+ /* show the File Open dialog box */
if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName))
return 0;
- // read the other png-files in the directory for later
- // next/previous commands
+ /* read the other png-files in the directory for later */
+ /* next/previous commands */
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
&iPngFileIndex);
- // load the image from file
+ /* load the image from file */
if (!LoadImageFile (hwnd, szImgPathName,
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
return 0;
- // invalidate the client area for later update
+ /* invalidate the client area for later update */
InvalidateRect (hwnd, NULL, TRUE);
- // display the PNG into the DIBitmap
+ /* display the PNG into the DIBitmap */
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -272,12 +274,12 @@
case IDM_FILE_SAVE:
- // show the File Save dialog box
+ /* show the File Save dialog box */
if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
return 0;
- // save the PNG to a disk file
+ /* save the PNG to a disk file */
SetCursor (LoadCursor (NULL, IDC_WAIT));
ShowCursor (TRUE);
@@ -295,55 +297,55 @@
case IDM_FILE_NEXT:
- // read next entry in the directory
+ /* read next entry in the directory */
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
NULL, szImgPathName))
{
if (strcmp (szImgPathName, "") == 0)
return 0;
-
- // load the image from file
-
+
+ /* load the image from file */
+
if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
&cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
return 0;
-
- // invalidate the client area for later update
-
+
+ /* invalidate the client area for later update */
+
InvalidateRect (hwnd, NULL, TRUE);
-
- // display the PNG into the DIBitmap
-
+
+ /* display the PNG into the DIBitmap */
+
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
}
-
+
return 0;
case IDM_FILE_PREVIOUS:
- // read previous entry in the directory
+ /* read previous entry in the directory */
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
szImgPathName, NULL))
{
-
+
if (strcmp (szImgPathName, "") == 0)
return 0;
-
- // load the image from file
-
+
+ /* load the image from file */
+
if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
&cyImgSize, &cImgChannels, &bkgColor))
return 0;
-
- // invalidate the client area for later update
-
+
+ /* invalidate the client area for later update */
+
InvalidateRect (hwnd, NULL, TRUE);
-
- // display the PNG into the DIBitmap
-
+
+ /* display the PNG into the DIBitmap */
+
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
}
@@ -352,9 +354,9 @@
case IDM_FILE_EXIT:
- // more cleanup needed...
+ /* more cleanup needed... */
- // free image buffer
+ /* free image buffer */
if (pDib != NULL)
{
@@ -362,7 +364,7 @@
pDib = NULL;
}
- // free file-list
+ /* free file-list */
if (pPngFileList != NULL)
{
@@ -370,7 +372,7 @@
pPngFileList = NULL;
}
- // let's go ...
+ /* let's go ... */
exit (0);
@@ -383,11 +385,11 @@
else
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
- // invalidate the client area for later update
+ /* invalidate the client area for later update */
InvalidateRect (hwnd, NULL, TRUE);
- // display the PNG into the DIBitmap
+ /* display the PNG into the DIBitmap */
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
@@ -398,7 +400,7 @@
DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
return 0;
- } // end switch
+ } /* end switch */
break;
@@ -450,10 +452,10 @@
return FALSE ;
}
-//---------------
-// CenterAbout
-//---------------
-
+/*---------------
+ * CenterAbout
+ *---------------
+ */
BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
{
RECT rChild, rParent, rWorkArea;
@@ -461,19 +463,19 @@
int xNew, yNew;
BOOL bResult;
- // Get the Height and Width of the child window
+ /* Get the Height and Width of the child window */
GetWindowRect (hwndChild, &rChild);
wChild = rChild.right - rChild.left;
hChild = rChild.bottom - rChild.top;
- // Get the Height and Width of the parent window
+ /* Get the Height and Width of the parent window */
GetWindowRect (hwndParent, &rParent);
wParent = rParent.right - rParent.left;
hParent = rParent.bottom - rParent.top;
- // Get the limits of the 'workarea'
+ /* Get the limits of the 'workarea' */
bResult = SystemParametersInfo(
- SPI_GETWORKAREA, // system parameter to query or set
+ SPI_GETWORKAREA, /* system parameter to query or set */
sizeof(RECT),
&rWorkArea,
0);
@@ -483,7 +485,7 @@
rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
}
- // Calculate new X position, then adjust for workarea
+ /* Calculate new X position, then adjust for workarea */
xNew = rParent.left + ((wParent - wChild) /2);
if (xNew < rWorkArea.left) {
xNew = rWorkArea.left;
@@ -491,7 +493,7 @@
xNew = rWorkArea.right - wChild;
}
- // Calculate new Y position, then adjust for workarea
+ /* Calculate new Y position, then adjust for workarea */
yNew = rParent.top + ((hParent - hChild) /2);
if (yNew < rWorkArea.top) {
yNew = rWorkArea.top;
@@ -499,15 +501,15 @@
yNew = rWorkArea.bottom - hChild;
}
- // Set it, and return
+ /* Set it, and return */
return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
SWP_NOZORDER);
}
-//----------------
-// BuildPngList
-//----------------
-
+/*----------------
+ * BuildPngList
+ *----------------
+ */
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
int *pFileIndex)
{
@@ -523,7 +525,7 @@
int i, ii;
int j, jj;
- // free previous file-list
+ /* free previous file-list */
if (*ppFileList != NULL)
{
@@ -531,7 +533,7 @@
*ppFileList = NULL;
}
- // extract foldername, filename and search-name
+ /* extract foldername, filename and search-name */
strcpy (szImgPathName, pstrPathName);
strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
@@ -540,7 +542,7 @@
*(strrchr (szImgFindName, '\\') + 1) = '\0';
strcat (szImgFindName, "*.png");
- // first cycle: count number of files in directory for memory allocation
+ /* first cycle: count number of files in directory for memory allocation */
*pFileCount = 0;
@@ -554,11 +556,11 @@
}
FindClose(hFind);
- // allocation memory for file-list
+ /* allocation memory for file-list */
*ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
- // second cycle: read directory and store filenames in file-list
+ /* second cycle: read directory and store filenames in file-list */
hFind = FindFirstFile(szImgFindName, &finddata);
bOk = (hFind != (HANDLE) -1);
@@ -580,7 +582,7 @@
}
FindClose(hFind);
- // finally we must sort the file-list
+ /* finally we must sort the file-list */
for (i = 0; i < *pFileCount - 1; i++)
{
@@ -594,7 +596,7 @@
strcpy (*ppFileList + jj, *ppFileList + ii);
strcpy (*ppFileList + ii, szTmp);
- // check if this was the current image that we moved
+ /* check if this was the current image that we moved */
if (*pFileIndex == i)
*pFileIndex = j;
@@ -608,9 +610,10 @@
return TRUE;
}
-//----------------
-// SearchPngList
-//----------------
+/*----------------
+ * SearchPngList
+ *----------------
+ */
BOOL SearchPngList (
TCHAR *pFileList, int FileCount, int *pFileIndex,
@@ -618,30 +621,30 @@
{
if (FileCount > 0)
{
- // get previous entry
-
+ /* get previous entry */
+
if (pstrPrevName != NULL)
{
if (*pFileIndex > 0)
*pFileIndex -= 1;
else
*pFileIndex = FileCount - 1;
-
+
strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
}
-
- // get next entry
-
+
+ /* get next entry */
+
if (pstrNextName != NULL)
{
if (*pFileIndex < FileCount - 1)
*pFileIndex += 1;
else
*pFileIndex = 0;
-
+
strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
}
-
+
return TRUE;
}
else
@@ -650,9 +653,10 @@
}
}
-//-----------------
-// LoadImageFile
-//-----------------
+/*-----------------
+ * LoadImageFile
+ *-----------------
+ */
BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
@@ -660,7 +664,7 @@
{
static TCHAR szTmp [MAX_PATH];
- // if there's an existing PNG, free the memory
+ /* if there's an existing PNG, free the memory */
if (*ppbImage)
{
@@ -668,7 +672,7 @@
*ppbImage = NULL;
}
- // Load the entire PNG into memory
+ /* Load the entire PNG into memory */
SetCursor (LoadCursor (NULL, IDC_WAIT));
ShowCursor (TRUE);
@@ -694,10 +698,10 @@
return TRUE;
}
-//----------------
-// DisplayImage
-//----------------
-
+/*----------------
+ * DisplayImage
+ *----------------
+ */
BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
BYTE **ppDiData, int cxWinSize, int cyWinSize,
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
@@ -705,14 +709,14 @@
{
BYTE *pDib = *ppDib;
BYTE *pDiData = *ppDiData;
- // BITMAPFILEHEADER *pbmfh;
+ /* BITMAPFILEHEADER *pbmfh; */
BITMAPINFOHEADER *pbmih;
WORD wDIRowBytes;
png_color bkgBlack = {0, 0, 0};
png_color bkgGray = {127, 127, 127};
png_color bkgWhite = {255, 255, 255};
- // allocate memory for the Device Independant bitmap
+ /* allocate memory for the Device Independant bitmap */
wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
@@ -733,7 +737,7 @@
*ppDib = pDib;
memset (pDib, 0, sizeof(BITMAPINFOHEADER));
- // initialize the dib-structure
+ /* initialize the dib-structure */
pbmih = (BITMAPINFOHEADER *) pDib;
pbmih->biSize = sizeof(BITMAPINFOHEADER);
@@ -745,11 +749,11 @@
pDiData = pDib + sizeof(BITMAPINFOHEADER);
*ppDiData = pDiData;
- // first fill bitmap with gray and image border
+ /* first fill bitmap with gray and image border */
InitBitmap (pDiData, cxWinSize, cyWinSize);
- // then fill bitmap with image
+ /* then fill bitmap with image */
if (pbImage)
{
@@ -762,16 +766,16 @@
return TRUE;
}
-//--------------
-// InitBitmap
-//--------------
-
+/*--------------
+ * InitBitmap
+ *--------------
+ */
BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
{
BYTE *dst;
int x, y, col;
- // initialize the background with gray
+ /* initialize the background with gray */
dst = pDiData;
for (y = 0; y < cyWinSize; y++)
@@ -779,13 +783,13 @@
col = 0;
for (x = 0; x < cxWinSize; x++)
{
- // fill with GRAY
+ /* fill with GRAY */
*dst++ = 127;
*dst++ = 127;
*dst++ = 127;
col += 3;
}
- // rows start on 4 byte boundaries
+ /* rows start on 4 byte boundaries */
while ((col % 4) != 0)
{
dst++;
@@ -796,10 +800,10 @@
return TRUE;
}
-//--------------
-// FillBitmap
-//--------------
-
+/*--------------
+ * FillBitmap
+ *--------------
+ */
BOOL FillBitmap (
BYTE *pDiData, int cxWinSize, int cyWinSize,
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
@@ -824,11 +828,12 @@
cxNewSize = cxWinSize - 2 * MARGIN;
cyNewSize = cyWinSize - 2 * MARGIN;
- // stretch the image to it's window determined size
+ /* stretch the image to it's window determined size */
- // the following two are the same, but the first has side-effects
- // because of rounding
-// if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize))
+ /* the following two are mathematically the same, but the first
+ * has side-effects because of rounding
+ */
+/* if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) */
if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
{
cyNewSize = cxNewSize * cyImgSize / cxImgSize;
@@ -867,12 +872,12 @@
}
}
- // calculate row-bytes
+ /* calculate row-bytes */
wImgRowBytes = cImgChannels * cxNewSize;
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
- // copy image to screen
+ /* copy image to screen */
for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
{
@@ -888,7 +893,7 @@
r = *src++;
g = *src++;
b = *src++;
- *dst++ = b; /* note the reverse order */
+ *dst++ = b; /* note the reverse order */
*dst++ = g;
*dst++ = r;
if (cImgChannels == 4)
@@ -898,7 +903,7 @@
}
}
- // free memory
+ /* free memory */
if (pStretchedImage != NULL)
{
@@ -908,28 +913,28 @@
}
- // process the image not-stretched
+ /* process the image not-stretched */
else
{
- // calculate the central position
+ /* calculate the central position */
cxImgPos = (cxWinSize - cxImgSize) / 2;
cyImgPos = (cyWinSize - cyImgSize) / 2;
- // check for image larger than window
+ /* check for image larger than window */
if (cxImgPos < MARGIN)
cxImgPos = MARGIN;
if (cyImgPos < MARGIN)
cyImgPos = MARGIN;
- // calculate both row-bytes
+ /* calculate both row-bytes */
wImgRowBytes = cImgChannels * cxImgSize;
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
- // copy image to screen
+ /* copy image to screen */
for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
{
@@ -945,7 +950,7 @@
r = *src++;
g = *src++;
b = *src++;
- *dst++ = b; /* note the reverse order */
+ *dst++ = b; /* note the reverse order */
*dst++ = g;
*dst++ = r;
if (cImgChannels == 4)
@@ -959,6 +964,7 @@
return TRUE;
}
-//-----------------
-// end of source
-//-----------------
+/*-----------------
+ * end of source
+ *-----------------
+ */
diff --git a/contrib/visupng/VisualPng.dsp b/contrib/visupng/VisualPng.dsp
index 53ef5a2..741ef6c 100644
--- a/contrib/visupng/VisualPng.dsp
+++ b/contrib/visupng/VisualPng.dsp
@@ -43,9 +43,9 @@
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c
+# ADD BASE CPP /nologo /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /FD /c
# SUBTRACT BASE CPP /YX
-# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /FD /c
# SUBTRACT CPP /YX
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
@@ -77,9 +77,9 @@
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c
+# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /FD /GZ /c
# SUBTRACT BASE CPP /YX
-# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
diff --git a/contrib/visupng/resource.h b/contrib/visupng/resource.h
index 611dd03..b62176d 100644
--- a/contrib/visupng/resource.h
+++ b/contrib/visupng/resource.h
@@ -12,7 +12,7 @@
#define IDM_HELP_ABOUT 40008
// Next default values for new objects
-//
+//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 113