summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Randers-Pehrson <glennrp at users.sourceforge.net>2011-07-07 12:54:39 -0500
committerGlenn Randers-Pehrson <glennrp at users.sourceforge.net>2011-07-11 15:38:54 -0500
commit3db6997c9223ac257a7cd63f6d1ea7fc66a95d82 (patch)
treecf8017b2f9f62f989d3eb39e3b47b7f9ebdd905f
parent4238975d9e00ece88d5fcb5c43df7921f9130260 (diff)
downloadlibpng-3db6997c9223ac257a7cd63f6d1ea7fc66a95d82.tar.gz
Imported from pngcrush-1.7.16.tarv1.7.16
-rw-r--r--ChangeLog.html4
-rw-r--r--Makefile-nolib85
-rw-r--r--png.c350
-rw-r--r--png.h395
-rw-r--r--pngconf.h61
-rw-r--r--pngcrush.c47
-rw-r--r--pngdebug.h157
-rw-r--r--pngerror.c255
-rw-r--r--pnginfo.h269
-rw-r--r--pnglibconf.h187
-rw-r--r--pngmem.c23
-rw-r--r--pngpread.c10
-rw-r--r--pngpriv.h272
-rw-r--r--pngread.c107
-rw-r--r--pngrtran.c1692
-rw-r--r--pngrutil.c228
-rw-r--r--pngset.c29
-rw-r--r--pngstruct.h337
-rw-r--r--pngtest.c24
-rw-r--r--pngtrans.c16
-rw-r--r--pngvalid.c8619
-rw-r--r--pngwrite.c178
-rw-r--r--pngwtran.c6
-rw-r--r--pngwutil.c343
24 files changed, 12508 insertions, 1186 deletions
diff --git a/ChangeLog.html b/ChangeLog.html
index 224f272c6..0a4038551 100644
--- a/ChangeLog.html
+++ b/ChangeLog.html
@@ -3,6 +3,10 @@
Change log:
+Version 1.7.16 (built with libpng-1.5.4 and zlib-1.2.5)
+ Only report best method==0 if pngcrush cannot match the input filesize.
+ Otherwise, if there is no improvement, report the first matching method.
+
Version 1.7.15 (built with libpng-1.5.2rc02 and zlib-1.2.5)
Force bit_depth to 1, 2, or 4 when -plte_len is <=2, <=4, or <=16 and
the -bit_depth option is not present, to avoid writing invalid palette
diff --git a/Makefile-nolib b/Makefile-nolib
new file mode 100644
index 000000000..edaab22bd
--- /dev/null
+++ b/Makefile-nolib
@@ -0,0 +1,85 @@
+# Sample makefile for pngcrush using gcc and GNU make.
+# Glenn Randers-Pehrson
+# Last modified: 19 February 2005
+#
+# Invoke this makefile from a shell prompt in the usual way; for example:
+#
+# make -f makefile.gcc
+#
+# This makefile builds a dynamically linked executable.
+
+# macros --------------------------------------------------------------------
+
+# uncomment these 2 lines only if you are using an external copy of libpng:
+PNGINC = /usr/local/include
+PNGLIB = /usr/local/lib
+# uncomment these 2 lines only if you are using an external copy of zlib:
+ZINC = /usr/local/include
+ZLIB = /usr/local/lib
+
+CC = gcc
+LD = gcc
+RM = rm -f
+#CFLAGS = -I. -O -Wall
+#CFLAGS = -I. -O3 -fomit-frame-pointer -Wall
+CFLAGS = -I${ZINC} -I. -g -O3 -fomit-frame-pointer -Wall
+# [note that -Wall is a gcc-specific compilation flag ("all warnings on")]
+LDFLAGS =
+O = .o
+E =
+
+PNGCRUSH = pngcrush
+
+# LIBS = -lm
+LIBS = -L$(PNGLIB) -lpng -lz -lm
+# LIBS = -lpng -lz -lm
+#LIBS = ${ZLIB}/libz.a -lm
+
+# uncomment these 4 lines only if you are NOT using an external copy of zlib:
+# ZHDR = zlib.h
+# ZOBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) \
+# infback$(O) inffast$(O) inflate$(O) inftrees$(O) \
+# trees$(O) uncompr$(O) zutil$(O)
+
+OBJS = pngcrush$(O)
+# png$(O) pngerror$(O) pngget$(O) pngmem$(O) \
+# pngpread$(O) pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \
+# pngset$(O) pngtrans$(O) pngwio$(O) pngwrite$(O) \
+# pngwtran$(O) pngwutil$(O) $(ZOBJS)
+
+EXES = $(PNGCRUSH)$(E)
+
+
+# implicit make rules -------------------------------------------------------
+
+.c$(O): png.h cexcept.h $(ZHDR)
+ $(CC) -c $(CFLAGS) $<
+#.c$(O): png.h pngconf.h pngcrush.h cexcept.h pngpriv.h $(ZHDR)
+# $(CC) -c $(CFLAGS) $<
+
+
+# dependencies --------------------------------------------------------------
+
+all: $(EXES)
+
+png.h:
+ ln -s $(PNGINC)/png.h png.h
+
+pngcrush$(O): pngcrush.c cexcept.h
+ $(CC) -c $(CFLAGS) $<
+# pngcrush$(O): pngcrush.c png.h pngconf.h pngcrush.h cexcept.h $(ZHDR)
+# $(CC) -c $(CFLAGS) $<
+
+$(PNGCRUSH)$(E): $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+# maintenance ---------------------------------------------------------------
+
+clean:
+ $(RM) $(EXES) $(OBJS) png.h
+
+install:
+ mkdir -p $(DESTDIR)/usr/bin/
+ cp $(PNGCRUSH)$(E) $(DESTDIR)/usr/bin/
+ chmod 0755 $(DESTDIR)/usr/bin/$(PNGCRUSH)$(E)
+
diff --git a/png.c b/png.c
index 742919e86..eed313619 100644
--- a/png.c
+++ b/png.c
@@ -1,7 +1,7 @@
/* png.c - location for general purpose libpng functions
*
- * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -14,7 +14,7 @@
#include "pngpriv.h"
/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_5_2rc02 Your_png_h_is_not_version_1_5_2rc02;
+typedef png_libpng_version_1_5_4 Your_png_h_is_not_version_1_5_4;
/* Tells libpng that we have already handled the first "num_bytes" bytes
* of the PNG file signature. If the PNG data is embedded into another
@@ -137,6 +137,61 @@ png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length)
png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
}
+/* Check a user supplied version number, called from both read and write
+ * functions that create a png_struct
+ */
+int
+png_user_version_check(png_structp png_ptr, png_const_charp user_png_ver)
+{
+ if (user_png_ver)
+ {
+ int i = 0;
+
+ do
+ {
+ if (user_png_ver[i] != png_libpng_ver[i])
+ png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+ } while (png_libpng_ver[i++]);
+ }
+
+ else
+ png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+
+ if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+ {
+ /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+ * we must recompile any applications that use any older library version.
+ * For versions after libpng 1.0, we will be compatible, so we need
+ * only check the first digit.
+ */
+ if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+ (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+ (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+ {
+#ifdef PNG_WARNINGS_SUPPORTED
+ size_t pos = 0;
+ char m[128];
+
+ pos = png_safecat(m, sizeof m, pos, "Application built with libpng-");
+ pos = png_safecat(m, sizeof m, pos, user_png_ver);
+ pos = png_safecat(m, sizeof m, pos, " but running with ");
+ pos = png_safecat(m, sizeof m, pos, png_libpng_ver);
+
+ png_warning(png_ptr, m);
+#endif
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ png_ptr->flags = 0;
+#endif
+
+ return 0;
+ }
+ }
+
+ /* Success return. */
+ return 1;
+}
+
/* Allocate the memory for an info_struct for the application. We don't
* really need the png_ptr, but it could potentially be useful in the
* future. This should be used in favour of malloc(png_sizeof(png_info))
@@ -291,12 +346,10 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
/* Free any sCAL entry */
if ((mask & PNG_FREE_SCAL) & info_ptr->free_me)
{
-#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
png_free(png_ptr, info_ptr->scal_s_width);
png_free(png_ptr, info_ptr->scal_s_height);
info_ptr->scal_s_width = NULL;
info_ptr->scal_s_height = NULL;
-#endif
info_ptr->valid &= ~PNG_INFO_sCAL;
}
#endif
@@ -518,28 +571,37 @@ png_convert_to_rfc1123(png_structp png_ptr, png_const_timep ptime)
if (png_ptr == NULL)
return (NULL);
- if (png_ptr->time_buffer == NULL)
{
- png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29*
- png_sizeof(char)));
+ size_t pos = 0;
+ char number_buf[5]; /* enough for a four digit year */
+
+# define APPEND_STRING(string)\
+ pos = png_safecat(png_ptr->time_buffer, sizeof png_ptr->time_buffer,\
+ pos, (string))
+# define APPEND_NUMBER(format, value)\
+ APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value)))
+# define APPEND(ch)\
+ if (pos < (sizeof png_ptr->time_buffer)-1)\
+ png_ptr->time_buffer[pos++] = (ch)
+
+ APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day % 32);
+ APPEND(' ');
+ APPEND_STRING(short_months[(ptime->month - 1) % 12]);
+ APPEND(' ');
+ APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year);
+ APPEND(' ');
+ APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour % 24);
+ APPEND(':');
+ APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute % 60);
+ APPEND(':');
+ APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second % 61);
+ APPEND_STRING(" +0000"); /* This reliably terminates the buffer */
+
+# undef APPEND
+# undef APPEND_NUMBER
+# undef APPEND_STRING
}
-# ifdef USE_FAR_KEYWORD
- {
- char near_time_buf[29];
- png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000",
- ptime->day % 32, short_months[(ptime->month - 1) % 12],
- ptime->year, ptime->hour % 24, ptime->minute % 60,
- ptime->second % 61);
- png_memcpy(png_ptr->time_buffer, near_time_buf,
- 29*png_sizeof(char));
- }
-# else
- png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000",
- ptime->day % 32, short_months[(ptime->month - 1) % 12],
- ptime->year, ptime->hour % 24, ptime->minute % 60,
- ptime->second % 61);
-# endif
return png_ptr->time_buffer;
}
# endif /* PNG_TIME_RFC1123_SUPPORTED */
@@ -555,13 +617,13 @@ png_get_copyright(png_const_structp png_ptr)
#else
# ifdef __STDC__
return PNG_STRING_NEWLINE \
- "libpng version 1.5.2rc02 - March 22, 2011" PNG_STRING_NEWLINE \
+ "libpng version 1.5.4 - July 7, 2011" PNG_STRING_NEWLINE \
"Copyright (c) 1998-2011 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
"Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
"Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
PNG_STRING_NEWLINE;
# else
- return "libpng version 1.5.2rc02 - March 22, 2011\
+ return "libpng version 1.5.4 - July 7, 2011\
Copyright (c) 1998-2011 Glenn Randers-Pehrson\
Copyright (c) 1996-1997 Andreas Dilger\
Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
@@ -681,6 +743,13 @@ png_check_cHRM_fixed(png_structp png_ptr,
if (png_ptr == NULL)
return 0;
+ /* (x,y,z) values are first limited to 0..100000 (PNG_FP_1), the white
+ * y must also be greater than 0. To test for the upper limit calculate
+ * (PNG_FP_1-y) - x must be <= to this for z to be >= 0 (and the expression
+ * cannot overflow.) At this point we know x and y are >= 0 and (x+y) is
+ * <= PNG_FP_1. The previous test on PNG_MAX_UINT_31 is removed because it
+ * pointless (and it produces compiler warnings!)
+ */
if (white_x < 0 || white_y <= 0 ||
red_x < 0 || red_y < 0 ||
green_x < 0 || green_y < 0 ||
@@ -690,38 +759,26 @@ png_check_cHRM_fixed(png_structp png_ptr,
"Ignoring attempt to set negative chromaticity value");
ret = 0;
}
- if (white_x > (png_fixed_point)PNG_UINT_31_MAX ||
- white_y > (png_fixed_point)PNG_UINT_31_MAX ||
- red_x > (png_fixed_point)PNG_UINT_31_MAX ||
- red_y > (png_fixed_point)PNG_UINT_31_MAX ||
- green_x > (png_fixed_point)PNG_UINT_31_MAX ||
- green_y > (png_fixed_point)PNG_UINT_31_MAX ||
- blue_x > (png_fixed_point)PNG_UINT_31_MAX ||
- blue_y > (png_fixed_point)PNG_UINT_31_MAX )
- {
- png_warning(png_ptr,
- "Ignoring attempt to set chromaticity value exceeding 21474.83");
- ret = 0;
- }
- if (white_x > 100000L - white_y)
+ /* And (x+y) must be <= PNG_FP_1 (so z is >= 0) */
+ if (white_x > PNG_FP_1 - white_y)
{
png_warning(png_ptr, "Invalid cHRM white point");
ret = 0;
}
- if (red_x > 100000L - red_y)
+ if (red_x > PNG_FP_1 - red_y)
{
png_warning(png_ptr, "Invalid cHRM red point");
ret = 0;
}
- if (green_x > 100000L - green_y)
+ if (green_x > PNG_FP_1 - green_y)
{
png_warning(png_ptr, "Invalid cHRM green point");
ret = 0;
}
- if (blue_x > 100000L - blue_y)
+ if (blue_x > PNG_FP_1 - blue_y)
{
png_warning(png_ptr, "Invalid cHRM blue point");
ret = 0;
@@ -763,7 +820,7 @@ png_check_IHDR(png_structp png_ptr,
}
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
- if (width > png_ptr->user_width_max || width > PNG_USER_WIDTH_MAX)
+ if (width > png_ptr->user_width_max)
# else
if (width > PNG_USER_WIDTH_MAX)
@@ -774,7 +831,7 @@ png_check_IHDR(png_structp png_ptr,
}
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
- if (height > png_ptr->user_height_max || height > PNG_USER_HEIGHT_MAX)
+ if (height > png_ptr->user_height_max)
# else
if (height > PNG_USER_HEIGHT_MAX)
# endif
@@ -889,16 +946,9 @@ png_check_IHDR(png_structp png_ptr,
/* Check an ASCII formated floating point value, see the more detailed
* comments in pngpriv.h
*/
-/* The following is used internally to preserve the 'valid' flag */
+/* The following is used internally to preserve the sticky flags */
#define png_fp_add(state, flags) ((state) |= (flags))
-#define png_fp_set(state, value)\
- ((state) = (value) | ((state) & PNG_FP_WAS_VALID))
-
-/* Internal type codes: bits above the base state! */
-#define PNG_FP_SIGN 0 /* [+-] */
-#define PNG_FP_DOT 4 /* . */
-#define PNG_FP_DIGIT 8 /* [0123456789] */
-#define PNG_FP_E 12 /* [Ee] */
+#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY))
int /* PRIVATE */
png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
@@ -911,55 +961,55 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
{
int type;
/* First find the type of the next character */
+ switch (string[i])
{
- char ch = string[i];
-
- if (ch >= 48 && ch <= 57)
- type = PNG_FP_DIGIT;
-
- else switch (ch)
- {
- case 43: case 45: type = PNG_FP_SIGN; break;
- case 46: type = PNG_FP_DOT; break;
- case 69: case 101: type = PNG_FP_E; break;
- default: goto PNG_FP_End;
- }
+ case 43: type = PNG_FP_SAW_SIGN; break;
+ case 45: type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break;
+ case 46: type = PNG_FP_SAW_DOT; break;
+ case 48: type = PNG_FP_SAW_DIGIT; break;
+ case 49: case 50: case 51: case 52:
+ case 53: case 54: case 55: case 56:
+ case 57: type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break;
+ case 69:
+ case 101: type = PNG_FP_SAW_E; break;
+ default: goto PNG_FP_End;
}
/* Now deal with this type according to the current
* state, the type is arranged to not overlap the
* bits of the PNG_FP_STATE.
*/
- switch ((state & PNG_FP_STATE) + type)
+ switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY))
{
- case PNG_FP_INTEGER + PNG_FP_SIGN:
+ case PNG_FP_INTEGER + PNG_FP_SAW_SIGN:
if (state & PNG_FP_SAW_ANY)
goto PNG_FP_End; /* not a part of the number */
- png_fp_add(state, PNG_FP_SAW_SIGN);
+ png_fp_add(state, type);
break;
- case PNG_FP_INTEGER + PNG_FP_DOT:
+ case PNG_FP_INTEGER + PNG_FP_SAW_DOT:
/* Ok as trailer, ok as lead of fraction. */
if (state & PNG_FP_SAW_DOT) /* two dots */
goto PNG_FP_End;
else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */
- png_fp_add(state, PNG_FP_SAW_DOT);
+ png_fp_add(state, type);
else
- png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
+ png_fp_set(state, PNG_FP_FRACTION | type);
break;
- case PNG_FP_INTEGER + PNG_FP_DIGIT:
+ case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT:
if (state & PNG_FP_SAW_DOT) /* delayed fraction */
png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
- png_fp_add(state, PNG_FP_SAW_DIGIT + PNG_FP_WAS_VALID);
+ png_fp_add(state, type | PNG_FP_WAS_VALID);
break;
- case PNG_FP_INTEGER + PNG_FP_E:
+
+ case PNG_FP_INTEGER + PNG_FP_SAW_E:
if ((state & PNG_FP_SAW_DIGIT) == 0)
goto PNG_FP_End;
@@ -967,17 +1017,17 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
break;
- /* case PNG_FP_FRACTION + PNG_FP_SIGN:
- goto PNG_FP_End; ** no sign in exponent */
+ /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN:
+ goto PNG_FP_End; ** no sign in fraction */
- /* case PNG_FP_FRACTION + PNG_FP_DOT:
+ /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT:
goto PNG_FP_End; ** Because SAW_DOT is always set */
- case PNG_FP_FRACTION + PNG_FP_DIGIT:
- png_fp_add(state, PNG_FP_SAW_DIGIT + PNG_FP_WAS_VALID);
+ case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT:
+ png_fp_add(state, type | PNG_FP_WAS_VALID);
break;
- case PNG_FP_FRACTION + PNG_FP_E:
+ case PNG_FP_FRACTION + PNG_FP_SAW_E:
/* This is correct because the trailing '.' on an
* integer is handled above - so we can only get here
* with the sequence ".E" (with no preceding digits).
@@ -989,7 +1039,7 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
break;
- case PNG_FP_EXPONENT + PNG_FP_SIGN:
+ case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN:
if (state & PNG_FP_SAW_ANY)
goto PNG_FP_End; /* not a part of the number */
@@ -997,15 +1047,15 @@ png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
break;
- /* case PNG_FP_EXPONENT + PNG_FP_DOT:
+ /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT:
goto PNG_FP_End; */
- case PNG_FP_EXPONENT + PNG_FP_DIGIT:
- png_fp_add(state, PNG_FP_SAW_DIGIT + PNG_FP_WAS_VALID);
+ case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT:
+ png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID);
break;
- /* case PNG_FP_EXPONEXT + PNG_FP_E:
+ /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E:
goto PNG_FP_End; */
default: goto PNG_FP_End; /* I.e. break 2 */
@@ -1033,8 +1083,11 @@ png_check_fp_string(png_const_charp string, png_size_t size)
int state=0;
png_size_t char_index=0;
- return png_check_fp_number(string, size, &state, &char_index) &&
- (char_index == size || string[char_index] == 0);
+ if (png_check_fp_number(string, size, &state, &char_index) &&
+ (char_index == size || string[char_index] == 0))
+ return state /* must be non-zero - see above */;
+
+ return 0; /* i.e. fail */
}
#endif /* pCAL or sCAL */
@@ -1102,7 +1155,7 @@ png_ascii_from_fp(png_structp png_ptr, png_charp ascii, png_size_t size,
if (fp < 0)
{
fp = -fp;
- *ascii++ = 45; /* '-' PLUS 1 TOTAL 1*/
+ *ascii++ = 45; /* '-' PLUS 1 TOTAL 1 */
--size;
}
@@ -1329,7 +1382,7 @@ png_ascii_from_fp(png_structp png_ptr, png_charp ascii, png_size_t size,
*/
size -= cdigits;
- *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision*/
+ *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision */
if (exp_b10 < 0)
{
*ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */
@@ -1401,7 +1454,7 @@ png_ascii_from_fixed(png_structp png_ptr, png_charp ascii, png_size_t size,
if (num <= 0x80000000U) /* else overflowed */
{
- unsigned int ndigits = 0, first = 16/*flag value*/;
+ unsigned int ndigits = 0, first = 16 /* flag value */;
char digits[10];
while (num)
@@ -1495,7 +1548,7 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
r /= divisor;
r = floor(r+.5);
- /* A png_fixed_point is a 32 bit integer. */
+ /* A png_fixed_point is a 32-bit integer. */
if (r <= 2147483647. && r >= -2147483648.)
{
*res = (png_fixed_point)r;
@@ -1540,7 +1593,7 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
if (s32 < D) /* else overflow */
{
- /* s32.s00 is now the 64 bit product, do a standard
+ /* s32.s00 is now the 64-bit product, do a standard
* division, we know that s32 < D, so the maximum
* required shift is 31.
*/
@@ -1683,7 +1736,7 @@ png_reciprocal2(png_fixed_point a, png_fixed_point b)
* 2010: moved from pngset.c) */
/*
* Multiply two 32-bit numbers, V1 and V2, using 32-bit
- * arithmetic, to produce a 64 bit result in the HI/LO words.
+ * arithmetic, to produce a 64-bit result in the HI/LO words.
*
* A B
* x C D
@@ -1727,17 +1780,17 @@ png_64bit_product (long v1, long v2, unsigned long *hi_product,
/* Fixed point gamma.
*
* To calculate gamma this code implements fast log() and exp() calls using only
- * fixed point arithmetic. This code has sufficient precision for either 8 or
- * 16 bit sample values.
+ * fixed point arithmetic. This code has sufficient precision for either 8-bit
+ * or 16-bit sample values.
*
* The tables used here were calculated using simple 'bc' programs, but C double
* precision floating point arithmetic would work fine. The programs are given
* at the head of each table.
*
- * 8 bit log table
+ * 8-bit log table
* 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.
+ * 255, so it's the base 2 logarithm of a normalized 8-bit floating point
+ * mantissa. The numbers are 32-bit fractions.
*/
static png_uint_32
png_8bit_l2[128] =
@@ -1768,10 +1821,10 @@ png_8bit_l2[128] =
172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
24347096U, 0U
#if 0
- /* The following are the values for 16 bit tables - these work fine for the 8
- * bit conversions but produce very slightly larger errors in the 16 bit log
- * (about 1.2 as opposed to 0.7 absolute error in the final value). To use
- * these all the shifts below must be adjusted appropriately.
+ /* The following are the values for 16-bit tables - these work fine for the
+ * 8-bit conversions but produce very slightly larger errors in the 16-bit
+ * log (about 1.2 as opposed to 0.7 absolute error in the final value). To
+ * use these all the shifts below must be adjusted appropriately.
*/
65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
@@ -1788,7 +1841,7 @@ png_8bit_l2[128] =
#endif
};
-static png_int_32
+PNG_STATIC png_int_32
png_log8bit(unsigned int x)
{
unsigned int lg2 = 0;
@@ -1814,11 +1867,11 @@ png_log8bit(unsigned int x)
return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16));
}
-/* The above gives exact (to 16 binary places) log2 values for 8 bit images,
- * for 16 bit images we use the most significant 8 bits of the 16 bit value to
+/* The above gives exact (to 16 binary places) log2 values for 8-bit images,
+ * for 16-bit images we use the most significant 8 bits of the 16-bit value to
* get an approximation then multiply the approximation by a correction factor
* determined by the remaining up to 8 bits. This requires an additional step
- * in the 16 bit case.
+ * in the 16-bit case.
*
* We want log2(value/65535), we have log2(v'/255), where:
*
@@ -1827,8 +1880,8 @@ png_log8bit(unsigned int x)
*
* So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
* to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
- * than 258. The final factor also needs to correct for the fact that our 8 bit
- * value is scaled by 255, whereas the 16 bit values must be scaled by 65535.
+ * than 258. The final factor also needs to correct for the fact that our 8-bit
+ * value is scaled by 255, whereas the 16-bit values must be scaled by 65535.
*
* This gives a final formula using a calculated value 'x' which is value/v' and
* scaling by 65536 to match the above table:
@@ -1838,13 +1891,13 @@ png_log8bit(unsigned int x)
* Since these numbers are so close to '1' we can use simple linear
* interpolation between the two end values 256/257 (result -368.61) and 258/257
* (result 367.179). The values used below are scaled by a further 64 to give
- * 16 bit precision in the interpolation:
+ * 16-bit precision in the interpolation:
*
* Start (256): -23591
* Zero (257): 0
* End (258): 23499
*/
-static png_int_32
+PNG_STATIC png_int_32
png_log16bit(png_uint_32 x)
{
unsigned int lg2 = 0;
@@ -1865,7 +1918,7 @@ png_log16bit(png_uint_32 x)
if ((x & 0x8000) == 0)
lg2 += 1, x <<= 1;
- /* Calculate the base logarithm from the top 8 bits as a 28 bit fractional
+ /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional
* value.
*/
lg2 <<= 28;
@@ -1895,18 +1948,18 @@ png_log16bit(png_uint_32 x)
return (png_int_32)((lg2 + 2048) >> 12);
}
-/* 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
+/* 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
+ * The worst case is the 16-bit distinction between 65535 and 65534, this
* requires perhaps spurious accuracty 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
+ * 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 png_uint_32
@@ -1915,7 +1968,7 @@ png_32bit_exp[16] =
# if PNG_DO_BC
for (i=0;i<16;++i) { .5 + e(-i/16*l(2))*2^32; }
# endif
- /* NOTE: the first entry is deliberately set to the maximum 32 bit value. */
+ /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */
4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
2553802834U, 2445529972U, 2341847524U, 2242560872U
@@ -1938,12 +1991,12 @@ for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
0 45425.85339951654943850496
#endif
-static png_uint_32
+PNG_STATIC png_uint_32
png_exp(png_fixed_point x)
{
if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
{
- /* Obtain a 4 bit approximation */
+ /* Obtain a 4-bit approximation */
png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf];
/* Incorporate the low 12 bits - these decrease the returned value by
@@ -1986,13 +2039,13 @@ png_exp(png_fixed_point x)
return 0;
}
-static png_byte
+PNG_STATIC png_byte
png_exp8bit(png_fixed_point lg2)
{
- /* Get a 32 bit value: */
+ /* Get a 32-bit value: */
png_uint_32 x = png_exp(lg2);
- /* Convert the 32 bit value to 0..255 by multiplying by 256-1, note that the
+ /* Convert the 32-bit value to 0..255 by multiplying by 256-1, note that the
* second, rounding, step can't overflow because of the first, subtraction,
* step.
*/
@@ -2000,13 +2053,13 @@ png_exp8bit(png_fixed_point lg2)
return (png_byte)((x + 0x7fffffU) >> 24);
}
-static png_uint_16
+PNG_STATIC png_uint_16
png_exp16bit(png_fixed_point lg2)
{
- /* Get a 32 bit value: */
+ /* Get a 32-bit value: */
png_uint_32 x = png_exp(lg2);
- /* Convert the 32 bit value to 0..65535 by multiplying by 65536-1: */
+ /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */
x -= x >> 16;
return (png_uint_16)((x + 32767U) >> 16);
}
@@ -2059,9 +2112,9 @@ png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)
}
/* This does the right thing based on the bit_depth field of the
- * png_struct, interpreting values as 8 or 16 bit. While the result
- * is nominally a 16 bit value if bit depth is 8 then the result is
- * 8 bit (as are the arguments.)
+ * png_struct, interpreting values as 8-bit or 16-bit. While the result
+ * is nominally a 16-bit value if bit depth is 8 then the result is
+ * 8-bit (as are the arguments.)
*/
png_uint_16 /* PRIVATE */
png_gamma_correct(png_structp png_ptr, unsigned int value,
@@ -2084,7 +2137,7 @@ png_gamma_significant(png_fixed_point gamma_val)
gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;
}
-/* Internal function to build a single 16 bit table - the table consists of
+/* Internal function to build a single 16-bit table - the table consists of
* 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount
* to shift the input values right (or 16-number_of_signifiant_bits).
*
@@ -2111,7 +2164,7 @@ png_build_16bit_table(png_structp png_ptr, png_uint_16pp *ptable,
(png_uint_16p)png_malloc(png_ptr, 256 * png_sizeof(png_uint_16));
/* The 'threshold' test is repeated here because it can arise for one of
- * the 16 bit tables even if the others don't hit it.
+ * the 16-bit tables even if the others don't hit it.
*/
if (png_gamma_significant(gamma_val))
{
@@ -2173,7 +2226,7 @@ png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
(png_uint_16pp)png_calloc(png_ptr, num * png_sizeof(png_uint_16p));
/* 'num' is the number of tables and also the number of low bits of low
- * bits of the input 16 bit value used to select a table. Each table is
+ * bits of the input 16-bit value used to select a table. Each table is
* itself index by the high 8 bits of the value.
*/
for (i = 0; i < num; i++)
@@ -2183,24 +2236,24 @@ png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
/* 'gamma_val' is set to the reciprocal of the value calculated above, so
* pow(out,g) is an *input* value. 'last' is the last input value set.
*
- * In the loop 'i' is used to find output values. Since the output is 8
- * bit there are only 256 possible values. The tables are set up to
+ * In the loop 'i' is used to find output values. Since the output is
+ * 8-bit there are only 256 possible values. The tables are set up to
* select the closest possible output value for each input by finding
* the input value at the boundary between each pair of output values
* and filling the table up to that boundary with the lower output
* value.
*
- * The boundary values are 0.5,1.5..253.5,254.5. Since these are 9 bit
- * values the code below uses a 16 bit value in i; the values start at
+ * The boundary values are 0.5,1.5..253.5,254.5. Since these are 9-bit
+ * values the code below uses a 16-bit value in i; the values start at
* 128.5 (for 0.5) and step by 257, for a total of 254 values (the last
* entries are filled with 255). Start i at 128 and fill all 'last'
* table entries <= 'max'
*/
last = 0;
- for (i = 0; i < 255; ++i) /* 8 bit output value */
+ for (i = 0; i < 255; ++i) /* 8-bit output value */
{
/* Find the corresponding maximum input value */
- png_uint_16 out = (png_uint_16)(i * 257U); /* 16 bit output value */
+ png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */
/* Find the boundary value in 16 bits: */
png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val);
@@ -2223,7 +2276,7 @@ png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
}
}
-/* Build a single 8 bit table: same as the 16 bit case but much simpler (and
+/* Build a single 8-bit table: same as the 16-bit case but much simpler (and
* typically much faster). Note that libpng currently does no sBIT processing
* (apparently contrary to the spec) so a 256 entry table is always generated.
*/
@@ -2258,8 +2311,9 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
png_ptr->screen_gamma) : PNG_FP_1);
#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+ defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
- if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY))
+ if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY))
{
png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
png_reciprocal(png_ptr->gamma));
@@ -2268,7 +2322,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
png_ptr->gamma/* Probably doing rgb_to_gray */);
}
-#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
}
else
{
@@ -2287,7 +2341,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
else
sig_bit = png_ptr->sig_bit.gray;
- /* 16 bit gamma code uses this equation:
+ /* 16-bit gamma code uses this equation:
*
* ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]
*
@@ -2302,7 +2356,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
*
* So the table 'n' corresponds to all those 'iv' of:
*
- * <all high 8 bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
+ * <all high 8-bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
*
*/
if (sig_bit > 0 && sig_bit < 16U)
@@ -2311,7 +2365,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
else
shift = 0; /* keep all 16 bits */
- if (png_ptr->transformations & PNG_16_TO_8)
+ if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8))
{
/* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
* the significant bits in the *input* when the output will
@@ -2327,7 +2381,12 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
png_ptr->gamma_shift = shift;
#ifdef PNG_16BIT_SUPPORTED
- if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND))
+ /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now
+ * PNG_COMPOSE). This effectively smashed the background calculation for
+ * 16-bit output because the 8-bit table assumes the result will be reduced
+ * to 8 bits.
+ */
+ if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8))
#endif
png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
png_ptr->screen_gamma > 0 ? png_product2(png_ptr->gamma,
@@ -2341,8 +2400,9 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
#endif
#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+ defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
- if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY))
+ if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY))
{
png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
png_reciprocal(png_ptr->gamma));
@@ -2355,7 +2415,7 @@ png_build_gamma_table(png_structp png_ptr, int bit_depth)
png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
png_ptr->gamma/* Probably doing rgb_to_gray */);
}
-#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
}
}
#endif /* READ_GAMMA */
diff --git a/png.h b/png.h
index a4d61d521..c655a51af 100644
--- a/png.h
+++ b/png.h
@@ -1,7 +1,7 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.5.2rc02 - March 22, 2011
+ * libpng version 1.5.4 - July 7, 2011
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -11,7 +11,7 @@
* Authors and maintainers:
* libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
* libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
- * libpng versions 0.97, January 1998, through 1.5.2rc02 - March 22, 2011: Glenn
+ * libpng versions 0.97, January 1998, through 1.5.4 - July 7, 2011: Glenn
* See also "Contributing Authors", below.
*
* Note about libpng version numbers:
@@ -148,7 +148,15 @@
* 1.5.1rc01-02 15 10501 15.so.15.1[.0]
* 1.5.1 15 10501 15.so.15.1[.0]
* 1.5.2beta01-03 15 10502 15.so.15.2[.0]
- * 1.5.2rc01 15 10502 15.so.15.2[.0]
+ * 1.5.2rc01-03 15 10502 15.so.15.2[.0]
+ * 1.5.2 15 10502 15.so.15.2[.0]
+ * 1.5.3beta01-10 15 10503 15.so.15.3[.0]
+ * 1.5.3rc01-02 15 10503 15.so.15.3[.0]
+ * 1.5.3beta11 15 10503 15.so.15.3[.0]
+ * 1.5.3 [omitted]
+ * 1.5.4beta01-08 15 10504 15.so.15.4[.0]
+ * 1.5.4rc01 15 10504 15.so.15.4[.0]
+ * 1.5.4 15 10504 15.so.15.4[.0]
*
* Henceforth the source version will match the shared-library major
* and minor numbers; the shared-library major version number will be
@@ -180,7 +188,7 @@
*
* This code is released under the libpng license.
*
- * libpng versions 1.2.6, August 15, 2004, through 1.5.2rc02, March 22, 2011, are
+ * libpng versions 1.2.6, August 15, 2004, through 1.5.4, July 7, 2011, are
* Copyright (c) 2004, 2006-2011 Glenn Randers-Pehrson, and are
* distributed according to the same disclaimer and license as libpng-1.2.5
* with the following individual added to the list of Contributing Authors:
@@ -292,25 +300,24 @@
* Y2K compliance in libpng:
* =========================
*
- * March 22, 2011
+ * July 7, 2011
*
* Since the PNG Development group is an ad-hoc body, we can't make
* an official declaration.
*
* This is your unofficial assurance that libpng from version 0.71 and
- * upward through 1.5.2rc02 are Y2K compliant. It is my belief that
+ * upward through 1.5.4 are Y2K compliant. It is my belief that
* earlier versions were also Y2K compliant.
*
- * Libpng only has three year fields. One is a 2-byte unsigned integer
- * that will hold years up to 65535. The other two hold the date in text
+ * Libpng only has two year fields. One is a 2-byte unsigned integer
+ * that will hold years up to 65535. The other holds the date in text
* format, and will hold years up to 9999.
*
* The integer is
* "png_uint_16 year" in png_time_struct.
*
- * The strings are
- * "png_charp time_buffer" in png_struct and
- * "near_time_buffer", which is a local character string in png.c.
+ * The string is
+ * "png_char time_buffer" in png_struct
*
* There are seven time-related functions:
* png.c: png_convert_to_rfc_1123() in png.c
@@ -354,9 +361,9 @@
*/
/* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.5.2rc02"
+#define PNG_LIBPNG_VER_STRING "1.5.4"
#define PNG_HEADER_VERSION_STRING \
- " libpng version 1.5.2rc02 - March 22, 2011\n"
+ " libpng version 1.5.4 - July 7, 2011\n"
#define PNG_LIBPNG_VER_SONUM 15
#define PNG_LIBPNG_VER_DLLNUM 15
@@ -364,12 +371,12 @@
/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
#define PNG_LIBPNG_VER_MAJOR 1
#define PNG_LIBPNG_VER_MINOR 5
-#define PNG_LIBPNG_VER_RELEASE 2
+#define PNG_LIBPNG_VER_RELEASE 4
/* This should match the numeric part of the final component of
* PNG_LIBPNG_VER_STRING, omitting any leading zero:
*/
-#define PNG_LIBPNG_VER_BUILD 02
+#define PNG_LIBPNG_VER_BUILD 0
/* Release Status */
#define PNG_LIBPNG_BUILD_ALPHA 1
@@ -394,7 +401,7 @@
* version 1.0.0 was mis-numbered 100 instead of 10000). From
* version 1.0.1 it's xxyyzz, where x=major, y=minor, z=release
*/
-#define PNG_LIBPNG_VER 10502 /* 1.5.2 */
+#define PNG_LIBPNG_VER 10504 /* 1.5.4 */
/* Library configuration: these options cannot be changed after
* the library has been built.
@@ -407,19 +414,24 @@
#endif
#ifndef PNG_VERSION_INFO_ONLY
-/* Standard header files (not needed for the version info) */
-# ifdef PNG_SETJMP_SUPPORTED
-# include <setjmp.h>
+# ifndef PNG_BUILDING_SYMBOL_TABLE
+ /*
+ * Standard header files (not needed for the version info or while
+ * building symbol table -- see scripts/pnglibconf.dfa)
+ */
+# ifdef PNG_SETJMP_SUPPORTED
+# include <setjmp.h>
+# endif
+
+ /* Need the time information for converting tIME chunks, it
+ * defines struct tm:
+ */
+# ifdef PNG_CONVERT_tIME_SUPPORTED
+ /* "time.h" functions are not supported on all operating systems */
+# include <time.h>
+# endif
# endif
-/* Need the time information for converting tIME chunks, it
- * defines struct tm:
- */
-#ifdef PNG_CONVERT_tIME_SUPPORTED
- /* "time.h" functions are not supported on all operating systems */
-# include <time.h>
-#endif
-
/* Machine specific configuration. */
# include "pngconf.h"
#endif
@@ -511,7 +523,7 @@ extern "C" {
/* This triggers a compiler error in png.c, if png.c and png.h
* do not agree upon the version number.
*/
-typedef char* png_libpng_version_1_5_2rc02;
+typedef char* png_libpng_version_1_5_4;
/* Three color definitions. The order of the red, green, and blue, (and the
* exact size) is not important, although the size of the fields need to
@@ -588,7 +600,7 @@ typedef png_sPLT_t FAR * FAR * png_sPLT_tpp;
* and whether that contents is compressed or not. The "key" field
* points to a regular zero-terminated C string. The "text", "lang", and
* "lang_key" fields can be regular C strings, empty strings, or NULL pointers.
- * However, the * structure returned by png_get_text() will always contain
+ * However, the structure returned by png_get_text() will always contain
* regular zero-terminated C strings (possibly empty), never NULL pointers,
* so they can be safely used in printf() and other string-handling functions.
*/
@@ -659,12 +671,24 @@ typedef struct png_unknown_chunk_t
/* libpng-using applications should NOT directly modify this byte. */
png_byte location; /* mode of operation at read time */
}
+
+
png_unknown_chunk;
typedef png_unknown_chunk FAR * png_unknown_chunkp;
typedef PNG_CONST png_unknown_chunk FAR * png_const_unknown_chunkp;
typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp;
#endif
+/* Values for the unknown chunk location byte */
+
+#define PNG_HAVE_IHDR 0x01
+#define PNG_HAVE_PLTE 0x02
+#define PNG_AFTER_IDAT 0x08
+
+/* The complete definition of png_info has, as of libpng-1.5.0,
+ * been moved into a separate header file that is not accessible to
+ * applications. Read libpng-manual.txt or libpng.3 for more info.
+ */
typedef struct png_info_def png_info;
typedef png_info FAR * png_infop;
typedef PNG_CONST png_info FAR * png_const_infop;
@@ -680,6 +704,8 @@ typedef png_info FAR * FAR * png_infopp;
*/
#define PNG_FP_1 100000
#define PNG_FP_HALF 50000
+#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL)
+#define PNG_FP_MIN (-PNG_FP_MAX)
/* These describe the color_type field in png_info. */
/* color type masks */
@@ -786,6 +812,14 @@ typedef struct png_row_info_struct
typedef png_row_info FAR * png_row_infop;
typedef png_row_info FAR * FAR * png_row_infopp;
+/* The complete definition of png_struct has, as of libpng-1.5.0,
+ * been moved into a separate header file that is not accessible to
+ * applications. Read libpng-manual.txt or libpng.3 for more info.
+ */
+typedef struct png_struct_def png_struct;
+typedef PNG_CONST png_struct FAR * png_const_structp;
+typedef png_struct FAR * png_structp;
+
/* These are the function types for the I/O functions and for the functions
* that allow the user to override the default I/O functions with his or her
* own. The png_error_ptr type should match that of user-supplied warning
@@ -794,10 +828,6 @@ typedef png_row_info FAR * FAR * png_row_infopp;
* modify the buffer it is passed. The 'read' function, on the other hand, is
* expected to return the read data in the buffer.
*/
-typedef struct png_struct_def png_struct;
-typedef PNG_CONST png_struct FAR * png_const_structp;
-typedef png_struct FAR * png_structp;
-
typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t));
typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
@@ -839,25 +869,18 @@ typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp));
#endif
#ifdef PNG_SETJMP_SUPPORTED
-/* This must match the function definition in <setjmp.h>, and the
- * application must include this before png.h to obtain the definition
- * of jmp_buf. The function is required to be PNG_NORETURN. (Note that
- * PNG_PTR_NORETURN is used here because current versions of the Microsoft
- * C compiler do not support the PNG_NORETURN attribute on a pointer.)
- *
- * If you get a type warning from the compiler when linking against this line
- * then your compiler has 'longjmp' that does not match the requirements of the
- * compiler that built libpng. You will have to write a wrapper function for
- * your compiler's longjmp and call png_set_longjmp_fn directly (not via the
- * png_jmpbuf macro.)
- *
- * If you get a warning here while building the library you will need to make
+/* This must match the function definition in <setjmp.h>, and the application
+ * must include this before png.h to obtain the definition of jmp_buf. The
+ * function is required to be PNG_NORETURN, but this is not checked. If the
+ * function does return the application will crash via an abort() or similar
+ * system level call.
+ *
+ * If you get a warning here while building the library you may need to make
* changes to ensure that pnglibconf.h records the calling convention used by
* your compiler. This may be very difficult - try using a different compiler
* to build the library!
*/
-typedef PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)),
- PNG_PTR_NORETURN);
+PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef);
#endif
/* Transform masks for the high-level interface */
@@ -879,6 +902,9 @@ typedef PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)),
#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */
/* Added to libpng-1.4.0 */
#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */
+/* Added to libpng-1.5.4 */
+#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */
+#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */
/* Flags for MNG supported features */
#define PNG_FLAG_MNG_EMPTY_PLTE 0x01
@@ -1073,7 +1099,7 @@ PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structp png_ptr));
#endif
#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* Expand to 16 bit channels, forces conversion of palette to RGB and expansion
+/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion
* of a tRNS chunk if present.
*/
PNG_EXPORT(221, void, png_set_expand_16, (png_structp png_ptr));
@@ -1100,8 +1126,219 @@ PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structp
png_ptr));
#endif
+#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
png_colorp palette));
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+/* How the alpha channel is interpreted - this affects how the color channels of
+ * a PNG file are returned when an alpha channel, or tRNS chunk in a palette
+ * file, is present.
+ *
+ * This has no effect on the way pixels are written into a PNG output
+ * datastream. The color samples in a PNG datastream are never premultiplied
+ * with the alpha samples.
+ *
+ * The default is to return data according to the PNG specification: the alpha
+ * channel is a linear measure of the contribution of the pixel to the
+ * corresponding composited pixel. The gamma encoded color channels must be
+ * scaled according to the contribution and to do this it is necessary to undo
+ * the encoding, scale the color values, perform the composition and reencode
+ * the values. This is the 'PNG' mode.
+ *
+ * The alternative is to 'associate' the alpha with the color information by
+ * storing color channel values that have been scaled by the alpha. The
+ * advantage is that the color channels can be resampled (the image can be
+ * scaled) in this form. The disadvantage is that normal practice is to store
+ * linear, not (gamma) encoded, values and this requires 16-bit channels for
+ * still images rather than the 8-bit channels that are just about sufficient if
+ * gamma encoding is used. In addition all non-transparent pixel values,
+ * including completely opaque ones, must be gamma encoded to produce the final
+ * image. This is the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' mode (the
+ * latter being the two common names for associated alpha color channels.)
+ *
+ * Since it is not necessary to perform arithmetic on opaque color values so
+ * long as they are not to be resampled and are in the final color space it is
+ * possible to optimize the handling of alpha by storing the opaque pixels in
+ * the PNG format (adjusted for the output color space) while storing partially
+ * opaque pixels in the standard, linear, format. The accuracy required for
+ * standard alpha composition is relatively low, because the pixels are
+ * isolated, therefore typically the accuracy loss in storing 8-bit linear
+ * values is acceptable. (This is not true if the alpha channel is used to
+ * simulate transparency over large areas - use 16 bits or the PNG mode in
+ * this case!) This is the 'OPTIMIZED' mode. For this mode a pixel is
+ * treated as opaque only if the alpha value is equal to the maximum value.
+ *
+ * The final choice is to gamma encode the alpha channel as well. This is
+ * broken because, in practice, no implementation that uses this choice
+ * correctly undoes the encoding before handling alpha composition. Use this
+ * choice only if other serious errors in the software or hardware you use
+ * mandate it; the typical serious error is for dark halos to appear around
+ * opaque areas of the composited PNG image because of arithmetic overflow.
+ *
+ * The API function png_set_alpha_mode specifies which of these choices to use
+ * with an enumerated 'mode' value and the gamma of the required output:
+ */
+#define PNG_ALPHA_PNG 0 /* according to the PNG standard */
+#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */
+#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */
+#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */
+#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */
+#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */
+
+PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structp png_ptr, int mode,
+ double output_gamma));
+PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structp png_ptr,
+ int mode, png_fixed_point output_gamma));
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+/* The output_gamma value is a screen gamma in libpng terminology: it expresses
+ * how to decode the output values, not how they are encoded. The values used
+ * correspond to the normal numbers used to describe the overall gamma of a
+ * computer display system; for example 2.2 for an sRGB conformant system. The
+ * values are scaled by 100000 in the _fixed version of the API (so 220000 for
+ * sRGB.)
+ *
+ * The inverse of the value is always used to provide a default for the PNG file
+ * encoding if it has no gAMA chunk and if png_set_gamma() has not been called
+ * to override the PNG gamma information.
+ *
+ * When the ALPHA_OPTIMIZED mode is selected the output gamma is used to encode
+ * opaque pixels however pixels with lower alpha values are not encoded,
+ * regardless of the output gamma setting.
+ *
+ * When the standard Porter Duff handling is requested with mode 1 the output
+ * encoding is set to be linear and the output_gamma value is only relevant
+ * as a default for input data that has no gamma information. The linear output
+ * encoding will be overridden if png_set_gamma() is called - the results may be
+ * highly unexpected!
+ *
+ * The following numbers are derived from the sRGB standard and the research
+ * behind it. sRGB is defined to be approximated by a PNG gAMA chunk value of
+ * 0.45455 (1/2.2) for PNG. The value implicitly includes any viewing
+ * correction required to take account of any differences in the color
+ * environment of the original scene and the intended display environment; the
+ * value expresses how to *decode* the image for display, not how the original
+ * data was *encoded*.
+ *
+ * sRGB provides a peg for the PNG standard by defining a viewing environment.
+ * sRGB itself, and earlier TV standards, actually use a more complex transform
+ * (a linear portion then a gamma 2.4 power law) than PNG can express. (PNG is
+ * limited to simple power laws.) By saying that an image for direct display on
+ * an sRGB conformant system should be stored with a gAMA chunk value of 45455
+ * (11.3.3.2 and 11.3.3.5 of the ISO PNG specification) the PNG specification
+ * makes it possible to derive values for other display systems and
+ * environments.
+ *
+ * The Mac value is deduced from the sRGB based on an assumption that the actual
+ * extra viewing correction used in early Mac display systems was implemented as
+ * a power 1.45 lookup table.
+ *
+ * Any system where a programmable lookup table is used or where the behavior of
+ * the final display device characteristics can be changed requires system
+ * specific code to obtain the current characteristic. However this can be
+ * difficult and most PNG gamma correction only requires an approximate value.
+ *
+ * By default, if png_set_alpha_mode() is not called, libpng assumes that all
+ * values are unencoded, linear, values and that the output device also has a
+ * linear characteristic. This is only very rarely correct - it is invariably
+ * better to call png_set_alpha_mode() with PNG_DEFAULT_sRGB than rely on the
+ * default if you don't know what the right answer is!
+ *
+ * The special value PNG_GAMMA_MAC_18 indicates an older Mac system (pre Mac OS
+ * 10.6) which used a correction table to implement a somewhat lower gamma on an
+ * otherwise sRGB system.
+ *
+ * Both these values are reserved (not simple gamma values) in order to allow
+ * more precise correction internally in the future.
+ *
+ * NOTE: the following values can be passed to either the fixed or floating
+ * point APIs, but the floating point API will also accept floating point
+ * values.
+ */
+#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */
+#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */
+#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */
+#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */
+#endif
+
+/* The following are examples of calls to png_set_alpha_mode to achieve the
+ * required overall gamma correction and, where necessary, alpha
+ * premultiplication.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
+ * This is the default libpng handling of the alpha channel - it is not
+ * pre-multiplied into the color components. In addition the call states
+ * that the output is for a sRGB system and causes all PNG files without gAMA
+ * chunks to be assumed to be encoded using sRGB.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
+ * In this case the output is assumed to be something like an sRGB conformant
+ * display preceeded by a power-law lookup table of power 1.45. This is how
+ * early Mac systems behaved.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR);
+ * This is the classic Jim Blinn approach and will work in academic
+ * environments where everything is done by the book. It has the shortcoming
+ * of assuming that input PNG data with no gamma information is linear - this
+ * is unlikely to be correct unless the PNG files where generated locally.
+ * Most of the time the output precision will be so low as to show
+ * significant banding in dark areas of the image.
+ *
+ * png_set_expand_16(pp);
+ * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB);
+ * This is a somewhat more realistic Jim Blinn inspired approach. PNG files
+ * are assumed to have the sRGB encoding if not marked with a gamma value and
+ * the output is always 16 bits per component. This permits accurate scaling
+ * and processing of the data. If you know that your input PNG files were
+ * generated locally you might need to replace PNG_DEFAULT_sRGB with the
+ * correct value for your system.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB);
+ * If you just need to composite the PNG image onto an existing background
+ * and if you control the code that does this you can use the optimization
+ * setting. In this case you just copy completely opaque pixels to the
+ * output. For pixels that are not completely transparent (you just skip
+ * those) you do the composition math using png_composite or png_composite_16
+ * below then encode the resultant 8-bit or 16-bit values to match the output
+ * encoding.
+ *
+ * Other cases
+ * If neither the PNG nor the standard linear encoding work for you because
+ * of the software or hardware you use then you have a big problem. The PNG
+ * case will probably result in halos around the image. The linear encoding
+ * will probably result in a washed out, too bright, image (it's actually too
+ * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably
+ * substantially reduce the halos. Alternatively try:
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB);
+ * This option will also reduce the halos, but there will be slight dark
+ * halos round the opaque parts of the image where the background is light.
+ * In the OPTIMIZED mode the halos will be light halos where the background
+ * is dark. Take your pick - the halos are unavoidable unless you can get
+ * your hardware/software fixed! (The OPTIMIZED approach is slightly
+ * faster.)
+ *
+ * When the default gamma of PNG files doesn't match the output gamma.
+ * If you have PNG files with no gamma information png_set_alpha_mode allows
+ * you to provide a default gamma, but it also sets the ouput gamma to the
+ * matching value. If you know your PNG files have a gamma that doesn't
+ * match the output you can take advantage of the fact that
+ * png_set_alpha_mode always sets the output gamma but only sets the PNG
+ * default if it is not already set:
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
+ * The first call sets both the default and the output gamma values, the
+ * second call overrides the output gamma without changing the default. This
+ * is easier than achieving the same effect with png_set_gamma. You must use
+ * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will
+ * fire if more than one call to png_set_alpha_mode and png_set_background is
+ * made in the same read operation, however multiple calls with PNG_ALPHA_PNG
+ * are ignored.
+ */
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
PNG_EXPORT(36, void, png_set_strip_alpha, (png_structp png_ptr));
@@ -1169,7 +1406,11 @@ PNG_EXPORT(46, void, png_set_invert_mono, (png_structp png_ptr));
#endif
#ifdef PNG_READ_BACKGROUND_SUPPORTED
-/* Handle alpha and tRNS by replacing with a background color. */
+/* Handle alpha and tRNS by replacing with a background color. Prior to
+ * libpng-1.5.4 this API must not be called before the PNG file header has been
+ * read. Doing so will result in unexpected behavior and possible warnings or
+ * errors if the PNG file contains a bKGD chunk.
+ */
PNG_FP_EXPORT(47, void, png_set_background, (png_structp png_ptr,
png_const_color_16p background_color, int background_gamma_code,
int need_expand, double background_gamma));
@@ -1184,7 +1425,13 @@ PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structp png_ptr,
# define PNG_BACKGROUND_GAMMA_UNIQUE 3
#endif
-#ifdef PNG_READ_16_TO_8_SUPPORTED
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+/* Scale a 16-bit depth file down to 8-bit, accurately. */
+PNG_EXPORT(229, void, png_set_scale_16, (png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+#define PNG_READ_16_TO_8 SUPPORTED /* Name prior to 1.5.4 */
/* Strip the second byte of information from a 16-bit depth file. */
PNG_EXPORT(48, void, png_set_strip_16, (png_structp png_ptr));
#endif
@@ -1205,12 +1452,22 @@ PNG_EXPORT(49, void, png_set_quantize,
*/
#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001)
-/* Handle gamma correction. Screen_gamma=(display_exponent) */
+/* Handle gamma correction. Screen_gamma=(display_exponent).
+ * NOTE: this API simply sets the screen and file gamma values. It will
+ * therefore override the value for gamma in a PNG file if it is called after
+ * the file header has been read - use with care - call before reading the PNG
+ * file for best results!
+ *
+ * These routines accept the same gamma values as png_set_alpha_mode (described
+ * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either
+ * API (floating point or fixed.) Notice, however, that the 'file_gamma' value
+ * is the inverse of a 'screen gamma' value.
+ */
PNG_FP_EXPORT(50, void, png_set_gamma,
(png_structp png_ptr, double screen_gamma,
- double default_file_gamma));
+ double override_file_gamma));
PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structp png_ptr,
- png_fixed_point screen_gamma, png_fixed_point default_file_gamma));
+ png_fixed_point screen_gamma, png_fixed_point override_file_gamma));
#endif
#ifdef PNG_WRITE_FLUSH_SUPPORTED
@@ -1385,6 +1642,7 @@ PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */
#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */
+#ifdef PNG_WRITE_SUPPORTED
/* Set the library compression level. Currently, valid values range from
* 0 - 9, corresponding directly to the zlib compression levels 0 - 9
* (0 - no compression, 9 - "maximal" compression). Note that tests have
@@ -1401,11 +1659,36 @@ PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structp png_ptr,
PNG_EXPORT(71, void, png_set_compression_strategy, (png_structp png_ptr,
int strategy));
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structp png_ptr,
int window_bits));
PNG_EXPORT(73, void, png_set_compression_method, (png_structp png_ptr,
int method));
+#endif
+
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+/* Also set zlib parameters for compressing non-IDAT chunks */
+PNG_EXPORT(222, void, png_set_text_compression_level,
+ (png_structp png_ptr, int level));
+
+PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structp png_ptr,
+ int mem_level));
+
+PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structp png_ptr,
+ int strategy));
+
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
+PNG_EXPORT(225, void, png_set_text_compression_window_bits, (png_structp
+ png_ptr, int window_bits));
+
+PNG_EXPORT(226, void, png_set_text_compression_method, (png_structp png_ptr,
+ int method));
+#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
/* These next functions are called for input/output, memory, and error
* handling. They are in the file pngrio.c, pngwio.c, and pngerror.c,
@@ -1617,6 +1900,7 @@ PNG_EXPORTA(103, void, png_chunk_error, (png_structp png_ptr,
PNG_EXPORTA(104, void, png_err, (png_structp png_ptr), PNG_NORETURN);
#endif
+#ifdef PNG_WARNINGS_SUPPORTED
/* Non-fatal error in libpng. Can continue, but may have a problem. */
PNG_EXPORT(105, void, png_warning, (png_structp png_ptr,
png_const_charp warning_message));
@@ -1624,6 +1908,7 @@ PNG_EXPORT(105, void, png_warning, (png_structp png_ptr,
/* Non-fatal error in libpng, chunk name is prepended to message. */
PNG_EXPORT(106, void, png_chunk_warning, (png_structp png_ptr,
png_const_charp warning_message));
+#endif
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
/* Benign error in libpng. Can continue, but may have a problem.
@@ -2291,7 +2576,7 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
* scripts/symbols.def as well.
*/
#ifdef PNG_EXPORT_LAST_ORDINAL
- PNG_EXPORT_LAST_ORDINAL(221);
+ PNG_EXPORT_LAST_ORDINAL(229);
#endif
#ifdef __cplusplus
diff --git a/pngconf.h b/pngconf.h
index f021929d8..f0e0da035 100644
--- a/pngconf.h
+++ b/pngconf.h
@@ -1,7 +1,7 @@
/* pngconf.h - machine configurable file for libpng
*
- * libpng version 1.5.2rc02 - (PENDING RELEASE)
+ * libpng version 1.5.4 - July 7, 2011
*
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
@@ -14,6 +14,7 @@
* This file has been modified, by Glenn Randers-Pehrson, from the original
* libpng distribution by adding a line reading
* #include "pngcrush.h"
+ *
*/
/* Any machine specific code is near the front of this file, so if you
@@ -27,29 +28,31 @@
#include "pngcrush.h"
+#ifndef PNG_BUILDING_SYMBOL_TABLE
/* PNG_NO_LIMITS_H may be used to turn off the use of the standard C
* definition file for machine specific limits, this may impact the
* correctness of the definitons below (see uses of INT_MAX).
*/
-#ifndef PNG_NO_LIMITS_H
-# include <limits.h>
-#endif
+# ifndef PNG_NO_LIMITS_H
+# include <limits.h>
+# endif
/* For the memory copy APIs (i.e. the standard definitions of these),
* because this file defines png_memcpy and so on the base APIs must
* be defined here.
*/
-#ifdef BSD
-# include <strings.h>
-#else
-# include <string.h>
-#endif
+# ifdef BSD
+# include <strings.h>
+# else
+# include <string.h>
+# endif
/* For png_FILE_p - this provides the standard definition of a
* FILE
*/
-#ifdef PNG_STDIO_SUPPORTED
-# include <stdio.h>
+# ifdef PNG_STDIO_SUPPORTED
+# include <stdio.h>
+# endif
#endif
/* This controls optimization of the reading of 16 and 32 bit values
@@ -356,23 +359,6 @@
# ifndef PNG_NORETURN
# define PNG_NORETURN __attribute__((__noreturn__))
# endif
-# ifndef PNG_PTR_NORETURN
- /* It's not enough to have the compiler be the correct compiler at
- * this point - it's necessary for the library (which defines
- * the type of the library longjmp) to also be the GNU library.
- * This is because many systems use the GNU compiler with a
- * non-GNU libc implementation. Min/GW headers are also compatible
- * with GCC as well as uclibc, so it seems best to exclude known
- * problem libcs here rather than just including known libcs.
- *
- * NOTE: this relies on the only use of PNG_PTR_NORETURN being with
- * the system longjmp. If the same type is used elsewhere then this
- * will need to be changed.
- */
-# if !defined(__CYGWIN__)
-# define PNG_PTR_NORETURN __attribute__((__noreturn__))
-# endif
-# endif
# ifndef PNG_ALLOCATED
# define PNG_ALLOCATED __attribute__((__malloc__))
# endif
@@ -385,9 +371,6 @@
# ifndef PNG_DEPRECATED
# define PNG_DEPRECATED __attribute__((__deprecated__))
# endif
-# ifndef PNG_DEPSTRUCT
-# define PNG_DEPSTRUCT __attribute__((__deprecated__))
-# endif
# ifndef PNG_PRIVATE
# if 0 /* Doesn't work so we use deprecated instead*/
# define PNG_PRIVATE \
@@ -407,11 +390,10 @@
# ifndef PNG_NORETURN
# define PNG_NORETURN __declspec(noreturn)
# endif
-# ifndef PNG_PTR_NORETURN
-# define PNG_PTR_NORETURN /* not supported */
-# endif
# ifndef PNG_ALLOCATED
-# define PNG_ALLOCATED __declspec(restrict)
+# if (_MSC_VER >= 1400)
+# define PNG_ALLOCATED __declspec(restrict)
+# endif
# endif
/* This specifically protects structure members that should only be
@@ -422,9 +404,6 @@
# ifndef PNG_DEPRECATED
# define PNG_DEPRECATED __declspec(deprecated)
# endif
-# ifndef PNG_DEPSTRUCT
-# define PNG_DEPSTRUCT __declspec(deprecated)
-# endif
# ifndef PNG_PRIVATE
# define PNG_PRIVATE __declspec(deprecated)
# endif
@@ -441,15 +420,9 @@
#ifndef PNG_NORETURN
# define PNG_NORETURN /* This function does not return */
#endif
-#ifndef PNG_PTR_NORETURN
-# define PNG_PTR_NORETURN /* This function does not return */
-#endif
#ifndef PNG_ALLOCATED
# define PNG_ALLOCATED /* The result of the function is new memory */
#endif
-#ifndef PNG_DEPSTRUCT
-# define PNG_DEPSTRUCT /* Access to this struct member is deprecated */
-#endif
#ifndef PNG_PRIVATE
# define PNG_PRIVATE /* This is a private libpng function */
#endif
diff --git a/pngcrush.c b/pngcrush.c
index be42ce108..87d0b1325 100644
--- a/pngcrush.c
+++ b/pngcrush.c
@@ -57,7 +57,7 @@
*
*/
-#define PNGCRUSH_VERSION "1.7.15"
+#define PNGCRUSH_VERSION "1.7.16"
/*
#define PNGCRUSH_COUNT_COLORS
@@ -111,6 +111,20 @@
* Reset CINFO to reflect decoder's required window size (instead of
* libz-1.1.3 encoder's required window size, which is 262 bytes larger).
* See discussion about zlib in png-list archives for April 2001.
+ * libpng-1.2.9 does some of this and libpng-1.5.4 does better.
+ * But neither has access to the entire datastream, so pngcrush could
+ * do even better.
+ *
+ * In the meantime, one can just do the following and select the smallest
+ * window that does not increase the filesize:
+ *
+ * for w in 32 16 8 4 2 1 512
+ * do
+ * pngcrush -m <best method> -w $w $1 w-$w.png
+ * done
+ *
+ * Use pngcheck -v and look at the IDAT report to find out what window
+ * size is actually set in a png file.
*
* Add a "pcRu" ancillary chunk that keeps track of the best method,
* methods already tried, and whether "loco crushing" was effective.
@@ -118,7 +132,7 @@
* Try both transformed and untransformed colors when "-loco" is used.
*
* Check for unused alpha channel and ok-to-reduce-depth.
- * Take care that sBIT and bKGD data aren't lost when reducing images
+ * Take care that sBIT and bKGD data are not lost when reducing images
* from truecolor to grayscale.
*
* Rearrange palette to put most-used color first and transparent color
@@ -132,7 +146,9 @@
*
* Use an alternate write function for the trial passes, that
* simply counts bytes rather than actually writing to a file, to save wear
- * and tear on disk drives.
+ * and tear on disk drives (actually, on modern operating systems it
+ * will only save wear and tear on the memory cache) and possibly some
+ * CPU time.
*
* Remove text-handling and color-handling features and put
* those in a separate program or programs, to avoid unnecessary
@@ -146,7 +162,7 @@
* libpng (maybe IDAT-compression parts only?), instead handling virtually
* all chunks as opaque binary blocks that are copied to output file _once_,
* with IDATs alone replaced (either by best in-memory result or by original
- * _data_ resplit into bigger IDATs, if pngcrush can't match/beat). "edit"
+ * _data_ resplit into bigger IDATs, if pngcrush cannot match/beat). "edit"
* version should be similar to current code but more efficient: make
* _one_ pass through args list, creating table of PNG_UINTs for removal;
* then make initial pass through PNG image, creating (in-order) table of
@@ -160,6 +176,10 @@
Change log:
+Version 1.7.16 (built with libpng-1.5.4 and zlib-1.2.5)
+ Only report best method==0 if pngcrush cannot match the input filesize.
+ Otherwise, if there is no improvement, report the first matching method.
+
Version 1.7.15 (built with libpng-1.5.2rc02 and zlib-1.2.5)
Force bit_depth to 1, 2, or 4 when -plte_len is <=2, <=4, or <=16 and
the -bit_depth option is not present, to avoid writing invalid palette
@@ -689,6 +709,7 @@ Version 1.1.4: added ability to restrict brute_force to one or more filter
#error pngcrush-nolib requires libpng-1.4 or earlier
# include <zlib.h>
# endif
+
/* The following became unavailable to applications in libpng
* version 1.5.0
*/
@@ -3443,6 +3464,11 @@ int main(int argc, char *argv[])
best_length = (png_uint_32) 0xffffffff;
for (j = things_have_changed; j < MAX_METHODS; j++)
{
+ if (best == 0 && best_length == idat_length[j])
+ {
+ /* If no change, report the first match */
+ best = j;
+ }
if (best_length > idat_length[j])
{
best_length = idat_length[j];
@@ -5730,11 +5756,20 @@ int main(int argc, char *argv[])
{
total_input_length += input_length + output_length;
- if (!already_crushed && !image_is_immutable)
+ if (best == 0)
+ {
+ fprintf(STDERR,
+ " Best pngcrush method = 0 (settings undetermined) for %s",
+ outname);
+ }
+
+ else if (!already_crushed && !image_is_immutable)
{
- fprintf(STDERR, " Best pngcrush method = %d (fm %d zl %d zs %d) "
+ fprintf(STDERR,
+ " Best pngcrush method = %d (fm %d zl %d zs %d) "
"for %s\n", best, fm[best], lv[best], zs[best], outname);
}
+
if (idat_length[0] == idat_length[best])
fprintf(STDERR, " (no IDAT change)\n");
else if (idat_length[0] > idat_length[best])
diff --git a/pngdebug.h b/pngdebug.h
new file mode 100644
index 000000000..16f81fdd1
--- /dev/null
+++ b/pngdebug.h
@@ -0,0 +1,157 @@
+
+/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* Define PNG_DEBUG at compile time for debugging information. Higher
+ * numbers for PNG_DEBUG mean more debugging information. This has
+ * only been added since version 0.95 so it is not implemented throughout
+ * libpng yet, but more support will be added as needed.
+ *
+ * png_debug[1-2]?(level, message ,arg{0-2})
+ * Expands to a statement (either a simple expression or a compound
+ * do..while(0) statement) that outputs a message with parameter
+ * substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG
+ * is undefined, 0 or 1 every png_debug expands to a simple expression
+ * (actually ((void)0)).
+ *
+ * level: level of detail of message, starting at 0. A level 'n'
+ * message is preceded by 'n' tab characters (not implemented
+ * on Microsoft compilers unless PNG_DEBUG_FILE is also
+ * defined, to allow debug DLL compilation with no standard IO).
+ * message: a printf(3) style text string. A trailing '\n' is added
+ * to the message.
+ * arg: 0 to 2 arguments for printf(3) style substitution in message.
+ */
+#ifndef PNGDEBUG_H
+#define PNGDEBUG_H
+/* These settings control the formatting of messages in png.c and pngerror.c */
+/* Moved to pngdebug.h at 1.5.0 */
+# ifndef PNG_LITERAL_SHARP
+# define PNG_LITERAL_SHARP 0x23
+# endif
+# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
+# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
+# endif
+# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
+# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
+# endif
+# ifndef PNG_STRING_NEWLINE
+# define PNG_STRING_NEWLINE "\n"
+# endif
+
+#ifdef PNG_DEBUG
+# if (PNG_DEBUG > 0)
+# if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
+# include <crtdbg.h>
+# if (PNG_DEBUG > 1)
+# ifndef _DEBUG
+# define _DEBUG
+# endif
+# ifndef png_debug
+# define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)
+# endif
+# ifndef png_debug1
+# define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)
+# endif
+# ifndef png_debug2
+# define png_debug2(l,m,p1,p2) \
+ _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)
+# endif
+# endif
+# else /* PNG_DEBUG_FILE || !_MSC_VER */
+# ifndef PNG_STDIO_SUPPORTED
+# include <stdio.h> /* not included yet */
+# endif
+# ifndef PNG_DEBUG_FILE
+# define PNG_DEBUG_FILE stderr
+# endif /* PNG_DEBUG_FILE */
+
+# if (PNG_DEBUG > 1)
+/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on
+ * non-ISO compilers
+ */
+# ifdef __STDC__
+# ifndef png_debug
+# define png_debug(l,m) \
+ do { \
+ int num_tabs=l; \
+ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \
+ } while (0)
+# endif
+# ifndef png_debug1
+# define png_debug1(l,m,p1) \
+ do { \
+ int num_tabs=l; \
+ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \
+ } while (0)
+# endif
+# ifndef png_debug2
+# define png_debug2(l,m,p1,p2) \
+ do { \
+ int num_tabs=l; \
+ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \
+ } while (0)
+# endif
+# else /* __STDC __ */
+# ifndef png_debug
+# define png_debug(l,m) \
+ do { \
+ int num_tabs=l; \
+ char format[256]; \
+ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+ m,PNG_STRING_NEWLINE); \
+ fprintf(PNG_DEBUG_FILE,format); \
+ } while (0)
+# endif
+# ifndef png_debug1
+# define png_debug1(l,m,p1) \
+ do { \
+ int num_tabs=l; \
+ char format[256]; \
+ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+ m,PNG_STRING_NEWLINE); \
+ fprintf(PNG_DEBUG_FILE,format,p1); \
+ } while (0)
+# endif
+# ifndef png_debug2
+# define png_debug2(l,m,p1,p2) \
+ do { \
+ int num_tabs=l; \
+ char format[256]; \
+ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+ m,PNG_STRING_NEWLINE); \
+ fprintf(PNG_DEBUG_FILE,format,p1,p2); \
+ } while (0)
+# endif
+# endif /* __STDC __ */
+# endif /* (PNG_DEBUG > 1) */
+
+# endif /* _MSC_VER */
+# endif /* (PNG_DEBUG > 0) */
+#endif /* PNG_DEBUG */
+#ifndef png_debug
+# define png_debug(l, m) ((void)0)
+#endif
+#ifndef png_debug1
+# define png_debug1(l, m, p1) ((void)0)
+#endif
+#ifndef png_debug2
+# define png_debug2(l, m, p1, p2) ((void)0)
+#endif
+#endif /* PNGDEBUG_H */
diff --git a/pngerror.c b/pngerror.c
index 8290bb410..4d4cebafa 100644
--- a/pngerror.c
+++ b/pngerror.c
@@ -1,7 +1,7 @@
/* pngerror.c - stub functions for i/o and memory allocation
*
- * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -89,15 +89,121 @@ png_error,(png_structp png_ptr, png_const_charp error_message),PNG_NORETURN)
PNG_FUNCTION(void,PNGAPI
png_err,(png_structp png_ptr),PNG_NORETURN)
{
+ /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
+ * erroneously as '\0', instead of the empty string "". This was
+ * apparently an error, introduced in libpng-1.2.20, and png_default_error
+ * will crash in this case.
+ */
if (png_ptr != NULL && png_ptr->error_fn != NULL)
- (*(png_ptr->error_fn))(png_ptr, '\0');
+ (*(png_ptr->error_fn))(png_ptr, "");
/* If the custom handler doesn't exist, or if it returns,
use the default handler, which will not return. */
- png_default_error(png_ptr, '\0');
+ png_default_error(png_ptr, "");
}
#endif /* PNG_ERROR_TEXT_SUPPORTED */
+/* Utility to safely appends strings to a buffer. This never errors out so
+ * error checking is not required in the caller.
+ */
+size_t
+png_safecat(png_charp buffer, size_t bufsize, size_t pos,
+ png_const_charp string)
+{
+ if (buffer != NULL && pos < bufsize)
+ {
+ if (string != NULL)
+ while (*string != '\0' && pos < bufsize-1)
+ buffer[pos++] = *string++;
+
+ buffer[pos] = '\0';
+ }
+
+ return pos;
+}
+
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Utility to dump an unsigned value into a buffer, given a start pointer and
+ * and end pointer (which should point just *beyond* the end of the buffer!)
+ * Returns the pointer to the start of the formatted string.
+ */
+png_charp
+png_format_number(png_const_charp start, png_charp end, int format,
+ png_alloc_size_t number)
+{
+ int count = 0; /* number of digits output */
+ int mincount = 1; /* minimum number required */
+ int output = 0; /* digit output (for the fixed point format) */
+
+ *--end = '\0';
+
+ /* This is written so that the loop always runs at least once, even with
+ * number zero.
+ */
+ while (end > start && (number != 0 || count < mincount))
+ {
+
+ static const char digits[] = "0123456789ABCDEF";
+
+ switch (format)
+ {
+ case PNG_NUMBER_FORMAT_fixed:
+ /* Needs five digits (the fraction) */
+ mincount = 5;
+ if (output || number % 10 != 0)
+ {
+ *--end = digits[number % 10];
+ output = 1;
+ }
+ number /= 10;
+ break;
+
+ case PNG_NUMBER_FORMAT_02u:
+ /* Expects at least 2 digits. */
+ mincount = 2;
+ /* fall through */
+
+ case PNG_NUMBER_FORMAT_u:
+ *--end = digits[number % 10];
+ number /= 10;
+ break;
+
+ case PNG_NUMBER_FORMAT_02x:
+ /* This format expects at least two digits */
+ mincount = 2;
+ /* fall through */
+
+ case PNG_NUMBER_FORMAT_x:
+ *--end = digits[number & 0xf];
+ number >>= 4;
+ break;
+
+ default: /* an error */
+ number = 0;
+ break;
+ }
+
+ /* Keep track of the number of digits added */
+ ++count;
+
+ /* Float a fixed number here: */
+ if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start)
+ {
+ /* End of the fraction, but maybe nothing was output? In that case
+ * drop the decimal point. If the number is a true zero handle that
+ * here.
+ */
+ if (output)
+ *--end = '.';
+ else if (number == 0) /* and !output */
+ *--end = '0';
+ }
+ }
+
+ return end;
+}
+#endif
+
#ifdef PNG_WARNINGS_SUPPORTED
/* This function is called whenever there is a non-fatal error. This function
* should not be changed. If there is a need to handle warnings differently,
@@ -128,6 +234,115 @@ png_warning(png_structp png_ptr, png_const_charp warning_message)
else
png_default_warning(png_ptr, warning_message + offset);
}
+
+/* These functions support 'formatted' warning messages with up to
+ * PNG_WARNING_PARAMETER_COUNT parameters. In the format string the parameter
+ * is introduced by @<number>, where 'number' starts at 1. This follows the
+ * standard established by X/Open for internationalizable error messages.
+ */
+void
+png_warning_parameter(png_warning_parameters p, int number,
+ png_const_charp string)
+{
+ if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
+ (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
+}
+
+void
+png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
+ png_alloc_size_t value)
+{
+ char buffer[PNG_NUMBER_BUFFER_SIZE];
+ png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
+}
+
+void
+png_warning_parameter_signed(png_warning_parameters p, int number, int format,
+ png_int_32 value)
+{
+ png_alloc_size_t u;
+ png_charp str;
+ char buffer[PNG_NUMBER_BUFFER_SIZE];
+
+ /* Avoid overflow by doing the negate in a png_alloc_size_t: */
+ u = (png_alloc_size_t)value;
+ if (value < 0)
+ u = ~u + 1;
+
+ str = PNG_FORMAT_NUMBER(buffer, format, u);
+
+ if (value < 0 && str > buffer)
+ *--str = '-';
+
+ png_warning_parameter(p, number, str);
+}
+
+void
+png_formatted_warning(png_structp png_ptr, png_warning_parameters p,
+ png_const_charp message)
+{
+ /* The internal buffer is just 128 bytes - enough for all our messages,
+ * overflow doesn't happen because this code checks!
+ */
+ size_t i;
+ char msg[128];
+
+ for (i=0; i<(sizeof msg)-1 && *message != '\0'; ++i)
+ {
+ if (*message == '@')
+ {
+ int parameter = -1;
+ switch (*++message)
+ {
+ case '1':
+ parameter = 0;
+ break;
+
+ case '2':
+ parameter = 1;
+ break;
+
+ case '\0':
+ continue; /* To break out of the for loop above. */
+
+ default:
+ break;
+ }
+
+ if (parameter >= 0 && parameter < PNG_WARNING_PARAMETER_COUNT)
+ {
+ /* Append this parameter */
+ png_const_charp parm = p[parameter];
+ png_const_charp pend = p[parameter] + (sizeof p[parameter]);
+
+ /* No need to copy the trailing '\0' here, but there is no guarantee
+ * that parm[] has been initialized, so there is no guarantee of a
+ * trailing '\0':
+ */
+ for (; i<(sizeof msg)-1 && parm != '\0' && parm < pend; ++i)
+ msg[i] = *parm++;
+
+ ++message;
+ continue;
+ }
+
+ /* else not a parameter and there is a character after the @ sign; just
+ * copy that.
+ */
+ }
+
+ /* At this point *message can't be '\0', even in the bad parameter case
+ * above where there is a lone '@' at the end of the message string.
+ */
+ msg[i] = *message++;
+ }
+
+ /* i is always less than (sizeof msg), so: */
+ msg[i] = '\0';
+
+ /* And this is the formatted message: */
+ png_warning(png_ptr, msg);
+}
#endif /* PNG_WARNINGS_SUPPORTED */
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
@@ -185,8 +400,13 @@ png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp
{
buffer[iout++] = ':';
buffer[iout++] = ' ';
- png_memcpy(buffer + iout, error_message, PNG_MAX_ERROR_TEXT);
- buffer[iout + PNG_MAX_ERROR_TEXT - 1] = '\0';
+
+ iin = 0;
+ while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0')
+ buffer[iout++] = error_message[iin++];
+
+ /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */
+ buffer[iout] = '\0';
}
}
#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */
@@ -272,7 +492,7 @@ png_set_longjmp_fn(png_structp png_ptr, png_longjmp_ptr longjmp_fn,
return NULL;
png_ptr->longjmp_fn = longjmp_fn;
- return &png_ptr->png_jmpbuf;
+ return &png_ptr->longjmp_buffer;
}
#endif
@@ -287,7 +507,8 @@ png_default_error,(png_structp png_ptr, png_const_charp error_message),
{
#ifdef PNG_CONSOLE_IO_SUPPORTED
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- if (*error_message == PNG_LITERAL_SHARP)
+ /* Check on NULL only added in 1.5.4 */
+ if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
{
/* Strip "#nnnn " from beginning of error message. */
int offset;
@@ -317,11 +538,11 @@ png_default_error,(png_structp png_ptr, png_const_charp error_message),
else
#endif
{
- fprintf(stderr, "libpng error: %s", error_message);
+ fprintf(stderr, "libpng error: %s", error_message ? error_message :
+ "undefined");
fprintf(stderr, PNG_STRING_NEWLINE);
}
-#endif
-#ifndef PNG_CONSOLE_IO_SUPPORTED
+#else
PNG_UNUSED(error_message) /* Make compiler happy */
#endif
png_longjmp(png_ptr, 1);
@@ -335,13 +556,13 @@ png_longjmp,(png_structp png_ptr, int val),PNG_NORETURN)
{
# ifdef USE_FAR_KEYWORD
{
- jmp_buf png_jmpbuf;
- png_memcpy(png_jmpbuf, png_ptr->png_jmpbuf, png_sizeof(jmp_buf));
- png_ptr->longjmp_fn(png_jmpbuf, val);
+ jmp_buf tmp_jmpbuf;
+ png_memcpy(tmp_jmpbuf, png_ptr->longjmp_buffer, png_sizeof(jmp_buf));
+ png_ptr->longjmp_fn(tmp_jmpbuf, val);
}
# else
- png_ptr->longjmp_fn(png_ptr->png_jmpbuf, val);
+ png_ptr->longjmp_fn(png_ptr->longjmp_buffer, val);
# endif
}
#endif
@@ -403,7 +624,7 @@ png_default_warning(png_structp png_ptr, png_const_charp warning_message)
/* This function is called when the application wants to use another method
* of handling errors and warnings. Note that the error function MUST NOT
* return to the calling routine or serious problems will occur. The return
- * method used in the default routine calls longjmp(png_ptr->png_jmpbuf, 1)
+ * method used in the default routine calls longjmp(png_ptr->longjmp_buffer, 1)
*/
void PNGAPI
png_set_error_fn(png_structp png_ptr, png_voidp error_ptr,
@@ -414,7 +635,11 @@ png_set_error_fn(png_structp png_ptr, png_voidp error_ptr,
png_ptr->error_ptr = error_ptr;
png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
png_ptr->warning_fn = warning_fn;
+#else
+ PNG_UNUSED(warning_fn)
+#endif
}
diff --git a/pnginfo.h b/pnginfo.h
new file mode 100644
index 000000000..a33bfab06
--- /dev/null
+++ b/pnginfo.h
@@ -0,0 +1,269 @@
+
+/* pnginfo.h - header file for PNG reference library
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+ /* png_info is a structure that holds the information in a PNG file so
+ * that the application can find out the characteristics of the image.
+ * If you are reading the file, this structure will tell you what is
+ * in the PNG file. If you are writing the file, fill in the information
+ * you want to put into the PNG file, using png_set_*() functions, then
+ * call png_write_info().
+ *
+ * The names chosen should be very close to the PNG specification, so
+ * consult that document for information about the meaning of each field.
+ *
+ * With libpng < 0.95, it was only possible to directly set and read the
+ * the values in the png_info_struct, which meant that the contents and
+ * order of the values had to remain fixed. With libpng 0.95 and later,
+ * however, there are now functions that abstract the contents of
+ * png_info_struct from the application, so this makes it easier to use
+ * libpng with dynamic libraries, and even makes it possible to use
+ * libraries that don't have all of the libpng ancillary chunk-handing
+ * functionality. In libpng-1.5.0 this was moved into a separate private
+ * file that is not visible to applications.
+ *
+ * The following members may have allocated storage attached that should be
+ * cleaned up before the structure is discarded: palette, trans, text,
+ * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
+ * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these
+ * are automatically freed when the info structure is deallocated, if they were
+ * allocated internally by libpng. This behavior can be changed by means
+ * of the png_data_freer() function.
+ *
+ * More allocation details: all the chunk-reading functions that
+ * change these members go through the corresponding png_set_*
+ * functions. A function to clear these members is available: see
+ * png_free_data(). The png_set_* functions do not depend on being
+ * able to point info structure members to any of the storage they are
+ * passed (they make their own copies), EXCEPT that the png_set_text
+ * functions use the same storage passed to them in the text_ptr or
+ * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
+ * functions do not make their own copies.
+ */
+#ifndef PNGINFO_H
+#define PNGINFO_H
+
+struct png_info_def
+{
+ /* the following are necessary for every PNG file */
+ png_uint_32 width; /* width of image in pixels (from IHDR) */
+ png_uint_32 height; /* height of image in pixels (from IHDR) */
+ png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */
+ png_size_t rowbytes; /* bytes needed to hold an untransformed row */
+ png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */
+ png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
+ png_uint_16 num_trans; /* number of transparent palette color (tRNS) */
+ png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
+ png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */
+ /* The following three should have been named *_method not *_type */
+ png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
+ png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
+ png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+
+ /* The following is informational only on read, and not used on writes. */
+ png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */
+ png_byte pixel_depth; /* number of bits per pixel */
+ png_byte spare_byte; /* to align the data, and for future use */
+ png_byte signature[8]; /* magic bytes read by libpng from start of file */
+
+ /* The rest of the data is optional. If you are reading, check the
+ * valid field to see if the information in these are valid. If you
+ * are writing, set the valid field to those chunks you want written,
+ * and initialize the appropriate fields below.
+ */
+
+#if defined(PNG_gAMA_SUPPORTED)
+ /* The gAMA chunk describes the gamma characteristics of the system
+ * on which the image was created, normally in the range [1.0, 2.5].
+ * Data is valid if (valid & PNG_INFO_gAMA) is non-zero.
+ */
+ png_fixed_point gamma;
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+ /* GR-P, 0.96a */
+ /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */
+ png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+ /* The tEXt, and zTXt chunks contain human-readable textual data in
+ * uncompressed, compressed, and optionally compressed forms, respectively.
+ * The data in "text" is an array of pointers to uncompressed,
+ * null-terminated C strings. Each chunk has a keyword that describes the
+ * textual data contained in that chunk. Keywords are not required to be
+ * unique, and the text string may be empty. Any number of text chunks may
+ * be in an image.
+ */
+ int num_text; /* number of comments read or comments to write */
+ int max_text; /* current size of text array */
+ png_textp text; /* array of comments read or comments to write */
+#endif /* PNG_TEXT_SUPPORTED */
+
+#ifdef PNG_tIME_SUPPORTED
+ /* The tIME chunk holds the last time the displayed image data was
+ * modified. See the png_time struct for the contents of this struct.
+ */
+ png_time mod_time;
+#endif
+
+#ifdef PNG_sBIT_SUPPORTED
+ /* The sBIT chunk specifies the number of significant high-order bits
+ * in the pixel data. Values are in the range [1, bit_depth], and are
+ * only specified for the channels in the pixel data. The contents of
+ * the low-order bits is not specified. Data is valid if
+ * (valid & PNG_INFO_sBIT) is non-zero.
+ */
+ png_color_8 sig_bit; /* significant bits in color channels */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
+defined(PNG_READ_BACKGROUND_SUPPORTED)
+ /* The tRNS chunk supplies transparency data for paletted images and
+ * other image types that don't need a full alpha channel. There are
+ * "num_trans" transparency values for a paletted image, stored in the
+ * same order as the palette colors, starting from index 0. Values
+ * for the data are in the range [0, 255], ranging from fully transparent
+ * to fully opaque, respectively. For non-paletted images, there is a
+ * single color specified that should be treated as fully transparent.
+ * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
+ */
+ png_bytep trans_alpha; /* alpha values for paletted image */
+ png_color_16 trans_color; /* transparent color for non-palette image */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ /* The bKGD chunk gives the suggested image background color if the
+ * display program does not have its own background color and the image
+ * is needs to composited onto a background before display. The colors
+ * in "background" are normally in the same color space/depth as the
+ * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
+ */
+ png_color_16 background;
+#endif
+
+#ifdef PNG_oFFs_SUPPORTED
+ /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
+ * and downwards from the top-left corner of the display, page, or other
+ * application-specific co-ordinate space. See the PNG_OFFSET_ defines
+ * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero.
+ */
+ png_int_32 x_offset; /* x offset on page */
+ png_int_32 y_offset; /* y offset on page */
+ png_byte offset_unit_type; /* offset units type */
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+ /* The pHYs chunk gives the physical pixel density of the image for
+ * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
+ * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
+ */
+ png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
+ png_uint_32 y_pixels_per_unit; /* vertical pixel density */
+ png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+ /* The hIST chunk contains the relative frequency or importance of the
+ * various palette entries, so that a viewer can intelligently select a
+ * reduced-color palette, if required. Data is an array of "num_palette"
+ * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
+ * is non-zero.
+ */
+ png_uint_16p hist;
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+ /* The cHRM chunk describes the CIE color characteristics of the monitor
+ * on which the PNG was created. This data allows the viewer to do gamut
+ * mapping of the input image to ensure that the viewer sees the same
+ * colors in the image as the creator. Values are in the range
+ * [0.0, 0.8]. Data valid if (valid & PNG_INFO_cHRM) non-zero.
+ */
+ png_fixed_point x_white;
+ png_fixed_point y_white;
+ png_fixed_point x_red;
+ png_fixed_point y_red;
+ png_fixed_point x_green;
+ png_fixed_point y_green;
+ png_fixed_point x_blue;
+ png_fixed_point y_blue;
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+ /* The pCAL chunk describes a transformation between the stored pixel
+ * values and original physical data values used to create the image.
+ * The integer range [0, 2^bit_depth - 1] maps to the floating-point
+ * range given by [pcal_X0, pcal_X1], and are further transformed by a
+ * (possibly non-linear) transformation function given by "pcal_type"
+ * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_
+ * defines below, and the PNG-Group's PNG extensions document for a
+ * complete description of the transformations and how they should be
+ * implemented, and for a description of the ASCII parameter strings.
+ * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
+ */
+ png_charp pcal_purpose; /* pCAL chunk description string */
+ png_int_32 pcal_X0; /* minimum value */
+ png_int_32 pcal_X1; /* maximum value */
+ png_charp pcal_units; /* Latin-1 string giving physical units */
+ png_charpp pcal_params; /* ASCII strings containing parameter values */
+ png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */
+ png_byte pcal_nparams; /* number of parameters given in pcal_params */
+#endif
+
+/* New members added in libpng-1.0.6 */
+ png_uint_32 free_me; /* flags items libpng is responsible for freeing */
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) || \
+ defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
+ /* Storage for unknown chunks that the library doesn't recognize. */
+ png_unknown_chunkp unknown_chunks;
+ int unknown_chunks_num;
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+ /* iCCP chunk data. */
+ png_charp iccp_name; /* profile name */
+ png_bytep iccp_profile; /* International Color Consortium profile data */
+ png_uint_32 iccp_proflen; /* ICC profile data length */
+ png_byte iccp_compression; /* Always zero */
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+ /* Data on sPLT chunks (there may be more than one). */
+ png_sPLT_tp splt_palettes;
+ png_uint_32 splt_palettes_num;
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+ /* The sCAL chunk describes the actual physical dimensions of the
+ * subject matter of the graphic. The chunk contains a unit specification
+ * a byte value, and two ASCII strings representing floating-point
+ * values. The values are width and height corresponsing to one pixel
+ * in the image. Data values are valid if (valid & PNG_INFO_sCAL) is
+ * non-zero.
+ */
+ png_byte scal_unit; /* unit of physical scale */
+ png_charp scal_s_width; /* string containing height */
+ png_charp scal_s_height; /* string containing width */
+#endif
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+ /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)
+ non-zero */
+ /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
+ png_bytepp row_pointers; /* the image bits */
+#endif
+
+};
+#endif /* PNGINFO_H */
diff --git a/pnglibconf.h b/pnglibconf.h
new file mode 100644
index 000000000..827d17e54
--- /dev/null
+++ b/pnglibconf.h
@@ -0,0 +1,187 @@
+
+/* libpng STANDARD API DEFINITION */
+
+/* pnglibconf.h - library build configuration */
+
+/* libpng version 1.5.4 - last changed on June 22, 2011 */
+
+/* Copyright (c) 1998-2011 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 */
+
+/* pnglibconf.h */
+/* Derived from: scripts/pnglibconf.dfa */
+/* If you edit this file by hand you must obey the rules expressed in */
+/* pnglibconf.dfa with respect to the dependencies between the following */
+/* symbols. It is much better to generate a new file using */
+/* scripts/libpngconf.mak */
+
+#ifndef PNGLCONF_H
+#define PNGLCONF_H
+/* settings */
+#define PNG_API_RULE 0
+#define PNG_CALLOC_SUPPORTED
+#define PNG_COST_SHIFT 3
+#define PNG_DEFAULT_READ_MACROS 1
+#define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_MAX_GAMMA_8 11
+#define PNG_QUANTIZE_BLUE_BITS 5
+#define PNG_QUANTIZE_GREEN_BITS 5
+#define PNG_QUANTIZE_RED_BITS 5
+#define PNG_sCAL_PRECISION 5
+#define PNG_USER_CHUNK_CACHE_MAX 0
+#define PNG_USER_CHUNK_MALLOC_MAX 0
+#define PNG_USER_HEIGHT_MAX 1000000L
+#define PNG_USER_WIDTH_MAX 1000000L
+#define PNG_WEIGHT_SHIFT 8
+#define PNG_ZBUF_SIZE 8192
+/* end of settings */
+/* options */
+#define PNG_16BIT_SUPPORTED
+#define PNG_ALIGN_MEMORY_SUPPORTED
+#define PNG_BENIGN_ERRORS_SUPPORTED
+#define PNG_bKGD_SUPPORTED
+#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
+#define PNG_CHECK_cHRM_SUPPORTED
+#define PNG_cHRM_SUPPORTED
+#define PNG_CONSOLE_IO_SUPPORTED
+#define PNG_CONVERT_tIME_SUPPORTED
+#define PNG_EASY_ACCESS_SUPPORTED
+/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
+#define PNG_ERROR_TEXT_SUPPORTED
+#define PNG_FIXED_POINT_SUPPORTED
+#define PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define PNG_FLOATING_POINT_SUPPORTED
+#define PNG_gAMA_SUPPORTED
+#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#define PNG_hIST_SUPPORTED
+#define PNG_iCCP_SUPPORTED
+#define PNG_INCH_CONVERSIONS_SUPPORTED
+#define PNG_INFO_IMAGE_SUPPORTED
+#define PNG_IO_STATE_SUPPORTED
+#define PNG_iTXt_SUPPORTED
+#define PNG_MNG_FEATURES_SUPPORTED
+#define PNG_oFFs_SUPPORTED
+#define PNG_pCAL_SUPPORTED
+#define PNG_pHYs_SUPPORTED
+#define PNG_POINTER_INDEXING_SUPPORTED
+#define PNG_PROGRESSIVE_READ_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+#define PNG_READ_ALPHA_MODE_SUPPORTED
+#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_READ_BACKGROUND_SUPPORTED
+#define PNG_READ_BGR_SUPPORTED
+#define PNG_READ_bKGD_SUPPORTED
+#define PNG_READ_cHRM_SUPPORTED
+#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
+#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
+#define PNG_READ_EXPAND_16_SUPPORTED
+#define PNG_READ_EXPAND_SUPPORTED
+#define PNG_READ_FILLER_SUPPORTED
+#define PNG_READ_gAMA_SUPPORTED
+#define PNG_READ_GAMMA_SUPPORTED
+#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#define PNG_READ_hIST_SUPPORTED
+#define PNG_READ_iCCP_SUPPORTED
+#define PNG_READ_INTERLACING_SUPPORTED
+#define PNG_READ_INT_FUNCTIONS_SUPPORTED
+#define PNG_READ_INVERT_ALPHA_SUPPORTED
+#define PNG_READ_INVERT_SUPPORTED
+#define PNG_READ_iTXt_SUPPORTED
+#define PNG_READ_oFFs_SUPPORTED
+#define PNG_READ_OPT_PLTE_SUPPORTED
+#define PNG_READ_PACK_SUPPORTED
+#define PNG_READ_PACKSWAP_SUPPORTED
+#define PNG_READ_pCAL_SUPPORTED
+#define PNG_READ_pHYs_SUPPORTED
+#define PNG_READ_QUANTIZE_SUPPORTED
+#define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#define PNG_READ_sBIT_SUPPORTED
+#define PNG_READ_SCALE_16_TO_8_SUPPORTED
+#define PNG_READ_sCAL_SUPPORTED
+#define PNG_READ_SHIFT_SUPPORTED
+#define PNG_READ_sPLT_SUPPORTED
+#define PNG_READ_sRGB_SUPPORTED
+#define PNG_READ_STRIP_16_TO_8_SUPPORTED
+#define PNG_READ_STRIP_ALPHA_SUPPORTED
+#define PNG_READ_SUPPORTED
+#define PNG_READ_SWAP_ALPHA_SUPPORTED
+#define PNG_READ_SWAP_SUPPORTED
+#define PNG_READ_tEXt_SUPPORTED
+#define PNG_READ_TEXT_SUPPORTED
+#define PNG_READ_tIME_SUPPORTED
+#define PNG_READ_TRANSFORMS_SUPPORTED
+#define PNG_READ_tRNS_SUPPORTED
+#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_READ_USER_CHUNKS_SUPPORTED
+#define PNG_READ_USER_TRANSFORM_SUPPORTED
+#define PNG_READ_zTXt_SUPPORTED
+#define PNG_SAVE_INT_32_SUPPORTED
+#define PNG_sBIT_SUPPORTED
+#define PNG_sCAL_SUPPORTED
+#define PNG_SEQUENTIAL_READ_SUPPORTED
+#define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED
+#define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+#define PNG_SETJMP_SUPPORTED
+#define PNG_SET_USER_LIMITS_SUPPORTED
+#define PNG_sPLT_SUPPORTED
+#define PNG_sRGB_SUPPORTED
+#define PNG_STDIO_SUPPORTED
+#define PNG_tEXt_SUPPORTED
+#define PNG_TEXT_SUPPORTED
+#define PNG_TIME_RFC1123_SUPPORTED
+#define PNG_tIME_SUPPORTED
+#define PNG_tRNS_SUPPORTED
+#define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_USER_CHUNKS_SUPPORTED
+#define PNG_USER_LIMITS_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED
+#define PNG_USER_TRANSFORM_INFO_SUPPORTED
+#define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#define PNG_WARNINGS_SUPPORTED
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_WRITE_BGR_SUPPORTED
+#define PNG_WRITE_bKGD_SUPPORTED
+#define PNG_WRITE_cHRM_SUPPORTED
+#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+#define PNG_WRITE_FILLER_SUPPORTED
+#define PNG_WRITE_FILTER_SUPPORTED
+#define PNG_WRITE_FLUSH_SUPPORTED
+#define PNG_WRITE_gAMA_SUPPORTED
+#define PNG_WRITE_hIST_SUPPORTED
+#define PNG_WRITE_iCCP_SUPPORTED
+#define PNG_WRITE_INTERLACING_SUPPORTED
+#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#define PNG_WRITE_INVERT_SUPPORTED
+#define PNG_WRITE_iTXt_SUPPORTED
+#define PNG_WRITE_oFFs_SUPPORTED
+#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+#define PNG_WRITE_PACK_SUPPORTED
+#define PNG_WRITE_PACKSWAP_SUPPORTED
+#define PNG_WRITE_pCAL_SUPPORTED
+#define PNG_WRITE_pHYs_SUPPORTED
+#define PNG_WRITE_sBIT_SUPPORTED
+#define PNG_WRITE_sCAL_SUPPORTED
+#define PNG_WRITE_SHIFT_SUPPORTED
+#define PNG_WRITE_sPLT_SUPPORTED
+#define PNG_WRITE_sRGB_SUPPORTED
+#define PNG_WRITE_SUPPORTED
+#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#define PNG_WRITE_SWAP_SUPPORTED
+#define PNG_WRITE_tEXt_SUPPORTED
+#define PNG_WRITE_TEXT_SUPPORTED
+#define PNG_WRITE_tIME_SUPPORTED
+#define PNG_WRITE_TRANSFORMS_SUPPORTED
+#define PNG_WRITE_tRNS_SUPPORTED
+#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#define PNG_WRITE_zTXt_SUPPORTED
+#define PNG_zTXt_SUPPORTED
+/* end of options */
+#endif /* PNGLCONF_H */
diff --git a/pngmem.c b/pngmem.c
index a15d8b085..ea606d6a6 100644
--- a/pngmem.c
+++ b/pngmem.c
@@ -1,7 +1,7 @@
/* pngmem.c - stub functions for memory allocation
*
- * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -187,8 +187,9 @@ png_malloc_default,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
int num_blocks;
png_uint_32 total_size;
png_bytep table;
- int i;
+ int i, mem_level, window_bits;
png_byte huge * hptr;
+ int window_bits
if (ret != NULL)
{
@@ -196,14 +197,22 @@ png_malloc_default,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
ret = NULL;
}
- if (png_ptr->zlib_window_bits > 14)
- num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14));
+ window_bits =
+ png_ptr->zlib_window_bits >= png_ptr->zlib_text_window_bits ?
+ png_ptr->zlib_window_bits : png_ptr->zlib_text_window_bits;
+
+ if (window_bits > 14)
+ num_blocks = (int)(1 << (window_bits - 14));
else
num_blocks = 1;
- if (png_ptr->zlib_mem_level >= 7)
- num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7));
+ mem_level =
+ png_ptr->zlib_mem_level >= png_ptr->zlib_text_mem_level ?
+ png_ptr->zlib_mem_level : png_ptr->zlib_text_mem_level;
+
+ if (mem_level >= 7)
+ num_blocks += (int)(1 << (mem_level - 7));
else
num_blocks++;
@@ -277,7 +286,7 @@ png_malloc_default,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
{
# ifndef PNG_USER_MEM_SUPPORTED
if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
- png_error(png_ptr, "Out of Memory"); /* Note "o" and "M" */
+ png_error(png_ptr, "Out of Memory"); /* Note "O" and "M" */
else
png_warning(png_ptr, "Out of Memory");
diff --git a/pngpread.c b/pngpread.c
index 87020c44e..a50292a76 100644
--- a/pngpread.c
+++ b/pngpread.c
@@ -1,7 +1,7 @@
/* pngpread.c - read a png file in push mode
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.2 [March 31, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -1026,8 +1026,10 @@ png_push_process_row(png_structp png_ptr)
png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1);
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
if (png_ptr->transformations)
png_do_read_transformations(png_ptr);
+#endif
#ifdef PNG_READ_INTERLACING_SUPPORTED
/* Blow up interlaced rows to full size */
@@ -1288,7 +1290,7 @@ png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
{
PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
png_error(png_ptr, "Out of place tEXt");
- /*NOT REACHED*/
+ /* NOT REACHED */
}
#ifdef PNG_MAX_MALLOC_64K
@@ -1385,7 +1387,7 @@ png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
{
PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
png_error(png_ptr, "Out of place zTXt");
- /*NOT REACHED*/
+ /* NOT REACHED */
}
#ifdef PNG_MAX_MALLOC_64K
@@ -1589,7 +1591,7 @@ png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
{
PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
png_error(png_ptr, "Out of place iTXt");
- /*NOT REACHED*/
+ /* NOT REACHED */
}
#ifdef PNG_MAX_MALLOC_64K
diff --git a/pngpriv.h b/pngpriv.h
index e74afeb9e..5b4d2127c 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -6,7 +6,7 @@
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -25,6 +25,20 @@
#ifndef PNGPRIV_H
#define PNGPRIV_H
+/* Feature Test Macros. The following are defined here to ensure that correctly
+ * implemented libraries reveal the APIs libpng needs to build and hide those
+ * that are not needed and potentially damaging to the compilation.
+ *
+ * Feature Test Macros must be defined before any system header is included (see
+ * POSIX 1003.1 2.8.2 "POSIX Symbols."
+ *
+ * These macros only have an effect if the operating system supports either
+ * POSIX 1003.1 or C99, or both. On other operating systems (particularly
+ * Windows/Visual Studio) there is no effect; the OS specific tests below are
+ * still required (as of 2011-05-02.)
+ */
+#define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
+
/* This is required for the definition of abort(), used as a last ditch
* error handler when all else fails.
*/
@@ -101,12 +115,27 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
# define PNG_ZBUF_SIZE 65536L
#endif
-/* If warnings or errors are turned off the code is disabled
- * or redirected here.
+/* PNG_STATIC is used to mark internal file scope functions if they need to be
+ * accessed for implementation tests (see the code in tests/?*).
*/
-#ifndef PNG_WARNINGS_SUPPORTED
-# define png_warning(s1,s2) ((void)0)
-# define png_chunk_warning(s1,s2) ((void)0)
+#ifndef PNG_STATIC
+# define PNG_STATIC static
+#endif
+
+/* If warnings or errors are turned off the code is disabled or redirected here.
+ * From 1.5.4 functions have been added to allow very limited formatting of
+ * error and warning messages - this code will also be disabled here.
+ */
+#ifdef PNG_WARNINGS_SUPPORTED
+# define PNG_WARNING_PARAMETERS(p) png_warning_parameters p;
+#else
+# define png_warning(s1,s2) ((void)(s1))
+# define png_chunk_warning(s1,s2) ((void)(s1))
+# define png_warning_parameter(p,number,string) ((void)0)
+# define png_warning_parameter_unsigned(p,number,format,value) ((void)0)
+# define png_warning_parameter_signed(p,number,format,value) ((void)0)
+# define png_formatted_warning(pp,p,message) ((void)(pp))
+# define PNG_WARNING_PARAMETERS(p)
#endif
#ifndef PNG_ERROR_TEXT_SUPPORTED
# define png_error(s1,s2) png_err(s1)
@@ -200,60 +229,28 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
# define NOCHECK 0
# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK))
# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK))
-# define png_strcpy _fstrcpy
-# define png_strncpy _fstrncpy /* Added to v 1.2.6 */
# define png_strlen _fstrlen
# define png_memcmp _fmemcmp /* SJT: added */
# define png_memcpy _fmemcpy
# define png_memset _fmemset
-# define png_sprintf sprintf
#else
# ifdef _WINDOWS_ /* Favor Windows over C runtime fns */
# define CVT_PTR(ptr) (ptr)
# define CVT_PTR_NOCHECK(ptr) (ptr)
-# define png_strcpy lstrcpyA
-# define png_strncpy lstrcpynA
# define png_strlen lstrlenA
# define png_memcmp memcmp
# define png_memcpy CopyMemory
# define png_memset memset
-# define png_sprintf wsprintfA
# else
# define CVT_PTR(ptr) (ptr)
# define CVT_PTR_NOCHECK(ptr) (ptr)
-# define png_strcpy strcpy
-# define png_strncpy strncpy /* Added to v 1.2.6 */
# define png_strlen strlen
# define png_memcmp memcmp /* SJT: added */
# define png_memcpy memcpy
# define png_memset memset
-# define png_sprintf sprintf
# endif
#endif
/* End of memory model/platform independent support */
-
-#ifndef PNG_NO_SNPRINTF
-# ifdef _MSC_VER
-# define png_snprintf _snprintf /* Added to v 1.2.19 */
-# define png_snprintf2 _snprintf
-# define png_snprintf6 _snprintf
-# else
-# define png_snprintf snprintf /* Added to v 1.2.19 */
-# define png_snprintf2 snprintf
-# define png_snprintf6 snprintf
-# endif
-#else
- /* You don't have or don't want to use snprintf(). Caution: Using
- * sprintf instead of snprintf exposes your application to accidental
- * or malevolent buffer overflows. If you don't have snprintf()
- * as a general rule you should provide one (you can get one from
- * Portable OpenSSH).
- */
-# define png_snprintf(s1,n,fmt,x1) png_sprintf(s1,fmt,x1)
-# define png_snprintf2(s1,n,fmt,x1,x2) png_sprintf(s1,fmt,x1,x2)
-# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \
- png_sprintf(s1,fmt,x1,x2,x3,x4,x5,x6)
-#endif
/* End of 1.5.0beta36 move from pngconf.h */
/* CONSTANTS and UTILITY MACROS
@@ -261,12 +258,14 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
*/
/* Various modes of operation. Note that after an init, mode is set to
- * zero automatically when the structure is created.
+ * zero automatically when the structure is created. Three of these
+ * are defined in png.h because they need to be visible to applications
+ * that call png_set_unknown_chunk().
*/
-#define PNG_HAVE_IHDR 0x01
-#define PNG_HAVE_PLTE 0x02
+/* #define PNG_HAVE_IHDR 0x01 (defined in png.h) */
+/* #define PNG_HAVE_PLTE 0x02 (defined in png.h) */
#define PNG_HAVE_IDAT 0x04
-#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */
+/* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */
#define PNG_HAVE_IEND 0x10
#define PNG_HAVE_gAMA 0x20
#define PNG_HAVE_cHRM 0x40
@@ -286,10 +285,10 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
#define PNG_SWAP_BYTES 0x0010
#define PNG_INVERT_MONO 0x0020
#define PNG_QUANTIZE 0x0040
-#define PNG_BACKGROUND 0x0080
+#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */
#define PNG_BACKGROUND_EXPAND 0x0100
#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */
-#define PNG_16_TO_8 0x0400
+#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */
#define PNG_RGBA 0x0800
#define PNG_EXPAND 0x1000
#define PNG_GAMMA 0x2000
@@ -303,10 +302,10 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
#define PNG_RGB_TO_GRAY_ERR 0x200000L
#define PNG_RGB_TO_GRAY_WARN 0x400000L
#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */
- /* 0x800000L Unused */
+#define PNG_ENCODE_ALPHA 0x800000L /* Added to libpng-1.5.4 */
#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */
#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */
- /* 0x4000000L unused */
+#define PNG_SCALE_16_TO_8 0x4000000L /* Added to libpng-1.5.4 */
/* 0x8000000L unused */
/* 0x10000000L unused */
/* 0x20000000L unused */
@@ -333,9 +332,9 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200
#define PNG_FLAG_CRC_CRITICAL_USE 0x0400
#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800
- /* 0x1000 unused */
- /* 0x2000 unused */
- /* 0x4000 unused */
+#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */
+#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */
+#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */
#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L
#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L
#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L
@@ -345,13 +344,13 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
/* 0x200000L unused */
/* 0x400000L unused */
#define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000L /* Added to libpng-1.4.0 */
- /* 0x1000000L unused */
- /* 0x2000000L unused */
- /* 0x4000000L unused */
- /* 0x8000000L unused */
- /* 0x10000000L unused */
- /* 0x20000000L unused */
- /* 0x40000000L unused */
+#define PNG_FLAG_ZTXT_CUSTOM_STRATEGY 0x1000000L /* 5 lines added */
+#define PNG_FLAG_ZTXT_CUSTOM_LEVEL 0x2000000L /* to libpng-1.5.4 */
+#define PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL 0x4000000L
+#define PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS 0x8000000L
+#define PNG_FLAG_ZTXT_CUSTOM_METHOD 0x10000000L
+ /* 0x20000000L unused */
+ /* 0x40000000L unused */
#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
PNG_FLAG_CRC_ANCILLARY_NOWARN)
@@ -460,6 +459,11 @@ PNG_EXTERN png_fixed_point png_fixed PNGARG((png_structp png_ptr, double fp,
#define PNG_tRNS PNG_CONST png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'}
#define PNG_zTXt PNG_CONST png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'}
+/* Gamma values (new at libpng-1.5.4): */
+#define PNG_GAMMA_MAC_OLD 151724 /* Assume '1.8' is really 2.2/1.45! */
+#define PNG_GAMMA_MAC_INVERSE 65909
+#define PNG_GAMMA_sRGB_INVERSE 45455
+
/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
#ifdef __cplusplus
@@ -472,6 +476,12 @@ extern "C" {
* be found in the files where the functions are located.
*/
+/* Check the user version string for compatibility, returns false if the version
+ * numbers aren't compatible.
+ */
+PNG_EXTERN int png_user_version_check(png_structp png_ptr,
+ png_const_charp user_png_ver);
+
/* Allocate memory for an internal libpng struct */
PNG_EXTERN PNG_FUNCTION(png_voidp,png_create_struct,PNGARG((int type)),
PNG_ALLOCATED);
@@ -540,8 +550,7 @@ PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf,
png_size_t length));
/* Decompress data in a chunk that uses compression */
-#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \
- defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED)
+#if defined(PNG_READ_COMPRESSED_TEXT_SUPPORTED)
PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr,
int comp_type, png_size_t chunklength, png_size_t prefix_length,
png_size_t *data_length));
@@ -644,6 +653,7 @@ PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr,
png_const_uint_16p hist, int num_hist));
#endif
+/* Chunks that have keywords */
#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr,
@@ -734,17 +744,17 @@ PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr,
PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr,
png_row_infop row_info));
-/* Write out the filtered row. */
-PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr,
- png_bytep filtered_row));
/* Finish a row while reading, dealing with interlacing passes, etc. */
PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr));
/* Initialize the row buffers, etc. */
PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr));
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
/* Optional call to update the users info structure */
PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr,
png_infop info_ptr));
+#endif
/* These are the functions that do the transformations */
#ifdef PNG_READ_FILLER_SUPPORTED
@@ -816,7 +826,12 @@ PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info,
png_bytep row));
#endif
-#ifdef PNG_READ_16_TO_8_SUPPORTED
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+PNG_EXTERN void png_do_scale_16_to_8 PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info,
png_bytep row));
#endif
@@ -847,26 +862,20 @@ PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info,
png_bytep row, png_const_color_8p bit_depth));
#endif
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
-# ifdef PNG_READ_GAMMA_SUPPORTED
-PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info,
- png_bytep row, png_const_color_16p trans_color,
- png_const_color_16p background, png_const_color_16p background_1,
- png_const_bytep gamma_table, png_const_bytep gamma_from_1,
- png_const_bytep gamma_to_1, png_const_uint_16pp gamma_16,
- png_const_uint_16pp gamma_16_from_1, png_const_uint_16pp gamma_16_to_1,
- int gamma_shift));
-# else
-PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info,
- png_bytep row, png_const_color_16p trans_color,
- png_const_color_16p background));
-# endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+ defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+PNG_EXTERN void png_do_compose PNGARG((png_row_infop row_info,
+ png_bytep row, png_structp png_ptr));
#endif
#ifdef PNG_READ_GAMMA_SUPPORTED
PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info,
- png_bytep row, png_const_bytep gamma_table,
- png_const_uint_16pp gamma_16_table, int gamma_shift));
+ png_bytep row, png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+PNG_EXTERN void png_do_encode_alpha PNGARG((png_row_infop row_info,
+ png_bytep row, png_structp png_ptr));
#endif
#ifdef PNG_READ_EXPAND_SUPPORTED
@@ -986,10 +995,16 @@ PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr,
png_const_bytep chunk_name));
/* Handle the transformations for reading and writing */
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr));
+#endif
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr));
+#endif
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr));
+#endif
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr,
@@ -1086,6 +1101,76 @@ PNG_EXTERN PNG_FUNCTION(void, png_fixed_error, (png_structp png_ptr,
png_const_charp name),PNG_NORETURN);
#endif
+/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite
+ * the end. Always leaves the buffer nul terminated. Never errors out (and
+ * there is no error code.)
+ */
+PNG_EXTERN size_t png_safecat(png_charp buffer, size_t bufsize, size_t pos,
+ png_const_charp string);
+
+/* Various internal functions to handle formatted warning messages, currently
+ * only implemented for warnings.
+ */
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Utility to dump an unsigned value into a buffer, given a start pointer and
+ * and end pointer (which should point just *beyond* the end of the buffer!)
+ * Returns the pointer to the start of the formatted string. This utility only
+ * does unsigned values.
+ */
+PNG_EXTERN png_charp png_format_number(png_const_charp start, png_charp end,
+ int format, png_alloc_size_t number);
+
+/* Convenience macro that takes an array: */
+#define PNG_FORMAT_NUMBER(buffer,format,number) \
+ png_format_number(buffer, buffer + (sizeof buffer), format, number)
+
+/* Suggested size for a number buffer (enough for 64 bits and a sign!) */
+#define PNG_NUMBER_BUFFER_SIZE 24
+
+/* These are the integer formats currently supported, the name is formed from
+ * the standard printf(3) format string.
+ */
+#define PNG_NUMBER_FORMAT_u 1 /* chose unsigned API! */
+#define PNG_NUMBER_FORMAT_02u 2
+#define PNG_NUMBER_FORMAT_d 1 /* chose signed API! */
+#define PNG_NUMBER_FORMAT_02d 2
+#define PNG_NUMBER_FORMAT_x 3
+#define PNG_NUMBER_FORMAT_02x 4
+#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */
+#endif
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* New defines and members adding in libpng-1.5.4 */
+# define PNG_WARNING_PARAMETER_SIZE 32
+# define PNG_WARNING_PARAMETER_COUNT 8
+
+/* An l-value of this type has to be passed to the APIs below to cache the
+ * values of the parameters to a formatted warning message.
+ */
+typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][
+ PNG_WARNING_PARAMETER_SIZE];
+
+PNG_EXTERN void png_warning_parameter(png_warning_parameters p, int number,
+ png_const_charp string);
+ /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,
+ * including the trailing '\0'.
+ */
+PNG_EXTERN void png_warning_parameter_unsigned(png_warning_parameters p,
+ int number, int format, png_alloc_size_t value);
+ /* Use png_alloc_size_t because it is an unsigned type as big as any we
+ * need to output. Use the following for a signed value.
+ */
+PNG_EXTERN void png_warning_parameter_signed(png_warning_parameters p,
+ int number, int format, png_int_32 value);
+
+PNG_EXTERN void png_formatted_warning(png_structp png_ptr,
+ png_warning_parameters p, png_const_charp message);
+ /* 'message' follows the X/Open approach of using @1, @2 to insert
+ * parameters previously supplied using the above functions. Errors in
+ * specifying the paramters will simple result in garbage substitutions.
+ */
+#endif
+
/* ASCII to FP interfaces, currently only implemented if sCAL
* support is required.
*/
@@ -1148,8 +1233,18 @@ PNG_EXTERN void png_ascii_from_fixed PNGARG((png_structp png_ptr,
#define PNG_FP_SAW_DOT 16 /* Saw a dot in current state */
#define PNG_FP_SAW_E 32 /* Saw an E (or e) in current state */
#define PNG_FP_SAW_ANY 60 /* Saw any of the above 4 */
+
+/* These three values don't affect the parser. They are set but not used.
+ */
#define PNG_FP_WAS_VALID 64 /* Preceding substring is a valid fp number */
-#define PNG_FP_INVALID 128 /* Available for callers as a distinct value */
+#define PNG_FP_NEGATIVE 128 /* A negative number, including "-0" */
+#define PNG_FP_NONZERO 256 /* A non-zero value */
+#define PNG_FP_STICKY 448 /* The above three flags */
+
+/* This is available for the caller to store in 'state' if required. Do not
+ * call the parser after setting it (the parser sometimes clears it.)
+ */
+#define PNG_FP_INVALID 512 /* Available for callers as a distinct value */
/* Result codes for the parser (boolean - true meants ok, false means
* not ok yet.)
@@ -1157,6 +1252,20 @@ PNG_EXTERN void png_ascii_from_fixed PNGARG((png_structp png_ptr,
#define PNG_FP_MAYBE 0 /* The number may be valid in the future */
#define PNG_FP_OK 1 /* The number is valid */
+/* Tests on the sticky non-zero and negative flags. To pass these checks
+ * the state must also indicate that the whole number is valid - this is
+ * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this
+ * is equivalent to PNG_FP_OK above.)
+ */
+#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO)
+ /* NZ_MASK: the string is valid and a non-zero negative value */
+#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO)
+ /* Z MASK: the string is valid and a non-zero value. */
+ /* PNG_FP_SAW_DIGIT: the string is valid. */
+#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT)
+#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK)
+#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK)
+
/* The actual parser. This can be called repeatedly, it updates
* the index into the string and the state variable (which must
* be initialzed to 0). It returns a result code, as above. There
@@ -1176,7 +1285,10 @@ PNG_EXTERN int png_check_fp_number PNGARG((png_const_charp string,
png_size_t size, int *statep, png_size_tp whereami));
/* This is the same but it checks a complete string and returns true
- * only if it just contains a floating point number.
+ * only if it just contains a floating point number. As of 1.5.4 this
+ * function also returns the state at the end of parsing the number if
+ * it was valid (otherwise it returns 0.) This can be used for testing
+ * for negative or zero values using the sticky flag.
*/
PNG_EXTERN int png_check_fp_string PNGARG((png_const_charp string,
png_size_t size));
diff --git a/pngread.c b/pngread.c
index b12f7b84f..be3df4769 100644
--- a/pngread.c
+++ b/pngread.c
@@ -1,7 +1,7 @@
/* pngread.c - read a PNG file
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -47,12 +47,10 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
#ifdef PNG_SETJMP_SUPPORTED
#ifdef USE_FAR_KEYWORD
- jmp_buf png_jmpbuf;
+ jmp_buf tmp_jmpbuf;
#endif
#endif
- int i;
-
png_debug(1, "in png_create_read_struct");
#ifdef PNG_USER_MEM_SUPPORTED
@@ -85,13 +83,13 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
encounter a png_error() will longjmp here. Since the jmpbuf is
then meaningless we abort instead of returning. */
#ifdef USE_FAR_KEYWORD
- if (setjmp(png_jmpbuf))
+ if (setjmp(tmp_jmpbuf))
#else
if (setjmp(png_jmpbuf(png_ptr))) /* Sets longjmp to match setjmp */
#endif
PNG_ABORT();
#ifdef USE_FAR_KEYWORD
- png_memcpy(png_jmpbuf(png_ptr), png_jmpbuf, png_sizeof(jmp_buf));
+ png_memcpy(png_jmpbuf(png_ptr), tmp_jmpbuf, png_sizeof(jmp_buf));
#endif
#endif /* PNG_SETJMP_SUPPORTED */
@@ -101,54 +99,9 @@ png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
- if (user_png_ver)
- {
- i = 0;
-
- do
- {
- if (user_png_ver[i] != png_libpng_ver[i])
- png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
- } while (png_libpng_ver[i++]);
- }
-
- else
- png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
-
-
- if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
- {
- /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
- * we must recompile any applications that use any older library version.
- * For versions after libpng 1.0, we will be compatible, so we need
- * only check the first digit.
- */
- if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
- (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
- (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
- {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- char msg[80];
- if (user_png_ver)
- {
- png_snprintf2(msg, 80,
- "Application built with libpng-%.20s"
- " but running with %.20s",
- user_png_ver,
- png_libpng_ver);
- png_warning(png_ptr, msg);
- }
-#else
- png_warning(png_ptr,
- "Incompatible libpng version in application and library");
-#endif
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- png_ptr->flags = 0;
-#endif
-
- png_cleanup_needed = 1;
- }
- }
+ /* Call the general version checker (shared with read and write code): */
+ if (!png_user_version_check(png_ptr, user_png_ver))
+ png_cleanup_needed = 1;
if (!png_cleanup_needed)
{
@@ -457,7 +410,11 @@ png_read_update_info(png_structp png_ptr, png_infop info_ptr)
"Ignoring extra png_read_update_info() call;"
" row buffer not reallocated");
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
png_read_transform_info(png_ptr, info_ptr);
+#else
+ PNG_UNUSED(info_ptr)
+#endif
}
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
@@ -704,8 +661,10 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row)
#endif
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
if (png_ptr->transformations)
png_do_read_transformations(png_ptr);
+#endif
#ifdef PNG_READ_INTERLACING_SUPPORTED
/* Blow up interlaced rows to full size */
@@ -1163,7 +1122,9 @@ png_read_destroy(png_structp png_ptr, png_infop info_ptr,
jmp_buf tmp_jmp;
#endif
png_error_ptr error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
png_error_ptr warning_fn;
+#endif
png_voidp error_ptr;
#ifdef PNG_USER_MEM_SUPPORTED
png_free_ptr free_fn;
@@ -1249,10 +1210,6 @@ png_read_destroy(png_structp png_ptr, png_infop info_ptr,
#endif
#endif
-#ifdef PNG_TIME_RFC1123_SUPPORTED
- png_free(png_ptr, png_ptr->time_buffer);
-#endif
-
inflateEnd(&png_ptr->zstream);
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
@@ -1269,11 +1226,13 @@ png_read_destroy(png_structp png_ptr, png_infop info_ptr,
* being used again.
*/
#ifdef PNG_SETJMP_SUPPORTED
- png_memcpy(tmp_jmp, png_ptr->png_jmpbuf, png_sizeof(jmp_buf));
+ png_memcpy(tmp_jmp, png_ptr->longjmp_buffer, png_sizeof(jmp_buf));
#endif
error_fn = png_ptr->error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
warning_fn = png_ptr->warning_fn;
+#endif
error_ptr = png_ptr->error_ptr;
#ifdef PNG_USER_MEM_SUPPORTED
free_fn = png_ptr->free_fn;
@@ -1282,14 +1241,16 @@ png_read_destroy(png_structp png_ptr, png_infop info_ptr,
png_memset(png_ptr, 0, png_sizeof(png_struct));
png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
png_ptr->warning_fn = warning_fn;
+#endif
png_ptr->error_ptr = error_ptr;
#ifdef PNG_USER_MEM_SUPPORTED
png_ptr->free_fn = free_fn;
#endif
#ifdef PNG_SETJMP_SUPPORTED
- png_memcpy(png_ptr->png_jmpbuf, tmp_jmp, png_sizeof(jmp_buf));
+ png_memcpy(png_ptr->longjmp_buffer, tmp_jmp, png_sizeof(jmp_buf));
#endif
}
@@ -1325,8 +1286,22 @@ png_read_png(png_structp png_ptr, png_infop info_ptr,
/* -------------- image transformations start here ------------------- */
-#ifdef PNG_READ_16_TO_8_SUPPORTED
- /* Tell libpng to strip 16 bit/color files down to 8 bits per color.
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ /* Tell libpng to strip 16-bit/color files down to 8 bits per color.
+ */
+ if (transforms & PNG_TRANSFORM_SCALE_16)
+ {
+ /* Added at libpng-1.5.4. "strip_16" produces the same result that it
+ * did in earlier versions, while "scale_16" is now more accurate.
+ */
+ png_set_scale_16(png_ptr);
+ }
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+ /* If both SCALE and STRIP are required pngrtran will effectively cancel the
+ * latter by doing SCALE first. This is ok and allows apps not to check for
+ * which is supported to get the right answer.
*/
if (transforms & PNG_TRANSFORM_STRIP_16)
png_set_strip_16(png_ptr);
@@ -1407,7 +1382,7 @@ png_read_png(png_structp png_ptr, png_infop info_ptr,
#endif
#ifdef PNG_READ_SWAP_SUPPORTED
- /* Swap bytes of 16 bit files to least significant byte first */
+ /* Swap bytes of 16-bit files to least significant byte first */
if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
png_set_swap(png_ptr);
#endif
@@ -1426,6 +1401,12 @@ png_read_png(png_structp png_ptr, png_infop info_ptr,
png_set_gray_to_rgb(png_ptr);
#endif
+/* Added at libpng-1.5.4 */
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+ if (transforms & PNG_TRANSFORM_EXPAND_16)
+ png_set_expand_16(png_ptr);
+#endif
+
/* We don't handle adding filler bytes */
/* We use png_read_image and rely on that for interlace handling, but we also
diff --git a/pngrtran.c b/pngrtran.c
index 23220a730..4e0401aa9 100644
--- a/pngrtran.c
+++ b/pngrtran.c
@@ -1,7 +1,7 @@
/* pngrtran.c - transforms the data in a row for PNG readers
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -106,12 +106,18 @@ png_set_background_fixed(png_structp png_ptr,
return;
}
- png_ptr->transformations |= PNG_BACKGROUND;
+ png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA;
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+
png_memcpy(&(png_ptr->background), background_color,
png_sizeof(png_color_16));
png_ptr->background_gamma = background_gamma;
png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
- png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0);
+ if (need_expand)
+ png_ptr->transformations |= PNG_BACKGROUND_EXPAND;
+ else
+ png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
}
# ifdef PNG_FLOATING_POINT_SUPPORTED
@@ -126,8 +132,25 @@ png_set_background(png_structp png_ptr,
# endif /* FLOATING_POINT */
#endif /* READ_BACKGROUND */
-#ifdef PNG_READ_16_TO_8_SUPPORTED
-/* Strip 16 bit depth files to 8 bit depth */
+/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the
+ * one that pngrtran does first (scale) happens. This is necessary to allow the
+ * TRANSFORM and API behavior to be somewhat consistent, and it's simpler.
+ */
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+void PNGAPI
+png_set_scale_16(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_scale_16");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_SCALE_16_TO_8;
+}
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+/* Chop 16-bit depth files to 8-bit depth */
void PNGAPI
png_set_strip_16(png_structp png_ptr)
{
@@ -137,7 +160,6 @@ png_set_strip_16(png_structp png_ptr)
return;
png_ptr->transformations |= PNG_16_TO_8;
- png_ptr->transformations &= ~PNG_EXPAND_16;
}
#endif
@@ -154,8 +176,190 @@ png_set_strip_alpha(png_structp png_ptr)
}
#endif
+#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED)
+static png_fixed_point
+translate_gamma_flags(png_structp png_ptr, png_fixed_point output_gamma,
+ int is_screen)
+{
+ /* Check for flag values. The main reason for having the old Mac value as a
+ * flag is that it is pretty near impossible to work out what the correct
+ * value is from Apple documentation - a working Mac system is needed to
+ * discover the value!
+ */
+ if (output_gamma == PNG_DEFAULT_sRGB ||
+ output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB)
+ {
+ /* If there is no sRGB support this just sets the gamma to the standard
+ * sRGB value. (This is a side effect of using this function!)
+ */
+# ifdef PNG_READ_sRGB_SUPPORTED
+ png_ptr->flags |= PNG_FLAG_ASSUME_sRGB;
+# endif
+ if (is_screen)
+ output_gamma = PNG_GAMMA_sRGB;
+ else
+ output_gamma = PNG_GAMMA_sRGB_INVERSE;
+ }
+
+ else if (output_gamma == PNG_GAMMA_MAC_18 ||
+ output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18)
+ {
+ if (is_screen)
+ output_gamma = PNG_GAMMA_MAC_OLD;
+ else
+ output_gamma = PNG_GAMMA_MAC_INVERSE;
+ }
+
+ return output_gamma;
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+static png_fixed_point
+convert_gamma_value(png_structp png_ptr, double output_gamma)
+{
+ /* The following silently ignores cases where fixed point (times 100,000)
+ * gamma values are passed to the floating point API. This is safe and it
+ * means the fixed point constants work just fine with the floating point
+ * API. The alternative would just lead to undetected errors and spurious
+ * bug reports. Negative values fail inside the _fixed API unless they
+ * correspond to the flag values.
+ */
+ if (output_gamma > 0 && output_gamma < 128)
+ output_gamma *= PNG_FP_1;
+
+ /* This preserves -1 and -2 exactly: */
+ output_gamma = floor(output_gamma + .5);
+
+ if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN)
+ png_fixed_error(png_ptr, "gamma value");
+
+ return (png_fixed_point)output_gamma;
+}
+# endif
+#endif /* READ_ALPHA_MODE || READ_GAMMA */
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+void PNGFAPI
+png_set_alpha_mode_fixed(png_structp png_ptr, int mode,
+ png_fixed_point output_gamma)
+{
+ int compose = 0;
+ png_fixed_point file_gamma;
+
+ png_debug(1, "in png_set_alpha_mode");
+
+ if (png_ptr == NULL)
+ return;
+
+ output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
+
+ /* Validate the value to ensure it is in a reasonable range. The value
+ * is expected to be 1 or greater, but this range test allows for some
+ * viewing correction values. The intent is to weed out users of this API
+ * who use the inverse of the gamma value accidentally! Since some of these
+ * values are reasonable this may have to be changed.
+ */
+ if (output_gamma < 70000 || output_gamma > 300000)
+ png_error(png_ptr, "output gamma out of expected range");
+
+ /* The default file gamma is the inverse of the output gamma; the output
+ * gamma may be changed below so get the file value first:
+ */
+ file_gamma = png_reciprocal(output_gamma);
+
+ /* There are really 8 possibilities here, composed of any combination
+ * of:
+ *
+ * premultiply the color channels
+ * do not encode non-opaque pixels
+ * encode the alpha as well as the color channels
+ *
+ * The differences disappear if the input/output ('screen') gamma is 1.0,
+ * because then the encoding is a no-op and there is only the choice of
+ * premultiplying the color channels or not.
+ *
+ * png_set_alpha_mode and png_set_background interact because both use
+ * png_compose to do the work. Calling both is only useful when
+ * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along
+ * with a default gamma value. Otherwise PNG_COMPOSE must not be set.
+ */
+ switch (mode)
+ {
+ case PNG_ALPHA_PNG: /* default: png standard */
+ /* No compose, but it may be set by png_set_background! */
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+ break;
+
+ case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */
+ compose = 1;
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+ /* The output is linear: */
+ output_gamma = PNG_FP_1;
+ break;
+
+ case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */
+ compose = 1;
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA;
+ /* output_gamma records the encoding of opaque pixels! */
+ break;
+
+ case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */
+ compose = 1;
+ png_ptr->transformations |= PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+ break;
+
+ default:
+ png_error(png_ptr, "invalid alpha mode");
+ }
+
+ /* Only set the default gamma if the file gamma has not been set (this has
+ * the side effect that the gamma in a second call to png_set_alpha_mode will
+ * be ignored.)
+ */
+ if (png_ptr->gamma == 0)
+ png_ptr->gamma = file_gamma;
+
+ /* But always set the output gamma: */
+ png_ptr->screen_gamma = output_gamma;
+
+ /* Finally, if pre-multiplying, set the background fields to achieve the
+ * desired result.
+ */
+ if (compose)
+ {
+ /* And obtain alpha pre-multiplication by composing on black: */
+ png_memset(&png_ptr->background, 0, sizeof png_ptr->background);
+ png_ptr->background_gamma = png_ptr->gamma; /* just in case */
+ png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE;
+ png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND;
+
+ if (png_ptr->transformations & PNG_COMPOSE)
+ png_error(png_ptr,
+ "conflicting calls to set alpha mode and background");
+
+ png_ptr->transformations |= PNG_COMPOSE;
+ }
+
+ /* New API, make sure apps call the correct initializers: */
+ png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_alpha_mode(png_structp png_ptr, int mode, double output_gamma)
+{
+ png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr,
+ output_gamma));
+}
+# endif
+#endif
+
#ifdef PNG_READ_QUANTIZE_SUPPORTED
-/* Dither file to 8 bit. Supply a palette, the current number
+/* Dither file to 8-bit. Supply a palette, the current number
* of elements in the palette, the maximum number of elements
* allowed, and a histogram if possible. If the current number
* of colors is greater then the maximum number, the palette will be
@@ -561,30 +765,6 @@ png_set_quantize(png_structp png_ptr, png_colorp palette,
#endif /* PNG_READ_QUANTIZE_SUPPORTED */
#ifdef PNG_READ_GAMMA_SUPPORTED
-/* Transform the image from the file_gamma to the screen_gamma. We
- * only do transformations on images where the file_gamma and screen_gamma
- * are not close reciprocals, otherwise it slows things down slightly, and
- * also needlessly introduces small errors.
- *
- * We will turn off gamma transformation later if no semitransparent entries
- * are present in the tRNS array for palette images. We can't do it here
- * because we don't necessarily have the tRNS chunk yet.
- */
-static int /* PRIVATE */
-png_gamma_threshold(png_fixed_point scrn_gamma, png_fixed_point file_gamma)
-{
- /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
- * correction as a difference of the overall transform from 1.0
- *
- * We want to compare the threshold with s*f - 1, if we get
- * overflow here it is because of wacky gamma values so we
- * turn on processing anyway.
- */
- png_fixed_point gtest;
- return !png_muldiv(&gtest, scrn_gamma, file_gamma, PNG_FP_1) ||
- png_gamma_significant(gtest);
-}
-
void PNGFAPI
png_set_gamma_fixed(png_structp png_ptr, png_fixed_point scrn_gamma,
png_fixed_point file_gamma)
@@ -594,10 +774,32 @@ png_set_gamma_fixed(png_structp png_ptr, png_fixed_point scrn_gamma,
if (png_ptr == NULL)
return;
- if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) ||
- (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||
- png_gamma_threshold(scrn_gamma, file_gamma))
- png_ptr->transformations |= PNG_GAMMA;
+ /* New in libpng-1.5.4 - reserve particular negative values as flags. */
+ scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/);
+ file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/);
+
+#if PNG_LIBPNG_VER >= 10600
+ /* Checking the gamma values for being >0 was added in 1.5.4 along with the
+ * premultiplied alpha support; this actually hides an undocumented feature
+ * of the previous implementation which allowed gamma processing to be
+ * disabled in background handling. There is no evidence (so far) that this
+ * was being used; however, png_set_background itself accepted and must still
+ * accept '0' for the gamma value it takes, because it isn't always used.
+ *
+ * Since this is an API change (albeit a very minor one that removes an
+ * undocumented API feature) it will not be made until libpng-1.6.0.
+ */
+ if (file_gamma <= 0)
+ png_error(png_ptr, "invalid file gamma in png_set_gamma");
+
+ if (scrn_gamma <= 0)
+ png_error(png_ptr, "invalid screen gamma in png_set_gamma");
+#endif
+
+ /* Set the gamma values unconditionally - this overrides the value in the PNG
+ * file if a gAMA chunk was present. png_set_alpha_mode provides a
+ * different, easier, way to default the file gamma.
+ */
png_ptr->gamma = file_gamma;
png_ptr->screen_gamma = scrn_gamma;
}
@@ -606,9 +808,8 @@ png_set_gamma_fixed(png_structp png_ptr, png_fixed_point scrn_gamma,
void PNGAPI
png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma)
{
- png_set_gamma_fixed(png_ptr,
- png_fixed(png_ptr, scrn_gamma, "png_set_gamma screen gamma"),
- png_fixed(png_ptr, file_gamma, "png_set_gamma file gamma"));
+ png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma),
+ convert_gamma_value(png_ptr, file_gamma));
}
# endif /* FLOATING_POINT_SUPPORTED */
#endif /* READ_GAMMA */
@@ -688,7 +889,7 @@ png_set_tRNS_to_alpha(png_structp png_ptr)
#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */
#ifdef PNG_READ_EXPAND_16_SUPPORTED
-/* Expand to 16 bit channels, expand the tRNS chunk too (because otherwise
+/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise
* it may not work correctly.)
*/
void PNGAPI
@@ -700,9 +901,10 @@ png_set_expand_16(png_structp png_ptr)
return;
png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
- png_ptr->transformations &= ~PNG_16_TO_8;
-
png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+
+ /* New API, make sure apps call the correct initializers: */
+ png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED;
}
#endif
@@ -762,30 +964,38 @@ png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action,
}
#endif
{
- png_uint_16 red_int, green_int;
- if (red < 0 || green < 0)
+ if (red >= 0 && green >= 0 && red + green <= PNG_FP_1)
{
- red_int = 6968; /* .212671 * 32768 + .5 */
- green_int = 23434; /* .715160 * 32768 + .5 */
- }
+ png_uint_16 red_int, green_int;
- else if (red + green < 100000L)
- {
red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L);
green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L);
+
+ png_ptr->rgb_to_gray_red_coeff = red_int;
+ png_ptr->rgb_to_gray_green_coeff = green_int;
+ png_ptr->rgb_to_gray_blue_coeff =
+ (png_uint_16)(32768 - red_int - green_int);
}
else
{
- png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients");
- red_int = 6968;
- green_int = 23434;
- }
+ if (red >= 0 && green >= 0)
+ png_warning(png_ptr,
+ "ignoring out of range rgb_to_gray coefficients");
- png_ptr->rgb_to_gray_red_coeff = red_int;
- png_ptr->rgb_to_gray_green_coeff = green_int;
- png_ptr->rgb_to_gray_blue_coeff =
- (png_uint_16)(32768 - red_int - green_int);
+ /* Use the defaults, from the cHRM chunk if set, else the built in Rec
+ * 709 values (which correspond to sRGB, so we don't have to worry
+ * about the sRGB chunk!)
+ */
+ if (png_ptr->rgb_to_gray_red_coeff == 0 &&
+ png_ptr->rgb_to_gray_green_coeff == 0 &&
+ png_ptr->rgb_to_gray_blue_coeff == 0)
+ {
+ png_ptr->rgb_to_gray_red_coeff = 6968; /* .212671 * 32768 + .5 */
+ png_ptr->rgb_to_gray_green_coeff = 23434; /* .715160 * 32768 + .5 */
+ png_ptr->rgb_to_gray_blue_coeff = 2366;
+ }
+ }
}
}
@@ -827,55 +1037,160 @@ png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
}
#endif
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* In the case of gamma transformations only do transformations on images where
+ * the [file] gamma and screen_gamma are not close reciprocals, otherwise it
+ * slows things down slightly, and also needlessly introduces small errors.
+ */
+static int /* PRIVATE */
+png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma)
+{
+ /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
+ * correction as a difference of the overall transform from 1.0
+ *
+ * We want to compare the threshold with s*f - 1, if we get
+ * overflow here it is because of wacky gamma values so we
+ * turn on processing anyway.
+ */
+ png_fixed_point gtest;
+ return !png_muldiv(&gtest, screen_gamma, file_gamma, PNG_FP_1) ||
+ png_gamma_significant(gtest);
+}
+#endif
+
/* Initialize everything needed for the read. This includes modifying
* the palette.
*/
-void /* PRIVATE */
-png_init_read_transformations(png_structp png_ptr)
+
+/*For the moment 'png_init_palette_transformations' and
+ * 'png_init_rgb_transformations' only do some flag canceling optimizations.
+ * The intent is that these two routines should have palette or rgb operations
+ * extracted from 'png_init_read_transformations'.
+ */
+static void /* PRIVATE */
+png_init_palette_transformations(png_structp png_ptr)
{
- png_debug(1, "in png_init_read_transformations");
+ /* Called to handle the (input) palette case. In png_do_read_transformations
+ * the first step is to expand the palette if requested, so this code must
+ * take care to only make changes that are invariant with respect to the
+ * palette expansion, or only do them if there is no expansion.
+ *
+ * STRIP_ALPHA has already been handled in the caller (by setting num_trans
+ * to 0.)
+ */
+ int input_has_alpha = 0;
+ int input_has_transparency = 0;
- {
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
- defined(PNG_READ_SHIFT_SUPPORTED) || \
- defined(PNG_READ_GAMMA_SUPPORTED)
- int color_type = png_ptr->color_type;
-#endif
+ if (png_ptr->num_trans > 0)
+ {
+ int i;
+
+ /* Ignore if all the entries are opaque (unlikely!) */
+ for (i=0; i<png_ptr->num_trans; ++i)
+ if (png_ptr->trans_alpha[i] == 255)
+ continue;
+ else if (png_ptr->trans_alpha[i] == 0)
+ input_has_transparency = 1;
+ else
+ input_has_alpha = 1;
+ }
+
+ /* If no alpha we can optimize. */
+ if (!input_has_alpha)
+ {
+ /* Any alpha means background and associative alpha processing is
+ * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA
+ * and ENCODE_ALPHA are irrelevant.
+ */
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+
+ if (!input_has_transparency)
+ png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
+ }
#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+ /* png_set_background handling - deals with the complexity of whether the
+ * background color is in the file format or the screen format in the case
+ * where an 'expand' will happen.
+ */
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
- /* Detect gray background and attempt to enable optimization
- * for gray --> RGB case
- *
- * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or
- * RGB_ALPHA (in which case need_expand is superfluous anyway), the
- * background color might actually be gray yet not be flagged as such.
- * This is not a problem for the current code, which uses
- * PNG_BACKGROUND_IS_GRAY only to decide when to do the
- * png_do_gray_to_rgb() transformation.
+ /* The following code cannot be entered in the alpha pre-multiplication case
+ * because PNG_BACKGROUND_EXPAND is cancelled below.
*/
if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
- !(color_type & PNG_COLOR_MASK_COLOR))
+ (png_ptr->transformations & PNG_EXPAND))
{
- png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
- }
+ {
+ png_ptr->background.red =
+ png_ptr->palette[png_ptr->background.index].red;
+ png_ptr->background.green =
+ png_ptr->palette[png_ptr->background.index].green;
+ png_ptr->background.blue =
+ png_ptr->palette[png_ptr->background.index].blue;
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_INVERT_ALPHA)
+ {
+ if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+ {
+ /* Invert the alpha channel (in tRNS) unless the pixels are
+ * going to be expanded, in which case leave it for later
+ */
+ int i, istop = png_ptr->num_trans;
+
+ for (i=0; i<istop; i++)
+ png_ptr->trans_alpha[i] = (png_byte)(255 -
+ png_ptr->trans_alpha[i]);
+ }
+ }
+#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */
+ }
+ } /* background expand and (therefore) no alpha association. */
+#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */
+}
+
+static void /* PRIVATE */
+png_init_rgb_transformations(png_structp png_ptr)
+{
+ /* Added to libpng-1.5.4: check the color type to determine whether there
+ * is any alpha or transparency in the image and simply cancel the
+ * background and alpha mode stuff if there isn't.
+ */
+ int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0;
+ int input_has_transparency = png_ptr->num_trans > 0;
- else if ((png_ptr->transformations & PNG_BACKGROUND) &&
- !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
- (png_ptr->transformations & PNG_GRAY_TO_RGB) &&
- png_ptr->background.red == png_ptr->background.green &&
- png_ptr->background.red == png_ptr->background.blue)
+ /* If no alpha we can optimize. */
+ if (!input_has_alpha)
{
- png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
- png_ptr->background.gray = png_ptr->background.red;
+ /* Any alpha means background and associative alpha processing is
+ * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA
+ * and ENCODE_ALPHA are irrelevant.
+ */
+# ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+# endif
+
+ if (!input_has_transparency)
+ png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND);
}
-#endif
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+ /* png_set_background handling - deals with the complexity of whether the
+ * background color is in the file format or the screen format in the case
+ * where an 'expand' will happen.
+ */
+
+ /* The following code cannot be entered in the alpha pre-multiplication case
+ * because PNG_BACKGROUND_EXPAND is cancelled below.
+ */
if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
- (png_ptr->transformations & PNG_EXPAND))
+ (png_ptr->transformations & PNG_EXPAND) &&
+ !(png_ptr->color_type & PNG_COLOR_MASK_COLOR))
+ /* i.e., GRAY or GRAY_ALPHA */
{
- if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */
{
/* Expand background and tRNS chunks */
switch (png_ptr->bit_depth)
@@ -926,68 +1241,286 @@ png_init_read_transformations(png_structp png_ptr)
break;
}
}
- else if (color_type == PNG_COLOR_TYPE_PALETTE)
- {
- png_ptr->background.red =
- png_ptr->palette[png_ptr->background.index].red;
- png_ptr->background.green =
- png_ptr->palette[png_ptr->background.index].green;
- png_ptr->background.blue =
- png_ptr->palette[png_ptr->background.index].blue;
+ } /* background expand and (therefore) no alpha association. */
+#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */
+}
-#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
- if (png_ptr->transformations & PNG_INVERT_ALPHA)
- {
-#ifdef PNG_READ_EXPAND_SUPPORTED
- if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
-#endif
- {
- /* Invert the alpha channel (in tRNS) unless the pixels are
- * going to be expanded, in which case leave it for later
- */
- int i, istop;
- istop=(int)png_ptr->num_trans;
- for (i=0; i<istop; i++)
- png_ptr->trans_alpha[i] = (png_byte)(255 -
- png_ptr->trans_alpha[i]);
- }
- }
-#endif
+void /* PRIVATE */
+png_init_read_transformations(png_structp png_ptr)
+{
+ png_debug(1, "in png_init_read_transformations");
+
+ /* This internal function is called from png_read_start_row in pngrutil.c
+ * and it is called before the 'rowbytes' calculation is done, so the code
+ * in here can change or update the transformations flags.
+ *
+ * First do updates that do not depend on the details of the PNG image data
+ * being processed.
+ */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds
+ * png_set_alpha_mode and this is another source for a default file gamma so
+ * the test needs to be performed later - here. In addition prior to 1.5.4
+ * the tests were repeated for the PALETTE color type here - this is no
+ * longer necessary (and doesn't seem to have been necessary before.)
+ */
+ {
+ /* The following temporary indicates if overall gamma correction is
+ * required.
+ */
+ int gamma_correction = 0;
+ if (png_ptr->gamma != 0) /* has been set */
+ {
+ if (png_ptr->screen_gamma != 0) /* screen set too */
+ gamma_correction = png_gamma_threshold(png_ptr->gamma,
+ png_ptr->screen_gamma);
+
+ else
+ /* Assume the output matches the input; a long time default behavior
+ * of libpng, although the standard has nothing to say about this.
+ */
+ png_ptr->screen_gamma = png_reciprocal(png_ptr->gamma);
}
+
+ else if (png_ptr->screen_gamma != 0)
+ /* The converse - assume the file matches the screen, note that this
+ * perhaps undesireable default can (from 1.5.4) be changed by calling
+ * png_set_alpha_mode (even if the alpha handling mode isn't required
+ * or isn't changed from the default.)
+ */
+ png_ptr->gamma = png_reciprocal(png_ptr->screen_gamma);
+
+ else /* neither are set */
+ /* Just in case the following prevents any processing - file and screen
+ * are both assumed to be linear and there is no way to introduce a
+ * third gamma value other than png_set_background with 'UNIQUE', and,
+ * prior to 1.5.4
+ */
+ png_ptr->screen_gamma = png_ptr->gamma = PNG_FP_1;
+
+ /* Now turn the gamma transformation on or off as appropriate. Notice
+ * that PNG_GAMMA just refers to the file->screen correction. Alpha
+ * composition may independently cause gamma correction because it needs
+ * linear data (e.g. if the file has a gAMA chunk but the screen gamma
+ * hasn't been specified.) In any case this flag may get turned off in
+ * the code immediately below if the transform can be handled outside the
+ * row loop.
+ */
+ if (gamma_correction)
+ png_ptr->transformations |= PNG_GAMMA;
+
+ else
+ png_ptr->transformations &= ~PNG_GAMMA;
}
#endif
-#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
- png_ptr->background_1 = png_ptr->background;
+ /* Certain transformations have the effect of preventing other
+ * transformations that happen afterward in png_do_read_transformations,
+ * resolve the interdependencies here. From the code of
+ * png_do_read_transformations the order is:
+ *
+ * 1) PNG_EXPAND (including PNG_EXPAND_tRNS)
+ * 2) PNG_STRIP_ALPHA (if no compose)
+ * 3) PNG_RGB_TO_GRAY
+ * 4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY
+ * 5) PNG_COMPOSE
+ * 6) PNG_GAMMA
+ * 7) PNG_STRIP_ALPHA (if compose)
+ * 8) PNG_ENCODE_ALPHA
+ * 9) PNG_SCALE_16_TO_8
+ * 10) PNG_16_TO_8
+ * 11) PNG_QUANTIZE (converts to palette)
+ * 12) PNG_EXPAND_16
+ * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY
+ * 14) PNG_INVERT_MONO
+ * 15) PNG_SHIFT
+ * 16) PNG_PACK
+ * 17) PNG_BGR
+ * 18) PNG_PACKSWAP
+ * 19) PNG_FILLER (includes PNG_ADD_ALPHA)
+ * 20) PNG_INVERT_ALPHA
+ * 21) PNG_SWAP_ALPHA
+ * 22) PNG_SWAP_BYTES
+ * 23) PNG_USER_TRANSFORM [must be last]
+ */
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
+ !(png_ptr->transformations & PNG_COMPOSE))
+ {
+ /* Stripping the alpha channel happens immediately after the 'expand'
+ * transformations, before all other transformation, so it cancels out
+ * the alpha handling. It has the side effect negating the effect of
+ * PNG_EXPAND_tRNS too:
+ */
+ png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA |
+ PNG_EXPAND_tRNS);
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+
+ /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen
+ * so transparency information would remain just so long as it wasn't
+ * expanded. This produces unexpected API changes if the set of things
+ * that do PNG_EXPAND_tRNS changes (perfectly possible given the
+ * documentation - which says ask for what you want, accept what you
+ * get.) This makes the behavior consistent from 1.5.4:
+ */
+ png_ptr->num_trans = 0;
+ }
+#endif /* STRIP_ALPHA supported, no COMPOSE */
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA
+ * settings will have no effect.
+ */
+ if (!png_gamma_significant(png_ptr->screen_gamma))
+ {
+ png_ptr->transformations &= ~PNG_ENCODE_ALPHA;
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
+ }
#endif
-#ifdef PNG_READ_GAMMA_SUPPORTED
- if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0)
- && png_gamma_threshold(png_ptr->screen_gamma, png_ptr->gamma))
+#if defined(PNG_READ_EXPAND_SUPPORTED) && \
+ defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+ defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+ /* Detect gray background and attempt to enable optimization for
+ * gray --> RGB case.
+ *
+ * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or
+ * RGB_ALPHA (in which case need_expand is superfluous anyway), the
+ * background color might actually be gray yet not be flagged as such.
+ * This is not a problem for the current code, which uses
+ * PNG_BACKGROUND_IS_GRAY only to decide when to do the
+ * png_do_gray_to_rgb() transformation.
+ *
+ * TODO: this code needs to be revised to avoid the complexity and
+ * interdependencies. The color type of the background should be recorded in
+ * png_set_background, along with the bit depth, then the code has a record
+ * of exactly what color space the background is currently in.
+ */
+ if (png_ptr->transformations & PNG_BACKGROUND_EXPAND)
+ {
+ /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if
+ * the file was greyscale the background value is gray.
+ */
+ if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))
+ png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+ }
+
+ else if (png_ptr->transformations & PNG_COMPOSE)
{
- int i, k;
- k=0;
- for (i=0; i<png_ptr->num_trans; i++)
+ /* PNG_COMPOSE: png_set_background was called with need_expand false,
+ * so the color is in the color space of the output or png_set_alpha_mode
+ * was called and the color is black. Ignore RGB_TO_GRAY because that
+ * happens before GRAY_TO_RGB.
+ */
+ if (png_ptr->transformations & PNG_GRAY_TO_RGB)
{
- if (png_ptr->trans_alpha[i] != 0 && png_ptr->trans_alpha[i] != 0xff)
- k=1; /* Partial transparency is present */
+ if (png_ptr->background.red == png_ptr->background.green &&
+ png_ptr->background.red == png_ptr->background.blue)
+ {
+ png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+ png_ptr->background.gray = png_ptr->background.red;
+ }
}
- if (k == 0)
- png_ptr->transformations &= ~PNG_GAMMA;
}
+#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED (etc) */
+
+ /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations
+ * can be performed directly on the palette, and some (such as rgb to gray)
+ * can be optimized inside the palette. This is particularly true of the
+ * composite (background and alpha) stuff, which can be pretty much all done
+ * in the palette even if the result is expanded to RGB or gray afterward.
+ *
+ * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and
+ * earlier and the palette stuff is actually handled on the first row. This
+ * leads to the reported bug that the palette returned by png_get_PLTE is not
+ * updated.
+ */
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ png_init_palette_transformations(png_ptr);
+
+ else
+ png_init_rgb_transformations(png_ptr);
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+ defined(PNG_READ_EXPAND_16_SUPPORTED)
+ if ((png_ptr->transformations & PNG_EXPAND_16) &&
+ (png_ptr->transformations & PNG_COMPOSE) &&
+ !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+ png_ptr->bit_depth != 16)
+ {
+ /* TODO: fix this. Because the expand_16 operation is after the compose
+ * handling the background color must be 8, not 16, bits deep, but the
+ * application will supply a 16-bit value so reduce it here.
+ *
+ * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at
+ * present, so that case is ok (until do_expand_16 is moved.)
+ *
+ * NOTE: this discards the low 16 bits of the user supplied background
+ * color, but until expand_16 works properly there is no choice!
+ */
+# define CHOP(x) (x)=((png_uint_16)(((png_uint_32)(x)*255+32895) >> 16))
+ CHOP(png_ptr->background.red);
+ CHOP(png_ptr->background.green);
+ CHOP(png_ptr->background.blue);
+ CHOP(png_ptr->background.gray);
+# undef CHOP
+ }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED && PNG_READ_EXPAND_16_SUPPORTED */
+
+ /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the
+ * background support (see the comments in scripts/pnglibconf.dfa), this
+ * allows pre-multiplication of the alpha channel to be implemented as
+ * compositing on black. This is probably sub-optimal and has been done in
+ * 1.5.4 betas simply to enable external critique and testing (i.e. to
+ * implement the new API quickly, without lots of internal changes.)
+ */
- if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) &&
- png_ptr->gamma != 0)
+#ifdef PNG_READ_GAMMA_SUPPORTED
+# ifdef PNG_READ_BACKGROUND_SUPPORTED
+ /* Includes ALPHA_MODE */
+ png_ptr->background_1 = png_ptr->background;
+# endif
+
+ /* This needs to change - in the palette image case a whole set of tables are
+ * built when it would be quicker to just calculate the correct value for
+ * each palette entry directly. Also, the test is too tricky - why check
+ * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that
+ * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the
+ * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction
+ * the gamma tables will not be built even if composition is required on a
+ * gamma encoded value.
+ *
+ * In 1.5.4 this is addressed below by an additional check on the individual
+ * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the
+ * tables.
+ */
+ if ((png_ptr->transformations & PNG_GAMMA)
+ || ((png_ptr->transformations & PNG_RGB_TO_GRAY)
+ && (png_gamma_significant(png_ptr->gamma) ||
+ png_gamma_significant(png_ptr->screen_gamma)))
+ || ((png_ptr->transformations & PNG_COMPOSE)
+ && (png_gamma_significant(png_ptr->gamma)
+ || png_gamma_significant(png_ptr->screen_gamma)
+# ifdef PNG_READ_BACKGROUND_SUPPORTED
+ || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE
+ && png_gamma_significant(png_ptr->background_gamma))
+# endif
+ )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA)
+ && png_gamma_significant(png_ptr->screen_gamma))
+ )
{
png_build_gamma_table(png_ptr, png_ptr->bit_depth);
#ifdef PNG_READ_BACKGROUND_SUPPORTED
- if (png_ptr->transformations & PNG_BACKGROUND)
+ if (png_ptr->transformations & PNG_COMPOSE)
{
- if (color_type == PNG_COLOR_TYPE_PALETTE)
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
- /* Could skip if no transparency */
+ /* We don't get to here unless there is a tRNS chunk with non-opaque
+ * entries - see the checking code at the start of this function.
+ */
png_color back, back_1;
png_colorp palette = png_ptr->palette;
int num_palette = png_ptr->num_palette;
@@ -1033,26 +1566,39 @@ png_init_read_transformations(png_structp png_ptr)
if (png_gamma_significant(gs))
{
+ back.red = png_gamma_8bit_correct(png_ptr->background.red,
+ gs);
+ back.green = png_gamma_8bit_correct(png_ptr->background.green,
+ gs);
+ back.blue = png_gamma_8bit_correct(png_ptr->background.blue,
+ gs);
+ }
+
+ else
+ {
back.red = (png_byte)png_ptr->background.red;
back.green = (png_byte)png_ptr->background.green;
back.blue = (png_byte)png_ptr->background.blue;
}
+ if (png_gamma_significant(g))
+ {
+ back_1.red = png_gamma_8bit_correct(png_ptr->background.red,
+ g);
+ back_1.green = png_gamma_8bit_correct(
+ png_ptr->background.green, g);
+ back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,
+ g);
+ }
+
else
{
- back.red = png_gamma_8bit_correct(png_ptr->background.red,
- gs);
- back.green = png_gamma_8bit_correct(png_ptr->background.green,
- gs);
- back.blue = png_gamma_8bit_correct(png_ptr->background.blue,
- gs);
+ back_1.red = (png_byte)png_ptr->background.red;
+ back_1.green = (png_byte)png_ptr->background.green;
+ back_1.blue = (png_byte)png_ptr->background.blue;
}
- back_1.red = png_gamma_8bit_correct(png_ptr->background.red, g);
- back_1.green = png_gamma_8bit_correct(png_ptr->background.green,
- g);
- back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,
- g);
}
+
for (i = 0; i < num_palette; i++)
{
if (i < (int)png_ptr->num_trans &&
@@ -1086,19 +1632,18 @@ png_init_read_transformations(png_structp png_ptr)
palette[i].blue = png_ptr->gamma_table[palette[i].blue];
}
}
- /* Prevent the transformations being done again, and make sure
- * that the now spurious alpha channel is stripped - the code
- * has just reduced background composition and gamma correction
- * to a simple alpha channel strip.
+
+ /* Prevent the transformations being done again.
+ *
+ * NOTE: this is highly dubious; it zaps the transformations in
+ * place. This seems inconsistent with the general treatment of the
+ * transformations elsewhere.
*/
- png_ptr->transformations &= ~PNG_BACKGROUND;
- png_ptr->transformations &= ~PNG_GAMMA;
- png_ptr->transformations |= PNG_STRIP_ALPHA;
- }
+ png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
+ } /* color_type == PNG_COLOR_TYPE_PALETTE */
/* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
- else
- /* color_type != PNG_COLOR_TYPE_PALETTE */
+ else /* color_type != PNG_COLOR_TYPE_PALETTE */
{
png_fixed_point g = PNG_FP_1;
png_fixed_point gs = PNG_FP_1;
@@ -1164,17 +1709,21 @@ png_init_read_transformations(png_structp png_ptr)
png_ptr->background.red = png_ptr->background.green
= png_ptr->background.blue = png_ptr->background.gray;
}
- }
- }
+ } /* color_type != PNG_COLOR_TYPE_PALETTE */
+ }/* png_ptr->transformations & PNG_BACKGROUND */
+
else
/* Transformation does not include PNG_BACKGROUND */
#endif /* PNG_READ_BACKGROUND_SUPPORTED */
- if (color_type == PNG_COLOR_TYPE_PALETTE)
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
png_colorp palette = png_ptr->palette;
int num_palette = png_ptr->num_palette;
int i;
+ /*NOTE: there are other transformations that should probably be in here
+ * too.
+ */
for (i = 0; i < num_palette; i++)
{
palette[i].red = png_ptr->gamma_table[palette[i].red];
@@ -1184,16 +1733,17 @@ png_init_read_transformations(png_structp png_ptr)
/* Done the gamma correction. */
png_ptr->transformations &= ~PNG_GAMMA;
- }
+ } /* color_type == PALETTE && !PNG_BACKGROUND transformation */
}
#ifdef PNG_READ_BACKGROUND_SUPPORTED
else
#endif
#endif /* PNG_READ_GAMMA_SUPPORTED */
+
#ifdef PNG_READ_BACKGROUND_SUPPORTED
- /* No GAMMA transformation */
- if ((png_ptr->transformations & PNG_BACKGROUND) &&
- (color_type == PNG_COLOR_TYPE_PALETTE))
+ /* No GAMMA transformation (see the hanging else 4 lines above) */
+ if ((png_ptr->transformations & PNG_COMPOSE) &&
+ (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
{
int i;
int istop = (int)png_ptr->num_trans;
@@ -1225,15 +1775,13 @@ png_init_read_transformations(png_structp png_ptr)
}
}
- /* Handled alpha, still need to strip the channel. */
- png_ptr->transformations &= ~PNG_BACKGROUND;
- png_ptr->transformations |= PNG_STRIP_ALPHA;
+ png_ptr->transformations &= ~PNG_COMPOSE;
}
#endif /* PNG_READ_BACKGROUND_SUPPORTED */
#ifdef PNG_READ_SHIFT_SUPPORTED
if ((png_ptr->transformations & PNG_SHIFT) &&
- (color_type == PNG_COLOR_TYPE_PALETTE))
+ (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
{
png_uint_16 i;
png_uint_16 istop = png_ptr->num_palette;
@@ -1258,12 +1806,6 @@ png_init_read_transformations(png_structp png_ptr)
}
}
#endif /* PNG_READ_SHIFT_SUPPORTED */
- }
-#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \
- && !defined(PNG_READ_BACKGROUND_SUPPORTED)
- if (png_ptr)
- return;
-#endif
}
/* Modify the info structure to reflect the transformations. The
@@ -1280,8 +1822,11 @@ png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
{
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
- if (png_ptr->num_trans &&
- (png_ptr->transformations & PNG_EXPAND_tRNS))
+ /* This check must match what actually happens in
+ * png_do_expand_palette; if it ever checks the tRNS chunk to see if
+ * it is all opaque we must do the same (at present it does not.)
+ */
+ if (png_ptr->num_trans > 0)
info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
else
@@ -1305,44 +1850,61 @@ png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
}
#endif
-#ifdef PNG_READ_EXPAND_16_SUPPORTED
- if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 &&
- info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
- {
- info_ptr->bit_depth = 16;
- }
-#endif
-
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- if (png_ptr->transformations & PNG_BACKGROUND)
- {
- info_ptr->color_type = (png_byte)(info_ptr->color_type &
- ~PNG_COLOR_MASK_ALPHA);
- info_ptr->num_trans = 0;
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+ defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+ /* The following is almost certainly wrong unless the background value is in
+ * the screen space!
+ */
+ if (png_ptr->transformations & PNG_COMPOSE)
info_ptr->background = png_ptr->background;
- }
#endif
#ifdef PNG_READ_GAMMA_SUPPORTED
- if (png_ptr->transformations & PNG_GAMMA)
- {
- info_ptr->gamma = png_ptr->gamma;
- }
+ /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4),
+ * however it seems that the code in png_init_read_transformations, which has
+ * been called before this from png_read_update_info->png_read_start_row
+ * sometimes does the gamma transform and cancels the flag.
+ */
+ info_ptr->gamma = png_ptr->gamma;
#endif
-#ifdef PNG_READ_16_TO_8_SUPPORTED
-#ifdef PNG_READ_16BIT_SUPPORTED
- if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16))
- info_ptr->bit_depth = 8;
-#else
- /* Force chopping 16-bit input down to 8 */
if (info_ptr->bit_depth == 16)
{
- png_ptr->transformations |=PNG_16_TO_8;
- info_ptr->bit_depth = 8;
+# ifdef PNG_READ_16BIT_SUPPORTED
+# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ if (png_ptr->transformations & PNG_SCALE_16_TO_8)
+ info_ptr->bit_depth = 8;
+# endif
+
+# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+ if (png_ptr->transformations & PNG_16_TO_8)
+ info_ptr->bit_depth = 8;
+# endif
+
+# else
+ /* No 16 bit support: force chopping 16-bit input down to 8, in this case
+ * the app program can chose if both APIs are available by setting the
+ * correct scaling to use.
+ */
+# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+ /* For compatibility with previous versions use the strip method by
+ * default. This code works because if PNG_SCALE_16_TO_8 is already
+ * set the code below will do that in preference to the chop.
+ */
+ png_ptr->transformations |= PNG_16_TO_8;
+ info_ptr->bit_depth = 8;
+# else
+
+# if PNG_READ_SCALE_16_TO_8_SUPPORTED
+ png_ptr->transformations |= PNG_SCALE_16_TO_8;
+ info_ptr->bit_depth = 8;
+# else
+
+ CONFIGURATION ERROR: you must enable at least one 16 to 8 method
+# endif
+# endif
+#endif /* !READ_16BIT_SUPPORTED */
}
-#endif
-#endif
#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
if (png_ptr->transformations & PNG_GRAY_TO_RGB)
@@ -1366,6 +1928,14 @@ png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
}
#endif
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+ if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 &&
+ info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ info_ptr->bit_depth = 16;
+ }
+#endif
+
#ifdef PNG_READ_PACK_SUPPORTED
if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))
info_ptr->bit_depth = 8;
@@ -1382,7 +1952,10 @@ png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
if (png_ptr->transformations & PNG_STRIP_ALPHA)
+ {
info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+ info_ptr->num_trans = 0;
+ }
#endif
if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
@@ -1418,6 +1991,14 @@ defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width);
+ /* Adding in 1.5.4: cache the above value in png_struct so that we can later
+ * check in png_rowbytes that the user buffer won't get overwritten. Note
+ * that the field is not always set - if png_read_update_info isn't called
+ * the application has to either not do any transforms or get the calculation
+ * right itself.
+ */
+ png_ptr->info_rowbytes = info_ptr->rowbytes;
+
#ifndef PNG_READ_EXPAND_SUPPORTED
if (png_ptr)
return;
@@ -1435,29 +2016,27 @@ png_do_read_transformations(png_structp png_ptr)
if (png_ptr->row_buf == NULL)
{
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- char msg[50];
-
- png_snprintf2(msg, 50,
- "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number,
- png_ptr->pass);
- png_error(png_ptr, msg);
-#else
+ /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this
+ * error is incredibly rare and incredibly easy to debug without this
+ * information.
+ */
png_error(png_ptr, "NULL row buffer");
-#endif
}
-#ifdef PNG_WARN_UNINITIALIZED_ROW
- if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
- /* Application has failed to call either png_read_start_image()
- * or png_read_update_info() after setting transforms that expand
- * pixels. This check added to libpng-1.2.19
+
+ /* The following is debugging; prior to 1.5.4 the code was never compiled in;
+ * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro
+ * PNG_WARN_UNINITIALIZED_ROW removed. In 1.5 the new flag is set only for
+ * selected new APIs to ensure that there is no API change.
+ */
+ if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 &&
+ !(png_ptr->flags & PNG_FLAG_ROW_INIT))
+ {
+ /* Application has failed to call either png_read_start_image() or
+ * png_read_update_info() after setting transforms that expand pixels.
+ * This check added to libpng-1.2.19 (but not enabled until 1.5.4).
*/
-#if (PNG_WARN_UNINITIALIZED_ROW==1)
png_error(png_ptr, "Uninitialized row");
-#else
- png_warning(png_ptr, "Uninitialized row");
-#endif
-#endif
+ }
#ifdef PNG_READ_EXPAND_SUPPORTED
if (png_ptr->transformations & PNG_EXPAND)
@@ -1482,16 +2061,13 @@ png_do_read_transformations(png_structp png_ptr)
}
#endif
- /* Delay the 'expand 16' step until later for efficiency, so that the
- * intermediate steps work with 8 bit data.
- */
-
#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
+ !(png_ptr->transformations & PNG_COMPOSE) &&
(png_ptr->row_info.color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
png_ptr->row_info.color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
png_do_strip_channel(&(png_ptr->row_info), png_ptr->row_buf + 1,
- 0/*!at_start, because SWAP_ALPHA happens later*/);
+ 0 /* at_start == false, because SWAP_ALPHA happens later */);
#endif
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
@@ -1555,36 +2131,49 @@ png_do_read_transformations(png_structp png_ptr)
png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
#endif
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- if ((png_ptr->transformations & PNG_BACKGROUND) &&
- ((png_ptr->num_trans != 0) ||
- (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)))
- png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1,
- &(png_ptr->trans_color), &(png_ptr->background)
-#ifdef PNG_READ_GAMMA_SUPPORTED
- , &(png_ptr->background_1),
- png_ptr->gamma_table, png_ptr->gamma_from_1,
- png_ptr->gamma_to_1, png_ptr->gamma_16_table,
- png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1,
- png_ptr->gamma_shift
-#endif
- );
+#if (defined PNG_READ_BACKGROUND_SUPPORTED) ||\
+ (defined PNG_READ_ALPHA_MODE_SUPPORTED)
+ if (png_ptr->transformations & PNG_COMPOSE)
+ png_do_compose(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr);
#endif
#ifdef PNG_READ_GAMMA_SUPPORTED
if ((png_ptr->transformations & PNG_GAMMA) &&
-#ifdef PNG_READ_BACKGROUND_SUPPORTED
- !((png_ptr->transformations & PNG_BACKGROUND) &&
+#if (defined PNG_READ_BACKGROUND_SUPPORTED) ||\
+ (defined PNG_READ_ALPHA_MODE_SUPPORTED)
+ !((png_ptr->transformations & PNG_COMPOSE) &&
((png_ptr->num_trans != 0) ||
(png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&
#endif
(png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
- png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1,
- png_ptr->gamma_table, png_ptr->gamma_16_table,
- png_ptr->gamma_shift);
+ png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr);
#endif
-#ifdef PNG_READ_16_TO_8_SUPPORTED
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
+ (png_ptr->transformations & PNG_COMPOSE) &&
+ (png_ptr->row_info.color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+ png_ptr->row_info.color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
+ png_do_strip_channel(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ 0 /* at_start == false, because SWAP_ALPHA happens later */);
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+ if ((png_ptr->transformations & PNG_ENCODE_ALPHA) &&
+ (png_ptr->row_info.color_type & PNG_COLOR_MASK_ALPHA))
+ png_do_encode_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr);
+#endif
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+ if (png_ptr->transformations & PNG_SCALE_16_TO_8)
+ png_do_scale_16_to_8(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+ /* There is no harm in doing both of these because only one has any effect,
+ * by putting the 'scale' option first if the app asks for scale (either by
+ * calling the API or in a TRANSFORM flag) this is what happens.
+ */
if (png_ptr->transformations & PNG_16_TO_8)
png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1);
#endif
@@ -1610,6 +2199,13 @@ png_do_read_transformations(png_structp png_ptr)
png_do_expand_16(&png_ptr->row_info, png_ptr->row_buf + 1);
#endif
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ /*NOTE: moved here in 1.5.4 (from much later in this list.) */
+ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+ (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+ png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
#ifdef PNG_READ_INVERT_SUPPORTED
if (png_ptr->transformations & PNG_INVERT_MONO)
png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
@@ -1636,16 +2232,6 @@ png_do_read_transformations(png_structp png_ptr)
png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
#endif
-#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
- /*NOTE: this must be in the wrong place - what happens if BGR is set too?
- * Need pngvalid to test this combo.
- */
- /* If gray -> RGB, do so now only if we did not do so above */
- if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
- (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
- png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
-#endif
-
#ifdef PNG_READ_FILLER_SUPPORTED
if (png_ptr->transformations & PNG_FILLER)
png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
@@ -1697,7 +2283,6 @@ png_do_read_transformations(png_structp png_ptr)
png_ptr->row_info.width);
}
#endif
-
}
#ifdef PNG_READ_PACK_SUPPORTED
@@ -1919,54 +2504,86 @@ png_do_unshift(png_row_infop row_info, png_bytep row,
}
#endif
-#ifdef PNG_READ_16_TO_8_SUPPORTED
-/* Chop rows of bit depth 16 down to 8 */
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+/* Scale rows of bit depth 16 down to 8 accurately */
void /* PRIVATE */
-png_do_chop(png_row_infop row_info, png_bytep row)
+png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
{
- png_debug(1, "in png_do_chop");
+ png_debug(1, "in png_do_scale_16_to_8");
if (row_info->bit_depth == 16)
{
- png_bytep sp = row;
- png_bytep dp = row;
- png_uint_32 i;
- png_uint_32 istop = row_info->width * row_info->channels;
+ png_bytep sp = row; /* source */
+ png_bytep dp = row; /* destinaton */
+ png_bytep ep = sp + row_info->rowbytes; /* end+1 */
- for (i = 0; i<istop; i++, sp += 2, dp++)
+ while (sp < ep)
{
-#ifdef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
- /* This does a more accurate scaling of the 16-bit color
- * value, rather than a simple low-byte truncation.
- *
- * What the ideal calculation should be:
- * *dp = (((((png_uint_32)(*sp) << 8) |
- * (png_uint_32)(*(sp + 1))) * 255 + 127)
- * / (png_uint_32)65535L;
- *
- * GRR: no, I think this is what it really should be:
- * *dp = (((((png_uint_32)(*sp) << 8) |
- * (png_uint_32)(*(sp + 1))) + 128L)
- * / (png_uint_32)257L;
- *
- * GRR: here's the exact calculation with shifts:
- * temp = (((png_uint_32)(*sp) << 8) |
- * (png_uint_32)(*(sp + 1))) + 128L;
- * *dp = (temp - (temp >> 8)) >> 8;
- *
- * Approximate calculation with shift/add instead of multiply/divide:
- * *dp = ((((png_uint_32)(*sp) << 8) |
- * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8;
- *
- * What we actually do to avoid extra shifting and conversion:
- */
+ /* The input is an array of 16 bit components, these must be scaled to
+ * 8 bits each. For a 16 bit value V the required value (from the PNG
+ * specification) is:
+ *
+ * (V * 255) / 65535
+ *
+ * This reduces to round(V / 257), or floor((V + 128.5)/257)
+ *
+ * Represent V as the two byte value vhi.vlo. Make a guess that the
+ * result is the top byte of V, vhi, then the correction to this value
+ * is:
+ *
+ * error = floor(((V-vhi.vhi) + 128.5) / 257)
+ * = floor(((vlo-vhi) + 128.5) / 257)
+ *
+ * This can be approximated using integer arithmetic (and a signed
+ * shift):
+ *
+ * error = (vlo-vhi+128) >> 8;
+ *
+ * The approximate differs from the exact answer only when (vlo-vhi) is
+ * 128; it then gives a correction of +1 when the exact correction is
+ * 0. This gives 128 errors. The exact answer (correct for all 16 bit
+ * input values) is:
+ *
+ * error = (vlo-vhi+128)*65535 >> 24;
+ *
+ * An alternative arithmetic calculation which also gives no errors is:
+ *
+ * (V * 255 + 32895) >> 16
+ */
- *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0);
-#else
- /* Simply discard the low order byte */
- *dp = *sp;
+ png_int_32 tmp = *sp++; /* must be signed! */
+ tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24;
+ *dp++ = (png_byte)tmp;
+ }
+
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+ row_info->rowbytes = row_info->width * row_info->channels;
+ }
+}
#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+void /* PRIVATE */
+/* Simply discard the low byte. This was the default behavior prior
+ * to libpng-1.5.4.
+ */
+png_do_chop(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_chop");
+
+ if (row_info->bit_depth == 16)
+ {
+ png_bytep sp = row; /* source */
+ png_bytep dp = row; /* destinaton */
+ png_bytep ep = sp + row_info->rowbytes; /* end+1 */
+
+ while (sp < ep)
+ {
+ *dp++ = *sp;
+ sp += 2; /* skip low byte */
}
+
row_info->bit_depth = 8;
row_info->pixel_depth = (png_byte)(8 * row_info->channels);
row_info->rowbytes = row_info->width * row_info->channels;
@@ -2721,11 +3338,13 @@ png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
return rgb_error;
}
#endif
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth
* large of png_color. This lets grayscale images be treated as
* paletted. Most useful for gamma correction and simplification
- * of code.
+ * of code. This API is not used internally.
*/
void PNGAPI
png_build_grayscale_palette(int bit_depth, png_colorp palette)
@@ -2775,34 +3394,36 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette)
palette[i].blue = (png_byte)v;
}
}
+#endif
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
#ifdef PNG_READ_BACKGROUND_SUPPORTED
/* Replace any alpha or transparency with the supplied background color.
* "background" is already in the screen gamma, while "background_1" is
* at a gamma of 1.0. Paletted files have already been taken care of.
*/
void /* PRIVATE */
-png_do_background(png_row_infop row_info, png_bytep row,
- png_const_color_16p trans_color, png_const_color_16p background
+png_do_compose(png_row_infop row_info, png_bytep row, png_structp png_ptr)
+{
#ifdef PNG_READ_GAMMA_SUPPORTED
- , png_const_color_16p background_1, png_const_bytep gamma_table,
- png_const_bytep gamma_from_1, png_const_bytep gamma_to_1,
- png_const_uint_16pp gamma_16, png_const_uint_16pp gamma_16_from_1,
- png_const_uint_16pp gamma_16_to_1, int gamma_shift
+ png_const_bytep gamma_table = png_ptr->gamma_table;
+ png_const_bytep gamma_from_1 = png_ptr->gamma_from_1;
+ png_const_bytep gamma_to_1 = png_ptr->gamma_to_1;
+ png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table;
+ png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1;
+ png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1;
+ int gamma_shift = png_ptr->gamma_shift;
#endif
- )
-{
- png_bytep sp, dp;
+
+ png_bytep sp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
+ int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
int shift;
- png_debug(1, "in png_do_background");
+ png_debug(1, "in png_do_compose");
- if (background != NULL &&
- (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) ||
- (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_color)))
{
switch (row_info->color_type)
{
@@ -2817,10 +3438,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
for (i = 0; i < row_width; i++)
{
if ((png_uint_16)((*sp >> shift) & 0x01)
- == trans_color->gray)
+ == png_ptr->trans_color.gray)
{
*sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
- *sp |= (png_byte)(background->gray << shift);
+ *sp |= (png_byte)(png_ptr->background.gray << shift);
}
if (!shift)
@@ -2845,10 +3466,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
for (i = 0; i < row_width; i++)
{
if ((png_uint_16)((*sp >> shift) & 0x03)
- == trans_color->gray)
+ == png_ptr->trans_color.gray)
{
*sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
- *sp |= (png_byte)(background->gray << shift);
+ *sp |= (png_byte)(png_ptr->background.gray << shift);
}
else
@@ -2879,10 +3500,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
for (i = 0; i < row_width; i++)
{
if ((png_uint_16)((*sp >> shift) & 0x03)
- == trans_color->gray)
+ == png_ptr->trans_color.gray)
{
*sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
- *sp |= (png_byte)(background->gray << shift);
+ *sp |= (png_byte)(png_ptr->background.gray << shift);
}
if (!shift)
@@ -2908,10 +3529,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
for (i = 0; i < row_width; i++)
{
if ((png_uint_16)((*sp >> shift) & 0x0f)
- == trans_color->gray)
+ == png_ptr->trans_color.gray)
{
*sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
- *sp |= (png_byte)(background->gray << shift);
+ *sp |= (png_byte)(png_ptr->background.gray << shift);
}
else
@@ -2942,10 +3563,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
for (i = 0; i < row_width; i++)
{
if ((png_uint_16)((*sp >> shift) & 0x0f)
- == trans_color->gray)
+ == png_ptr->trans_color.gray)
{
*sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
- *sp |= (png_byte)(background->gray << shift);
+ *sp |= (png_byte)(png_ptr->background.gray << shift);
}
if (!shift)
@@ -2969,8 +3590,8 @@ png_do_background(png_row_infop row_info, png_bytep row,
sp = row;
for (i = 0; i < row_width; i++, sp++)
{
- if (*sp == trans_color->gray)
- *sp = (png_byte)background->gray;
+ if (*sp == png_ptr->trans_color.gray)
+ *sp = (png_byte)png_ptr->background.gray;
else
*sp = gamma_table[*sp];
@@ -2982,8 +3603,8 @@ png_do_background(png_row_infop row_info, png_bytep row,
sp = row;
for (i = 0; i < row_width; i++, sp++)
{
- if (*sp == trans_color->gray)
- *sp = (png_byte)background->gray;
+ if (*sp == png_ptr->trans_color.gray)
+ *sp = (png_byte)png_ptr->background.gray;
}
}
break;
@@ -3001,11 +3622,11 @@ png_do_background(png_row_infop row_info, png_bytep row,
v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
- if (v == trans_color->gray)
+ if (v == png_ptr->trans_color.gray)
{
/* Background is already in screen gamma */
- *sp = (png_byte)((background->gray >> 8) & 0xff);
- *(sp + 1) = (png_byte)(background->gray & 0xff);
+ *sp = (png_byte)((png_ptr->background.gray >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
}
else
@@ -3026,10 +3647,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
- if (v == trans_color->gray)
+ if (v == png_ptr->trans_color.gray)
{
- *sp = (png_byte)((background->gray >> 8) & 0xff);
- *(sp + 1) = (png_byte)(background->gray & 0xff);
+ *sp = (png_byte)((png_ptr->background.gray >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
}
}
}
@@ -3052,13 +3673,13 @@ png_do_background(png_row_infop row_info, png_bytep row,
sp = row;
for (i = 0; i < row_width; i++, sp += 3)
{
- if (*sp == trans_color->red &&
- *(sp + 1) == trans_color->green &&
- *(sp + 2) == trans_color->blue)
+ if (*sp == png_ptr->trans_color.red &&
+ *(sp + 1) == png_ptr->trans_color.green &&
+ *(sp + 2) == png_ptr->trans_color.blue)
{
- *sp = (png_byte)background->red;
- *(sp + 1) = (png_byte)background->green;
- *(sp + 2) = (png_byte)background->blue;
+ *sp = (png_byte)png_ptr->background.red;
+ *(sp + 1) = (png_byte)png_ptr->background.green;
+ *(sp + 2) = (png_byte)png_ptr->background.blue;
}
else
@@ -3075,13 +3696,13 @@ png_do_background(png_row_infop row_info, png_bytep row,
sp = row;
for (i = 0; i < row_width; i++, sp += 3)
{
- if (*sp == trans_color->red &&
- *(sp + 1) == trans_color->green &&
- *(sp + 2) == trans_color->blue)
+ if (*sp == png_ptr->trans_color.red &&
+ *(sp + 1) == png_ptr->trans_color.green &&
+ *(sp + 2) == png_ptr->trans_color.blue)
{
- *sp = (png_byte)background->red;
- *(sp + 1) = (png_byte)background->green;
- *(sp + 2) = (png_byte)background->blue;
+ *sp = (png_byte)png_ptr->background.red;
+ *(sp + 1) = (png_byte)png_ptr->background.green;
+ *(sp + 2) = (png_byte)png_ptr->background.blue;
}
}
}
@@ -3102,16 +3723,17 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+ *(sp + 5));
- if (r == trans_color->red && g == trans_color->green &&
- b == trans_color->blue)
+ if (r == png_ptr->trans_color.red &&
+ g == png_ptr->trans_color.green &&
+ b == png_ptr->trans_color.blue)
{
/* Background is already in screen gamma */
- *sp = (png_byte)((background->red >> 8) & 0xff);
- *(sp + 1) = (png_byte)(background->red & 0xff);
- *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
- *(sp + 3) = (png_byte)(background->green & 0xff);
- *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
- *(sp + 5) = (png_byte)(background->blue & 0xff);
+ *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+ *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(png_ptr->background.green & 0xff);
+ *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
}
else
@@ -3145,15 +3767,16 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+ *(sp + 5));
- if (r == trans_color->red && g == trans_color->green &&
- b == trans_color->blue)
+ if (r == png_ptr->trans_color.red &&
+ g == png_ptr->trans_color.green &&
+ b == png_ptr->trans_color.blue)
{
- *sp = (png_byte)((background->red >> 8) & 0xff);
- *(sp + 1) = (png_byte)(background->red & 0xff);
- *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
- *(sp + 3) = (png_byte)(background->green & 0xff);
- *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
- *(sp + 5) = (png_byte)(background->blue & 0xff);
+ *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+ *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(png_ptr->background.green & 0xff);
+ *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
}
}
}
@@ -3170,18 +3793,17 @@ png_do_background(png_row_infop row_info, png_bytep row,
gamma_table != NULL)
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 2, dp++)
+ for (i = 0; i < row_width; i++, sp += 2)
{
png_uint_16 a = *(sp + 1);
if (a == 0xff)
- *dp = gamma_table[*sp];
+ *sp = gamma_table[*sp];
else if (a == 0)
{
/* Background is already in screen gamma */
- *dp = (png_byte)background->gray;
+ *sp = (png_byte)png_ptr->background.gray;
}
else
@@ -3189,8 +3811,10 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_byte v, w;
v = gamma_to_1[*sp];
- png_composite(w, v, a, background_1->gray);
- *dp = gamma_from_1[w];
+ png_composite(w, v, a, png_ptr->background_1.gray);
+ if (!optimize)
+ w = gamma_from_1[w];
+ *sp = w;
}
}
}
@@ -3198,24 +3822,15 @@ png_do_background(png_row_infop row_info, png_bytep row,
#endif
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 2, dp++)
+ for (i = 0; i < row_width; i++, sp += 2)
{
png_byte a = *(sp + 1);
- if (a == 0xff)
- *dp = *sp;
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
- else if (a == 0)
- *dp = (png_byte)background->gray;
-
- else
- png_composite(*dp, *sp, a, background_1->gray);
+ if (a == 0)
+ *sp = (png_byte)png_ptr->background.gray;
-#else
- *dp = (png_byte)background->gray;
-#endif
+ else if (a < 0xff)
+ png_composite(*sp, *sp, a, png_ptr->background_1.gray);
}
}
}
@@ -3226,8 +3841,7 @@ png_do_background(png_row_infop row_info, png_bytep row,
gamma_16_to_1 != NULL)
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+ for (i = 0; i < row_width; i++, sp += 4)
{
png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+ *(sp + 3));
@@ -3237,69 +3851,56 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_uint_16 v;
v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
- *dp = (png_byte)((v >> 8) & 0xff);
- *(dp + 1) = (png_byte)(v & 0xff);
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
}
-#ifdef PNG_READ_GAMMA_SUPPORTED
else if (a == 0)
-#else
- else
-#endif
{
/* Background is already in screen gamma */
- *dp = (png_byte)((background->gray >> 8) & 0xff);
- *(dp + 1) = (png_byte)(background->gray & 0xff);
+ *sp = (png_byte)((png_ptr->background.gray >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
}
-#ifdef PNG_READ_GAMMA_SUPPORTED
else
{
png_uint_16 g, v, w;
g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
- png_composite_16(v, g, a, background_1->gray);
- w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];
- *dp = (png_byte)((w >> 8) & 0xff);
- *(dp + 1) = (png_byte)(w & 0xff);
+ png_composite_16(v, g, a, png_ptr->background_1.gray);
+ if (optimize)
+ w = v;
+ else
+ w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];
+ *sp = (png_byte)((w >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(w & 0xff);
}
-#endif
}
}
else
#endif
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+ for (i = 0; i < row_width; i++, sp += 4)
{
png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+ *(sp + 3));
- if (a == (png_uint_16)0xffff)
- png_memcpy(dp, sp, 2);
-
-#ifdef PNG_READ_GAMMA_SUPPORTED
- else if (a == 0)
-#else
- else
-#endif
+ if (a == 0)
{
- *dp = (png_byte)((background->gray >> 8) & 0xff);
- *(dp + 1) = (png_byte)(background->gray & 0xff);
+ *sp = (png_byte)((png_ptr->background.gray >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff);
}
-#ifdef PNG_READ_GAMMA_SUPPORTED
- else
+ else if (a < 0xffff)
{
png_uint_16 g, v;
g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
- png_composite_16(v, g, a, background_1->gray);
- *dp = (png_byte)((v >> 8) & 0xff);
- *(dp + 1) = (png_byte)(v & 0xff);
+ png_composite_16(v, g, a, png_ptr->background_1.gray);
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
}
-#endif
}
}
}
@@ -3315,24 +3916,23 @@ png_do_background(png_row_infop row_info, png_bytep row,
gamma_table != NULL)
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+ for (i = 0; i < row_width; i++, sp += 4)
{
png_byte a = *(sp + 3);
if (a == 0xff)
{
- *dp = gamma_table[*sp];
- *(dp + 1) = gamma_table[*(sp + 1)];
- *(dp + 2) = gamma_table[*(sp + 2)];
+ *sp = gamma_table[*sp];
+ *(sp + 1) = gamma_table[*(sp + 1)];
+ *(sp + 2) = gamma_table[*(sp + 2)];
}
else if (a == 0)
{
/* Background is already in screen gamma */
- *dp = (png_byte)background->red;
- *(dp + 1) = (png_byte)background->green;
- *(dp + 2) = (png_byte)background->blue;
+ *sp = (png_byte)png_ptr->background.red;
+ *(sp + 1) = (png_byte)png_ptr->background.green;
+ *(sp + 2) = (png_byte)png_ptr->background.blue;
}
else
@@ -3340,16 +3940,19 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_byte v, w;
v = gamma_to_1[*sp];
- png_composite(w, v, a, background_1->red);
- *dp = gamma_from_1[w];
+ png_composite(w, v, a, png_ptr->background_1.red);
+ if (!optimize) w = gamma_from_1[w];
+ *sp = w;
v = gamma_to_1[*(sp + 1)];
- png_composite(w, v, a, background_1->green);
- *(dp + 1) = gamma_from_1[w];
+ png_composite(w, v, a, png_ptr->background_1.green);
+ if (!optimize) w = gamma_from_1[w];
+ *(sp + 1) = w;
v = gamma_to_1[*(sp + 2)];
- png_composite(w, v, a, background_1->blue);
- *(dp + 2) = gamma_from_1[w];
+ png_composite(w, v, a, png_ptr->background_1.blue);
+ if (!optimize) w = gamma_from_1[w];
+ *(sp + 2) = w;
}
}
}
@@ -3357,34 +3960,26 @@ png_do_background(png_row_infop row_info, png_bytep row,
#endif
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+ for (i = 0; i < row_width; i++, sp += 4)
{
png_byte a = *(sp + 3);
- if (a == 0xff)
+ if (a == 0)
{
- *dp = *sp;
- *(dp + 1) = *(sp + 1);
- *(dp + 2) = *(sp + 2);
+ *sp = (png_byte)png_ptr->background.red;
+ *(sp + 1) = (png_byte)png_ptr->background.green;
+ *(sp + 2) = (png_byte)png_ptr->background.blue;
}
- else if (a == 0)
+ else if (a < 0xff)
{
- *dp = (png_byte)background->red;
- *(dp + 1) = (png_byte)background->green;
- *(dp + 2) = (png_byte)background->blue;
- }
+ png_composite(*sp, *sp, a, png_ptr->background.red);
- else
- {
- png_composite(*dp, *sp, a, background->red);
-
- png_composite(*(dp + 1), *(sp + 1), a,
- background->green);
+ png_composite(*(sp + 1), *(sp + 1), a,
+ png_ptr->background.green);
- png_composite(*(dp + 2), *(sp + 2), a,
- background->blue);
+ png_composite(*(sp + 2), *(sp + 2), a,
+ png_ptr->background.blue);
}
}
}
@@ -3396,8 +3991,7 @@ png_do_background(png_row_infop row_info, png_bytep row,
gamma_16_to_1 != NULL)
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+ for (i = 0; i < row_width; i++, sp += 8)
{
png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
<< 8) + (png_uint_16)(*(sp + 7)));
@@ -3407,53 +4001,55 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_uint_16 v;
v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
- *dp = (png_byte)((v >> 8) & 0xff);
- *(dp + 1) = (png_byte)(v & 0xff);
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
- *(dp + 2) = (png_byte)((v >> 8) & 0xff);
- *(dp + 3) = (png_byte)(v & 0xff);
+ *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(v & 0xff);
v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
- *(dp + 4) = (png_byte)((v >> 8) & 0xff);
- *(dp + 5) = (png_byte)(v & 0xff);
+ *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(v & 0xff);
}
else if (a == 0)
{
/* Background is already in screen gamma */
- *dp = (png_byte)((background->red >> 8) & 0xff);
- *(dp + 1) = (png_byte)(background->red & 0xff);
- *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
- *(dp + 3) = (png_byte)(background->green & 0xff);
- *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
- *(dp + 5) = (png_byte)(background->blue & 0xff);
+ *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+ *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(png_ptr->background.green & 0xff);
+ *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
}
else
{
- png_uint_16 v, w, x;
+ png_uint_16 v, w;
v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
- png_composite_16(w, v, a, background_1->red);
-
- x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
- *dp = (png_byte)((x >> 8) & 0xff);
- *(dp + 1) = (png_byte)(x & 0xff);
+ png_composite_16(w, v, a, png_ptr->background_1.red);
+ if (!optimize)
+ w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+ *sp = (png_byte)((w >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(w & 0xff);
v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
- png_composite_16(w, v, a, background_1->green);
+ png_composite_16(w, v, a, png_ptr->background_1.green);
+ if (!optimize)
+ w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
- x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
- *(dp + 2) = (png_byte)((x >> 8) & 0xff);
- *(dp + 3) = (png_byte)(x & 0xff);
+ *(sp + 2) = (png_byte)((w >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(w & 0xff);
v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
- png_composite_16(w, v, a, background_1->blue);
+ png_composite_16(w, v, a, png_ptr->background_1.blue);
+ if (!optimize)
+ w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
- x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8];
- *(dp + 4) = (png_byte)((x >> 8) & 0xff);
- *(dp + 5) = (png_byte)(x & 0xff);
+ *(sp + 4) = (png_byte)((w >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(w & 0xff);
}
}
}
@@ -3462,28 +4058,22 @@ png_do_background(png_row_infop row_info, png_bytep row,
#endif
{
sp = row;
- dp = row;
- for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+ for (i = 0; i < row_width; i++, sp += 8)
{
png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
<< 8) + (png_uint_16)(*(sp + 7)));
- if (a == (png_uint_16)0xffff)
+ if (a == 0)
{
- png_memcpy(dp, sp, 6);
+ *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff);
+ *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(png_ptr->background.green & 0xff);
+ *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff);
}
- else if (a == 0)
- {
- *dp = (png_byte)((background->red >> 8) & 0xff);
- *(dp + 1) = (png_byte)(background->red & 0xff);
- *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
- *(dp + 3) = (png_byte)(background->green & 0xff);
- *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
- *(dp + 5) = (png_byte)(background->blue & 0xff);
- }
-
- else
+ else if (a < 0xffff)
{
png_uint_16 v;
@@ -3493,17 +4083,17 @@ png_do_background(png_row_infop row_info, png_bytep row,
png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+ *(sp + 5));
- png_composite_16(v, r, a, background->red);
- *dp = (png_byte)((v >> 8) & 0xff);
- *(dp + 1) = (png_byte)(v & 0xff);
+ png_composite_16(v, r, a, png_ptr->background.red);
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
- png_composite_16(v, g, a, background->green);
- *(dp + 2) = (png_byte)((v >> 8) & 0xff);
- *(dp + 3) = (png_byte)(v & 0xff);
+ png_composite_16(v, g, a, png_ptr->background.green);
+ *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(v & 0xff);
- png_composite_16(v, b, a, background->blue);
- *(dp + 4) = (png_byte)((v >> 8) & 0xff);
- *(dp + 5) = (png_byte)(v & 0xff);
+ png_composite_16(v, b, a, png_ptr->background.blue);
+ *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(v & 0xff);
}
}
}
@@ -3514,16 +4104,6 @@ png_do_background(png_row_infop row_info, png_bytep row,
default:
break;
}
-
- if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
- {
- row_info->color_type = (png_byte)(row_info->color_type &
- ~PNG_COLOR_MASK_ALPHA);
- row_info->channels--;
- row_info->pixel_depth = (png_byte)(row_info->channels *
- row_info->bit_depth);
- row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
- }
}
}
#endif
@@ -3536,10 +4116,12 @@ png_do_background(png_row_infop row_info, png_bytep row,
* build_gamma_table().
*/
void /* PRIVATE */
-png_do_gamma(png_row_infop row_info, png_bytep row,
- png_const_bytep gamma_table, png_const_uint_16pp gamma_16_table,
- int gamma_shift)
+png_do_gamma(png_row_infop row_info, png_bytep row, png_structp png_ptr)
{
+ png_const_bytep gamma_table = png_ptr->gamma_table;
+ png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table;
+ int gamma_shift = png_ptr->gamma_shift;
+
png_bytep sp;
png_uint_32 i;
png_uint_32 row_width=row_info->width;
@@ -3729,6 +4311,73 @@ png_do_gamma(png_row_infop row_info, png_bytep row,
}
#endif
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+/* Encode the alpha channel to the output gamma (the input channel is always
+ * linear.) Called only with color types that have an alpha channel. Needs the
+ * from_1 tables.
+ */
+void /* PRIVATE */
+png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structp png_ptr)
+{
+ png_uint_32 row_width = row_info->width;
+
+ png_debug(1, "in png_do_encode_alpha");
+
+ if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ PNG_CONST png_bytep table = png_ptr->gamma_from_1;
+
+ if (table != NULL)
+ {
+ PNG_CONST int step =
+ (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2;
+
+ /* The alpha channel is the last component: */
+ row += step - 1;
+
+ for (; row_width > 0; --row_width, row += step)
+ *row = table[*row];
+
+ return;
+ }
+ }
+
+ else if (row_info->bit_depth == 16)
+ {
+ PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1;
+ PNG_CONST int gamma_shift = png_ptr->gamma_shift;
+
+ if (table != NULL)
+ {
+ PNG_CONST int step =
+ (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4;
+
+ /* The alpha channel is the last component: */
+ row += step - 2;
+
+ for (; row_width > 0; --row_width, row += step)
+ {
+ png_uint_16 v;
+
+ v = table[*(row + 1) >> gamma_shift][*row];
+ *row = (png_byte)((v >> 8) & 0xff);
+ *(row + 1) = (png_byte)(v & 0xff);
+ }
+
+ return;
+ }
+ }
+ }
+
+ /* Only get to here if called with a weird row_info; no harm has been done,
+ * so just issue a warning.
+ */
+ png_warning(png_ptr, "png_do_encode_alpha: unexpected call");
+}
+#endif
+
#ifdef PNG_READ_EXPAND_SUPPORTED
/* Expands a palette row to an RGB or RGBA row depending
* upon whether you supply trans and num_trans.
@@ -3834,7 +4483,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row,
if (row_info->bit_depth == 8)
{
{
- if (trans_alpha != NULL)
+ if (num_trans > 0)
{
sp = row + (png_size_t)row_width - 1;
dp = row + (png_size_t)(row_width << 2) - 1;
@@ -3888,7 +4537,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row,
*/
void /* PRIVATE */
png_do_expand(png_row_infop row_info, png_bytep row,
- png_const_color_16p trans_value)
+ png_const_color_16p trans_color)
{
int shift, value;
png_bytep sp, dp;
@@ -3900,7 +4549,7 @@ png_do_expand(png_row_infop row_info, png_bytep row,
{
if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
{
- png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0);
+ png_uint_16 gray = (png_uint_16)(trans_color ? trans_color->gray : 0);
if (row_info->bit_depth < 8)
{
@@ -3992,7 +4641,7 @@ png_do_expand(png_row_infop row_info, png_bytep row,
row_info->rowbytes = row_width;
}
- if (trans_value != NULL)
+ if (trans_color != NULL)
{
if (row_info->bit_depth == 8)
{
@@ -4044,13 +4693,13 @@ png_do_expand(png_row_infop row_info, png_bytep row,
row_width);
}
}
- else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value)
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_color)
{
if (row_info->bit_depth == 8)
{
- png_byte red = (png_byte)(trans_value->red & 0xff);
- png_byte green = (png_byte)(trans_value->green & 0xff);
- png_byte blue = (png_byte)(trans_value->blue & 0xff);
+ png_byte red = (png_byte)(trans_color->red & 0xff);
+ png_byte green = (png_byte)(trans_color->green & 0xff);
+ png_byte blue = (png_byte)(trans_color->blue & 0xff);
sp = row + (png_size_t)row_info->rowbytes - 1;
dp = row + (png_size_t)(row_width << 2) - 1;
for (i = 0; i < row_width; i++)
@@ -4068,12 +4717,12 @@ png_do_expand(png_row_infop row_info, png_bytep row,
}
else if (row_info->bit_depth == 16)
{
- png_byte red_high = (png_byte)((trans_value->red >> 8) & 0xff);
- png_byte green_high = (png_byte)((trans_value->green >> 8) & 0xff);
- png_byte blue_high = (png_byte)((trans_value->blue >> 8) & 0xff);
- png_byte red_low = (png_byte)(trans_value->red & 0xff);
- png_byte green_low = (png_byte)(trans_value->green & 0xff);
- png_byte blue_low = (png_byte)(trans_value->blue & 0xff);
+ png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff);
+ png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff);
+ png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff);
+ png_byte red_low = (png_byte)(trans_color->red & 0xff);
+ png_byte green_low = (png_byte)(trans_color->green & 0xff);
+ png_byte blue_low = (png_byte)(trans_color->blue & 0xff);
sp = row + row_info->rowbytes - 1;
dp = row + (png_size_t)(row_width << 3) - 1;
for (i = 0; i < row_width; i++)
@@ -4236,6 +4885,7 @@ png_do_quantize(png_row_infop row_info, png_bytep row,
}
}
#endif /* PNG_READ_QUANTIZE_SUPPORTED */
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
#ifdef PNG_MNG_FEATURES_SUPPORTED
/* Undoes intrapixel differencing */
diff --git a/pngrutil.c b/pngrutil.c
index 785524119..07e46e2fe 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -1,7 +1,7 @@
/* pngrutil.c - utilities to read a PNG file
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -278,8 +278,7 @@ png_crc_error(png_structp png_ptr)
return (0);
}
-#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \
- defined(PNG_READ_iCCP_SUPPORTED)
+#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
static png_size_t
png_inflate(png_structp png_ptr, png_bytep data, png_size_t size,
png_bytep output, png_size_t output_size)
@@ -370,41 +369,31 @@ png_inflate(png_structp png_ptr, png_bytep data, png_size_t size,
* and the error message is dumped into the uncompressed
* buffer if available.
*/
+# ifdef PNG_WARNINGS_SUPPORTED
{
- PNG_CONST char *msg;
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- char umsg[52];
-#endif
+ png_const_charp msg;
+
if (png_ptr->zstream.msg != 0)
msg = png_ptr->zstream.msg;
- else
+ else switch (ret)
{
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- switch (ret)
- {
- case Z_BUF_ERROR:
- msg = "Buffer error in compressed datastream in %s chunk";
- break;
+ case Z_BUF_ERROR:
+ msg = "Buffer error in compressed datastream";
+ break;
- case Z_DATA_ERROR:
- msg = "Data error in compressed datastream in %s chunk";
- break;
+ case Z_DATA_ERROR:
+ msg = "Data error in compressed datastream";
+ break;
- default:
- msg = "Incomplete compressed datastream in %s chunk";
- break;
- }
-
- png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name);
- msg = umsg;
-#else
- msg = "Damaged compressed datastream in chunk other than IDAT";
-#endif
+ default:
+ msg = "Incomplete compressed datastream";
+ break;
}
- png_warning(png_ptr, msg);
+ png_chunk_warning(png_ptr, msg);
}
+# endif
/* 0 means an error - notice that this code simply ignores
* zero length compressed chunks as a result.
@@ -438,8 +427,8 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
png_size_t expanded_size = png_inflate(png_ptr,
(png_bytep)(png_ptr->chunkdata + prefix_size),
chunklength - prefix_size,
- 0, /*output*/
- 0); /*output size*/
+ 0, /* output */
+ 0); /* output size */
/* Now check the limits on this chunk - if the limit fails the
* compressed data will be removed, the prefix will remain.
@@ -500,15 +489,9 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
{
-#ifdef PNG_STDIO_SUPPORTED
- char umsg[50];
-
- png_snprintf(umsg, sizeof umsg,
- "Unknown zTXt compression type %d", comp_type);
- png_warning(png_ptr, umsg);
-#else
- png_warning(png_ptr, "Unknown zTXt compression type");
-#endif
+ PNG_WARNING_PARAMETERS(p)
+ png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d, comp_type);
+ png_formatted_warning(png_ptr, p, "Unknown zTXt compression type @1");
/* The recovery is to simply drop the data. */
}
@@ -536,7 +519,7 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
*newlength = prefix_size;
}
-#endif
+#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */
/* Read and check the IDHR chunk */
void /* PRIVATE */
@@ -846,12 +829,10 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{
if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
{
- png_warning(png_ptr,
- "Ignoring incorrect gAMA value when sRGB is also present");
-
-# ifdef PNG_CONSOLE_IO_SUPPORTED
- fprintf(stderr, "gamma = (%d/100000)", (int)igamma);
-# endif
+ PNG_WARNING_PARAMETERS(p)
+ png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, igamma);
+ png_formatted_warning(png_ptr, p,
+ "Ignoring incorrect gAMA value @1 when sRGB is also present");
return;
}
}
@@ -1020,21 +1001,52 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
PNG_OUT_OF_RANGE(x_blue, 15000, 1000) ||
PNG_OUT_OF_RANGE(y_blue, 6000, 1000))
{
- png_warning(png_ptr,
- "Ignoring incorrect cHRM value when sRGB is also present");
-
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- fprintf(stderr, "wx=%d, wy=%d, rx=%d, ry=%d\n",
- x_white, y_white, x_red, y_red);
-
- fprintf(stderr, "gx=%d, gy=%d, bx=%d, by=%d\n",
- x_green, y_green, x_blue, y_blue);
-#endif /* PNG_CONSOLE_IO_SUPPORTED */
+ PNG_WARNING_PARAMETERS(p)
+
+ png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, x_white);
+ png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_fixed, y_white);
+ png_warning_parameter_signed(p, 3, PNG_NUMBER_FORMAT_fixed, x_red);
+ png_warning_parameter_signed(p, 4, PNG_NUMBER_FORMAT_fixed, y_red);
+ png_warning_parameter_signed(p, 5, PNG_NUMBER_FORMAT_fixed, x_green);
+ png_warning_parameter_signed(p, 6, PNG_NUMBER_FORMAT_fixed, y_green);
+ png_warning_parameter_signed(p, 7, PNG_NUMBER_FORMAT_fixed, x_blue);
+ png_warning_parameter_signed(p, 8, PNG_NUMBER_FORMAT_fixed, y_blue);
+
+ png_formatted_warning(png_ptr, p,
+ "Ignoring incorrect cHRM white(@1,@2) r(@3,@4)g(@5,@6)b(@7,@8) "
+ "when sRGB is also present");
}
return;
}
#endif /* PNG_READ_sRGB_SUPPORTED */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+ /* Store the _white values as default coefficients for the rgb to gray
+ * operation if it is supported.
+ */
+ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
+ {
+ /* png_set_background has not been called, the coefficients must be in
+ * range for the following to work without overflow.
+ */
+ if (y_red <= (1<<17) && y_green <= (1<<17) && y_blue <= (1<<17))
+ {
+ /* The y values are chromaticities: Y/X+Y+Z, the weights for the gray
+ * transformation are simply the normalized Y values for red, green and
+ * blue scaled by 32768.
+ */
+ png_uint_32 w = y_red + y_green + y_blue;
+
+ png_ptr->rgb_to_gray_red_coeff = (png_uint_16)(((png_uint_32)y_red *
+ 32768)/w);
+ png_ptr->rgb_to_gray_green_coeff = (png_uint_16)(((png_uint_32)y_green
+ * 32768)/w);
+ png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(((png_uint_32)y_blue *
+ 32768)/w);
+ }
+ }
+#endif
+
png_set_cHRM_fixed(png_ptr, info_ptr, x_white, y_white, x_red, y_red,
x_green, y_green, x_blue, y_blue);
}
@@ -1096,11 +1108,13 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{
if (PNG_OUT_OF_RANGE(info_ptr->gamma, 45500L, 500))
{
- png_warning(png_ptr,
- "Ignoring incorrect gAMA value when sRGB is also present");
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- fprintf(stderr, "incorrect gamma=(%d/100000)\n", info_ptr->gamma);
-#endif
+ PNG_WARNING_PARAMETERS(p)
+
+ png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed,
+ info_ptr->gamma);
+
+ png_formatted_warning(png_ptr, p,
+ "Ignoring incorrect gAMA value @1 when sRGB is also present");
}
}
#endif /* PNG_READ_gAMA_SUPPORTED */
@@ -1240,23 +1254,15 @@ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
/* And the following guarantees that profile_size == profile_length. */
if (profile_size > profile_length)
{
+ PNG_WARNING_PARAMETERS(p)
+
png_free(png_ptr, png_ptr->chunkdata);
png_ptr->chunkdata = NULL;
-#ifdef PNG_STDIO_SUPPORTED
- {
- char umsg[80];
-
- png_snprintf2(umsg, 80,
- "Ignoring iCCP chunk with declared size = %u "
- "and actual length = %u",
- (unsigned int) profile_size,
- (unsigned int) profile_length);
- png_warning(png_ptr, umsg);
- }
-#else
- png_warning(png_ptr,
- "Ignoring iCCP chunk with uncompressed size mismatch");
-#endif
+
+ png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_u, profile_size);
+ png_warning_parameter_unsigned(p, 2, PNG_NUMBER_FORMAT_u, profile_length);
+ png_formatted_warning(png_ptr, p,
+ "Ignoring iCCP chunk with declared size = @1 and actual length = @2");
return;
}
@@ -1565,6 +1571,7 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
{
png_size_t truelen;
png_byte buf[6];
+ png_color_16 background;
png_debug(1, "in png_handle_bKGD");
@@ -1621,7 +1628,7 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
*/
if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
{
- png_ptr->background.index = buf[0];
+ background.index = buf[0];
if (info_ptr && info_ptr->num_palette)
{
@@ -1631,33 +1638,36 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
return;
}
- png_ptr->background.red =
- (png_uint_16)png_ptr->palette[buf[0]].red;
+ background.red = (png_uint_16)png_ptr->palette[buf[0]].red;
+ background.green = (png_uint_16)png_ptr->palette[buf[0]].green;
+ background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue;
+ }
- png_ptr->background.green =
- (png_uint_16)png_ptr->palette[buf[0]].green;
+ else
+ background.red = background.green = background.blue = 0;
- png_ptr->background.blue =
- (png_uint_16)png_ptr->palette[buf[0]].blue;
- }
+ background.gray = 0;
}
else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */
{
- png_ptr->background.red =
- png_ptr->background.green =
- png_ptr->background.blue =
- png_ptr->background.gray = png_get_uint_16(buf);
+ background.index = 0;
+ background.red =
+ background.green =
+ background.blue =
+ background.gray = png_get_uint_16(buf);
}
else
{
- png_ptr->background.red = png_get_uint_16(buf);
- png_ptr->background.green = png_get_uint_16(buf + 2);
- png_ptr->background.blue = png_get_uint_16(buf + 4);
+ background.index = 0;
+ background.red = png_get_uint_16(buf);
+ background.green = png_get_uint_16(buf + 2);
+ background.blue = png_get_uint_16(buf + 4);
+ background.gray = 0;
}
- png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background));
+ png_set_bKGD(png_ptr, info_ptr, &background);
}
#endif
@@ -1982,6 +1992,14 @@ png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
return;
}
+ /* Need unit type, width, \0, height: minimum 4 bytes */
+ else if (length < 4)
+ {
+ png_warning(png_ptr, "sCAL chunk too short");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
length + 1);
@@ -2017,23 +2035,29 @@ png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
/* Validate the ASCII numbers, need two ASCII numbers separated by
* a '\0' and they need to fit exactly in the chunk data.
*/
- i = 0;
+ i = 1;
state = 0;
- if (png_ptr->chunkdata[1] == 45 /* negative width */ ||
- !png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
+ if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
i >= slength || png_ptr->chunkdata[i++] != 0)
png_warning(png_ptr, "Invalid sCAL chunk ignored: bad width format");
+ else if (!PNG_FP_IS_POSITIVE(state))
+ png_warning(png_ptr, "Invalid sCAL chunk ignored: non-positive width");
+
else
{
png_size_t heighti = i;
- if (png_ptr->chunkdata[i] == 45 /* negative height */ ||
- !png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
+ state = 0;
+ if (!png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
i != slength)
png_warning(png_ptr, "Invalid sCAL chunk ignored: bad height format");
+ else if (!PNG_FP_IS_POSITIVE(state))
+ png_warning(png_ptr,
+ "Invalid sCAL chunk ignored: non-positive height");
+
else
/* This is the (only) success case. */
png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0],
@@ -2660,6 +2684,14 @@ png_combine_row(png_structp png_ptr, png_bytep row, int mask)
{
png_debug(1, "in png_combine_row");
+ /* Added in 1.5.4: the row_info should match the information returned by any
+ * call to png_read_update_info at this point. Do not continue if we got
+ * this wrong.
+ */
+ if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes !=
+ PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width))
+ png_error(png_ptr, "internal row size calculation error");
+
if (mask == 0xff)
{
png_memcpy(row, png_ptr->row_buf + 1,
@@ -3398,7 +3430,9 @@ png_read_start_row(png_structp png_ptr)
png_debug(1, "in png_read_start_row");
png_ptr->zstream.avail_in = 0;
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
png_init_read_transformations(png_ptr);
+#endif
#ifdef PNG_READ_INTERLACING_SUPPORTED
if (png_ptr->interlaced)
{
diff --git a/pngset.c b/pngset.c
index 2cfcf33bd..7eaad7dd4 100644
--- a/pngset.c
+++ b/pngset.c
@@ -1,7 +1,7 @@
/* pngset.c - storage of image information into info struct
*
- * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -94,15 +94,16 @@ png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point
if (png_ptr == NULL || info_ptr == NULL)
return;
- /* Previously these values were limited, however they must be
- * wrong, therefore storing them (and setting PNG_INFO_gAMA)
- * must be wrong too.
+ /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't
+ * occur. Since the fixed point representation is assymetrical it is
+ * possible for 1/gamma to overflow the limit of 21474 and this means the
+ * gamma value must be at least 5/100000 and hence at most 20000.0. For
+ * safety the limits here are a little narrower. The values are 0.00016 to
+ * 6250.0, which are truely ridiculous gammma values (and will produce
+ * displays that are all black or all white.)
*/
- if (file_gamma > (png_fixed_point)PNG_UINT_31_MAX)
- png_warning(png_ptr, "Gamma too large, ignored");
-
- else if (file_gamma <= 0)
- png_warning(png_ptr, "Negative or zero gamma ignored");
+ if (file_gamma < 16 || file_gamma > 625000000)
+ png_warning(png_ptr, "Out of range gamma value ignored");
else
{
@@ -340,12 +341,12 @@ png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr,
if (unit != 1 && unit != 2)
png_error(png_ptr, "Invalid sCAL unit");
- if (swidth == NULL || (lengthw = png_strlen(swidth)) <= 0 ||
- swidth[0] == 45 /*'-'*/ || !png_check_fp_string(swidth, lengthw))
+ if (swidth == NULL || (lengthw = png_strlen(swidth)) == 0 ||
+ swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw))
png_error(png_ptr, "Invalid sCAL width");
- if (sheight == NULL || (lengthh = png_strlen(sheight)) <= 0 ||
- sheight[0] == 45 /*'-'*/ || !png_check_fp_string(sheight, lengthh))
+ if (sheight == NULL || (lengthh = png_strlen(sheight)) == 0 ||
+ sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh))
png_error(png_ptr, "Invalid sCAL height");
info_ptr->scal_unit = (png_byte)unit;
@@ -545,7 +546,7 @@ png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr,
png_set_sRGB(png_ptr, info_ptr, srgb_intent);
# ifdef PNG_gAMA_SUPPORTED
- png_set_gAMA_fixed(png_ptr, info_ptr, 45455L);
+ png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);
# endif
# ifdef PNG_cHRM_SUPPORTED
diff --git a/pngstruct.h b/pngstruct.h
new file mode 100644
index 000000000..93b2b3eb0
--- /dev/null
+++ b/pngstruct.h
@@ -0,0 +1,337 @@
+
+/* pngstruct.h - header file for PNG reference library
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.4 [July 7, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* The structure that holds the information to read and write PNG files.
+ * The only people who need to care about what is inside of this are the
+ * people who will be modifying the library for their own special needs.
+ * It should NOT be accessed directly by an application.
+ */
+
+#ifndef PNGSTRUCT_H
+#define PNGSTRUCT_H
+/* 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.
+ */
+#include "zlib.h"
+
+struct png_struct_def
+{
+#ifdef PNG_SETJMP_SUPPORTED
+ jmp_buf longjmp_buffer; /* used in png_error */
+ png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
+#endif
+ png_error_ptr error_fn; /* function for printing errors and aborting */
+#ifdef PNG_WARNINGS_SUPPORTED
+ png_error_ptr warning_fn; /* function for printing warnings */
+#endif
+ png_voidp error_ptr; /* user supplied struct for error functions */
+ png_rw_ptr write_data_fn; /* function for writing output data */
+ png_rw_ptr read_data_fn; /* function for reading input data */
+ png_voidp io_ptr; /* ptr to application struct for I/O functions */
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+ png_user_transform_ptr read_user_transform_fn; /* user read transform */
+#endif
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+ png_user_transform_ptr write_user_transform_fn; /* user write transform */
+#endif
+
+/* These were added in libpng-1.0.2 */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+ png_voidp user_transform_ptr; /* user supplied struct for user transform */
+ png_byte user_transform_depth; /* bit depth of user transformed pixels */
+ png_byte user_transform_channels; /* channels in user transformed pixels */
+#endif
+#endif
+
+ png_uint_32 mode; /* tells us where we are in the PNG file */
+ png_uint_32 flags; /* flags indicating various things to libpng */
+ png_uint_32 transformations; /* which transformations to perform */
+
+ z_stream zstream; /* pointer to decompression structure (below) */
+ png_bytep zbuf; /* buffer for zlib */
+ uInt zbuf_size; /* size of zbuf (typically 65536) */
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Added in 1.5.4: state to keep track of whether the zstream has been
+ * initialized and if so whether it is for IDAT or some other chunk.
+ */
+#define PNG_ZLIB_UNINITIALIZED 0
+#define PNG_ZLIB_FOR_IDAT 1
+#define PNG_ZLIB_FOR_TEXT 2 /* anything other than IDAT */
+#define PNG_ZLIB_USE_MASK 3 /* bottom two bits */
+#define PNG_ZLIB_IN_USE 4 /* a flag value */
+
+ png_uint_32 zlib_state; /* State of zlib initialization */
+/* End of material added at libpng 1.5.4 */
+
+ int zlib_level; /* holds zlib compression level */
+ int zlib_method; /* holds zlib compression method */
+ int zlib_window_bits; /* holds zlib compression window bits */
+ int zlib_mem_level; /* holds zlib compression memory level */
+ int zlib_strategy; /* holds zlib compression strategy */
+#endif
+/* Added at libpng 1.5.4 */
+#if defined(PNG_WRITE_COMPRESSED_TEXT_SUPPORTED) || \
+ defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED)
+ int zlib_text_level; /* holds zlib compression level */
+ int zlib_text_method; /* holds zlib compression method */
+ int zlib_text_window_bits; /* holds zlib compression window bits */
+ int zlib_text_mem_level; /* holds zlib compression memory level */
+ int zlib_text_strategy; /* holds zlib compression strategy */
+#endif
+/* End of material added at libpng 1.5.4 */
+
+ png_uint_32 width; /* width of image in pixels */
+ png_uint_32 height; /* height of image in pixels */
+ png_uint_32 num_rows; /* number of rows in current pass */
+ png_uint_32 usr_width; /* width of row at start of write */
+ png_size_t rowbytes; /* size of row in bytes */
+ png_uint_32 iwidth; /* width of current interlaced row in pixels */
+ png_uint_32 row_number; /* current row in interlace pass */
+ png_bytep prev_row; /* buffer to save previous (unfiltered) row */
+ png_bytep row_buf; /* buffer to save current (unfiltered) row */
+ png_bytep sub_row; /* buffer to save "sub" row when filtering */
+ png_bytep up_row; /* buffer to save "up" row when filtering */
+ png_bytep avg_row; /* buffer to save "avg" row when filtering */
+ png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */
+ png_row_info row_info; /* used for transformation routines */
+ png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */
+
+ png_uint_32 idat_size; /* current IDAT size for read */
+ png_uint_32 crc; /* current chunk CRC value */
+ png_colorp palette; /* palette from the input file */
+ png_uint_16 num_palette; /* number of color entries in palette */
+ png_uint_16 num_trans; /* number of transparency values */
+ png_byte chunk_name[5]; /* null-terminated name of current chunk */
+ png_byte compression; /* file compression type (always 0) */
+ png_byte filter; /* file filter type (always 0) */
+ png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+ png_byte pass; /* current interlace pass (0 - 6) */
+ png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */
+ png_byte color_type; /* color type of file */
+ png_byte bit_depth; /* bit depth of file */
+ png_byte usr_bit_depth; /* bit depth of users row */
+ png_byte pixel_depth; /* number of bits per pixel */
+ png_byte channels; /* number of channels in file */
+ png_byte usr_channels; /* channels at start of write */
+ png_byte sig_bytes; /* magic bytes read/written from start of file */
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+ png_uint_16 filler; /* filler bytes for pixel expansion */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
+ defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+ png_byte background_gamma_type;
+ png_fixed_point background_gamma;
+ png_color_16 background; /* background color in screen gamma space */
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ png_color_16 background_1; /* background normalized to gamma 1.0 */
+#endif
+#endif /* PNG_bKGD_SUPPORTED */
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_flush_ptr output_flush_fn; /* Function for flushing output */
+ png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */
+ png_uint_32 flush_rows; /* number of rows written since last flush */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */
+ png_fixed_point gamma; /* file gamma value */
+ png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ png_bytep gamma_table; /* gamma table for 8-bit depth files */
+ png_bytep gamma_from_1; /* converts from 1.0 to screen */
+ png_bytep gamma_to_1; /* converts from file to 1.0 */
+ png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
+ png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
+ png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
+ png_color_8 sig_bit; /* significant bits in each available channel */
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+ png_color_8 shift; /* shift for significant bit tranformation */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
+ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ png_bytep trans_alpha; /* alpha values for paletted files */
+ png_color_16 trans_color; /* transparent color for non-paletted files */
+#endif
+
+ png_read_status_ptr read_row_fn; /* called after each row is decoded */
+ png_write_status_ptr write_row_fn; /* called after each row is encoded */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+ png_progressive_info_ptr info_fn; /* called after header data fully read */
+ png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */
+ png_progressive_end_ptr end_fn; /* called after image is complete */
+ png_bytep save_buffer_ptr; /* current location in save_buffer */
+ png_bytep save_buffer; /* buffer for previously read data */
+ png_bytep current_buffer_ptr; /* current location in current_buffer */
+ png_bytep current_buffer; /* buffer for recently used data */
+ png_uint_32 push_length; /* size of current input chunk */
+ png_uint_32 skip_length; /* bytes to skip in input data */
+ png_size_t save_buffer_size; /* amount of data now in save_buffer */
+ png_size_t save_buffer_max; /* total size of save_buffer */
+ png_size_t buffer_size; /* total amount of available input data */
+ png_size_t current_buffer_size; /* amount of data now in current_buffer */
+ int process_mode; /* what push library is currently doing */
+ int cur_palette; /* current push library palette index */
+
+# ifdef PNG_TEXT_SUPPORTED
+ png_size_t current_text_size; /* current size of text input data */
+ png_size_t current_text_left; /* how much text left to read in input */
+ png_charp current_text; /* current text chunk buffer */
+ png_charp current_text_ptr; /* current location in current_text */
+# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* For the Borland special 64K segment handler */
+ png_bytepp offset_table_ptr;
+ png_bytep offset_table;
+ png_uint_16 offset_table_number;
+ png_uint_16 offset_table_count;
+ png_uint_16 offset_table_count_free;
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+ png_bytep palette_lookup; /* lookup table for quantizing */
+ png_bytep quantize_index; /* index translation for palette files */
+#endif
+
+#if defined(PNG_READ_QUANTIZE_SUPPORTED) || defined(PNG_hIST_SUPPORTED)
+ png_uint_16p hist; /* histogram */
+#endif
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ png_byte heuristic_method; /* heuristic for row filter selection */
+ png_byte num_prev_filters; /* number of weights for previous rows */
+ png_bytep prev_filters; /* filter type(s) of previous row(s) */
+ png_uint_16p filter_weights; /* weight(s) for previous line(s) */
+ png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */
+ png_uint_16p filter_costs; /* relative filter calculation cost */
+ png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */
+#endif
+
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+ char time_buffer[29]; /* String to hold RFC 1123 time text */
+#endif
+
+/* New members added in libpng-1.0.6 */
+
+ png_uint_32 free_me; /* flags items libpng is responsible for freeing */
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+ png_voidp user_chunk_ptr;
+ png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
+#endif
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ int num_chunk_list;
+ png_bytep chunk_list;
+#endif
+
+/* New members added in libpng-1.0.3 */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+ png_byte rgb_to_gray_status;
+ /* These were changed from png_byte in libpng-1.0.6 */
+ png_uint_16 rgb_to_gray_red_coeff;
+ png_uint_16 rgb_to_gray_green_coeff;
+ png_uint_16 rgb_to_gray_blue_coeff;
+#endif
+
+/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
+#if defined(PNG_MNG_FEATURES_SUPPORTED) || \
+ defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+ defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+/* Changed from png_byte to png_uint_32 at version 1.2.0 */
+ png_uint_32 mng_features_permitted;
+#endif
+
+/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ png_byte filter_type;
+#endif
+
+/* New members added in libpng-1.2.0 */
+
+/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_voidp mem_ptr; /* user supplied struct for mem functions */
+ png_malloc_ptr malloc_fn; /* function for allocating memory */
+ png_free_ptr free_fn; /* function for freeing memory */
+#endif
+
+/* New member added in libpng-1.0.13 and 1.2.0 */
+ png_bytep big_row_buf; /* buffer to save current (unfiltered) row */
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* The following three members were added at version 1.0.14 and 1.2.4 */
+ png_bytep quantize_sort; /* working sort array */
+ png_bytep index_to_palette; /* where the original index currently is
+ in the palette */
+ png_bytep palette_to_index; /* which original index points to this
+ palette color */
+#endif
+
+/* New members added in libpng-1.0.16 and 1.2.6 */
+ png_byte compression_type;
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ png_uint_32 user_width_max;
+ png_uint_32 user_height_max;
+
+ /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown
+ * chunks that can be stored (0 means unlimited).
+ */
+ png_uint_32 user_chunk_cache_max;
+
+ /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk
+ * can occupy when decompressed. 0 means unlimited.
+ */
+ png_alloc_size_t user_chunk_malloc_max;
+#endif
+
+/* New member added in libpng-1.0.25 and 1.2.17 */
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+ /* Storage for unknown chunk that the library doesn't recognize. */
+ png_unknown_chunk unknown_chunk;
+#endif
+
+/* New members added in libpng-1.2.26 */
+ png_size_t old_big_row_buf_size;
+ png_size_t old_prev_row_size;
+
+/* New member added in libpng-1.2.30 */
+ png_charp chunkdata; /* buffer for reading chunk data */
+
+#ifdef PNG_IO_STATE_SUPPORTED
+/* New member added in libpng-1.4.0 */
+ png_uint_32 io_state;
+#endif
+};
+#endif /* PNGSTRUCT_H */
diff --git a/pngtest.c b/pngtest.c
index 3a412dd45..9084ada62 100644
--- a/pngtest.c
+++ b/pngtest.c
@@ -1,7 +1,7 @@
/* pngtest.c - a simple test program to test libpng
*
- * Last changed in libpng 1.5.0 [January 6, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -31,6 +31,8 @@
* of files at once by typing "pngtest -m file1.png file2.png ..."
*/
+#define _POSIX_SOURCE 1
+
#include "zlib.h"
#include "png.h"
/* Copied from pngpriv.h but only used in error messages below. */
@@ -779,7 +781,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
int bit_depth, color_type;
#ifdef PNG_SETJMP_SUPPORTED
#ifdef USE_FAR_KEYWORD
- jmp_buf png_jmpbuf;
+ jmp_buf tmp_jmpbuf;
#endif
#endif
@@ -848,7 +850,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
#ifdef PNG_SETJMP_SUPPORTED
pngtest_debug("Setting jmpbuf for read struct");
#ifdef USE_FAR_KEYWORD
- if (setjmp(png_jmpbuf))
+ if (setjmp(tmp_jmpbuf))
#else
if (setjmp(png_jmpbuf(read_ptr)))
#endif
@@ -866,14 +868,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
return (1);
}
#ifdef USE_FAR_KEYWORD
- png_memcpy(png_jmpbuf(read_ptr), png_jmpbuf, png_sizeof(jmp_buf));
+ png_memcpy(png_jmpbuf(read_ptr), tmp_jmpbuf, png_sizeof(jmp_buf));
#endif
#ifdef PNG_WRITE_SUPPORTED
pngtest_debug("Setting jmpbuf for write struct");
#ifdef USE_FAR_KEYWORD
- if (setjmp(png_jmpbuf))
+ if (setjmp(tmp_jmpbuf))
#else
if (setjmp(png_jmpbuf(write_ptr)))
#endif
@@ -890,7 +892,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
}
#ifdef USE_FAR_KEYWORD
- png_memcpy(png_jmpbuf(write_ptr), png_jmpbuf, png_sizeof(jmp_buf));
+ png_memcpy(png_jmpbuf(write_ptr), tmp_jmpbuf, png_sizeof(jmp_buf));
#endif
#endif
#endif
@@ -913,6 +915,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname)
# endif
#endif
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+ /* Normally one would use Z_DEFAULT_STRATEGY for text compression.
+ * This is here just to make pngtest replicate the results from libpng
+ * versions prior to 1.5.4, and to test this new API.
+ */
+ png_set_text_compression_strategy(write_ptr, Z_FILTERED);
+#endif
+
if (status_dots_requested == 1)
{
#ifdef PNG_WRITE_SUPPORTED
@@ -1784,4 +1794,4 @@ main(int argc, char *argv[])
}
/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_5_2rc02 Your_png_h_is_not_version_1_5_2rc02;
+typedef png_libpng_version_1_5_4 Your_png_h_is_not_version_1_5_4;
diff --git a/pngtrans.c b/pngtrans.c
index c58112a3e..6a6908dcd 100644
--- a/pngtrans.c
+++ b/pngtrans.c
@@ -1,7 +1,7 @@
/* pngtrans.c - transforms the data in a row (used by both readers and writers)
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -442,7 +442,11 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
/* At the start sp will point to the first byte to copy and dp to where
* it is copied to. ep always points just beyond the end of the row, so
* the loop simply copies (channels-1) channels until sp reaches ep.
+ *
+ * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.
+ * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.
*/
+
/* GA, GX, XG cases */
if (row_info->channels == 2)
{
@@ -450,7 +454,7 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
{
if (at_start) /* Skip initial filler */
++sp;
- else /* Skip initial channels and, for sp, the filler */
+ else /* Skip initial channel and, for sp, the filler */
sp += 2, ++dp;
/* For a 1 pixel wide image there is nothing to do */
@@ -462,9 +466,9 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
else if (row_info->bit_depth == 16)
{
- if (at_start)
+ if (at_start) /* Skip initial filler */
sp += 2;
- else
+ else /* Skip initial channel and, for sp, the filler */
sp += 4, dp += 2;
while (sp < ep)
@@ -502,9 +506,9 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
else if (row_info->bit_depth == 16)
{
- if (at_start)
+ if (at_start) /* Skip initial filler */
sp += 2;
- else
+ else /* Skip initial channels and, for sp, the filler */
sp += 8, dp += 6;
while (sp < ep)
diff --git a/pngvalid.c b/pngvalid.c
new file mode 100644
index 000000000..6d1736c15
--- /dev/null
+++ b/pngvalid.c
@@ -0,0 +1,8619 @@
+
+/* pngvalid.c - validate libpng by constructing then reading png files.
+ *
+ * Last changed in libpng 1.5.4 [July 7, 2011]
+ * Copyright (c) 2011 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
+
+#include "png.h"
+#if PNG_LIBPNG_VER < 10500
+/* This delibarately 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_GAMMA_THRESHOLD_FIXED\
+ ((png_fixed_point)(PNG_GAMMA_THRESHOLD * 100000))
+#endif
+
+#include "zlib.h" /* For crc32 */
+
+#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 *****************************/
+#include "contrib/visupng/cexcept.h"
+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
+
+/******************************* 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,
+ PNG_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 PNG_CONST char invalid[] = "invalid";
+static PNG_CONST char sep[] = ": ";
+
+static PNG_CONST char *colour_types[8] =
+{
+ "greyscale", invalid, "truecolour", "indexed-colour",
+ "greyscale with alpha", invalid, "truecolour with alpha", invalid
+};
+
+/* 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 = /*no cast required*/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);
+}
+
+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))
+
+/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
+ * simply records whether pngvalid did the interlace itself or whether it
+ * was done by libpng. Width and height must be less than 256. 'palette' is an
+ * index of the palette to use for formats with a palette (0 otherwise.)
+ */
+#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) ((int)(((id) >> 8) & 0x1f))
+#define INTERLACE_FROM_ID(id) ((int)(((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, 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 (npalette > 0)
+ {
+ pos = safecat(buffer, bufsize, pos, "[");
+ pos = safecatn(buffer, bufsize, pos, npalette);
+ pos = safecat(buffer, bufsize, pos, "]");
+ }
+ 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 ? 1 : 16)
+
+static int
+next_format(png_bytep colour_type, png_bytep bit_depth, int* palette_number)
+{
+ if (*bit_depth == 0)
+ {
+ *colour_type = 0, *bit_depth = 1, *palette_number = 0;
+ return 1;
+ }
+
+ if (*colour_type == 3)
+ {
+ /* Add multiple palettes for colour type 3. */
+ if (++*palette_number < PALETTE_COUNT(*bit_depth))
+ return 1;
+
+ *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)
+{
+ 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)
+ return (result << 8) + *++row;
+
+ /* Less than 8 bits per sample. */
+ bit_index &= 7;
+ return (result >> (8-bit_index-bit_depth)) & ((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)
+{
+ /* 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) << (8-pixelSize-(toIndex&7));
+ /* The following read the entire pixels and clears the extra: */
+ unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
+ unsigned int sourceByte = fromBuffer[fromIndex >> 3];
+
+ /* Don't rely on << or >> supporting '0' here, just in case: */
+ fromIndex &= 7;
+ if (fromIndex > 0) sourceByte <<= fromIndex;
+ if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
+
+ toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
+ }
+ else /* One or more bytes */
+ memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3);
+}
+
+/* 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 (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;
+ }
+
+ return 1; /* Different */
+}
+
+/*************************** 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;
+ 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);
+}
+
+/* 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;
+ }
+}
+
+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;
+}
+
+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 = 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 = 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_structp pp, char *buffer, size_t bufsize,
+ size_t pos, PNG_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_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_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);
+}
+
+/* Functions to use as PNG callbacks. */
+static void
+store_error(png_structp pp, png_const_charp message) /* PNG_NORETURN */
+{
+ png_store *ps = 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
+store_warning(png_structp pp, png_const_charp message)
+{
+ png_store *ps = 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(PNG_CONST png_store* ps, png_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_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_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 = 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 '1' - this matches what
+ * happens with the 'size' test images on write and also matches the unused
+ * bits in the test rows.
+ */
+ memset(ps->image, 0xff, 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;
+ }
+ }
+}
+
+static void
+store_image_check(PNG_CONST png_store* ps, png_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;
+ }
+ }
+}
+
+static void
+store_write(png_structp pp, png_bytep pb, png_size_t st)
+{
+ png_store *ps = 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
+store_flush(png_structp pp)
+{
+ UNUSED(pp) /*DOES NOTHING*/
+}
+
+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
+store_read(png_structp pp, png_bytep pb, png_size_t st)
+{
+ png_store *ps = 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));
+}
+
+/* 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 = malloc(npalette * sizeof *ps->palette);
+
+ if (ps->palette == NULL)
+ png_error(ps->pwrite, "store new palette: OOM");
+
+ ps->npalette = npalette;
+ }
+
+ return ps->palette;
+}
+
+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);
+
+ /* The result may be null if there is no palette. */
+ *npalette = ps->current->npalette;
+ return ps->current->palette;
+}
+
+/***************************** MEMORY MANAGEMENT*** ***************************/
+/* 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_structp pp, PNG_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_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, (PNG_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
+store_malloc(png_structp pp, png_alloc_size_t cb)
+{
+ store_pool *pool = png_get_mem_ptr(pp);
+ store_memory *new = 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
+ store_pool_error(pool->store, pp, "out of memory");
+
+ return new;
+}
+
+static void
+store_free(png_structp pp, png_voidp memory)
+{
+ store_pool *pool = png_get_mem_ptr(pp);
+ store_memory *this = memory, **test;
+
+ /* 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);
+}
+
+/* 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.
+ */
+ store_pool_delete(ps, &ps->write_memory_pool);
+
+ 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,
+ PNG_CONST char * volatile 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. */
+ if (ps->speed)
+ ps->pwrite = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ ps, store_error, store_warning);
+
+ else
+ ps->pwrite = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
+ ps, store_error, store_warning, &ps->write_memory_pool,
+ store_malloc, store_free);
+
+ png_set_write_fn(ps->pwrite, ps, store_write, store_flush);
+
+ 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).
+ */
+static void
+store_read_reset(png_store *ps)
+{
+ 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;
+ }
+
+ /* Always do this to be safe. */
+ store_pool_delete(ps, &ps->read_memory_pool);
+
+ ps->current = NULL;
+ ps->next = NULL;
+ ps->readpos = 0;
+ ps->validated = 0;
+}
+
+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,
+ PNG_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.
+ */
+ if (ps->speed)
+ ps->pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, ps,
+ store_error, store_warning);
+
+ else
+ ps->pread = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, ps,
+ store_error, store_warning, &ps->read_memory_pool, store_malloc,
+ store_free);
+
+ 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;
+ }
+
+ store_read_set(ps, id);
+
+ if (ppi != NULL)
+ *ppi = ps->piread = png_create_info_struct(ps->pread);
+
+ return ps->pread;
+}
+
+/* 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 struct png_modifier
+{
+ png_store this; /* I am a png_store */
+ struct png_modification *modifications; /* Changes to make */
+
+ enum modifier_state
+ {
+ modifier_start, /* Initial value */
+ modifier_signature, /* Have a signature */
+ modifier_IHDR /* Have an IHDR */
+ } 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;
+
+ /* Lowest sbit to test (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 maxpc16; /* Percentage sample error 0..100% */
+
+ /* 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 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 combiniation of transforms, */
+ unsigned int test_transform :1;
+
+ /* When to use the use_input_precision option: */
+ 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 both input
+ * and output are 8 bit or less.
+ */
+ 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 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->gammas = 0;
+ pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0;
+ pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0;
+ 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->interlace_type = PNG_INTERLACE_NONE;
+ pm->test_standard = 0;
+ pm->test_size = 0;
+ pm->test_transform = 0;
+ pm->use_input_precision = 0;
+ pm->use_input_precision_sbit = 0;
+ pm->use_input_precision_16to8 = 0;
+ pm->calculations_use_input_precision = 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->log = 0;
+
+ /* Rely on the memset for all the other fields - there are no pointers */
+}
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+/* If pm->calculations_use_input_precision is set then operations will happen
+ * with only 8 bit precision unless both the input and output bit depth are 16.
+ *
+ * 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!
+ */
+static double abserr(png_modifier *pm, int in_depth, int out_depth)
+{
+ /* Absolute error permitted in linear values - affected by the bit depth of
+ * the calculations.
+ */
+ if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
+ !pm->calculations_use_input_precision)))
+ return pm->maxabs16;
+ else
+ return pm->maxabs8;
+}
+
+static double calcerr(png_modifier *pm, int in_depth, int out_depth)
+{
+ /* Error in the linear composition arithmetic - only relevant when
+ * composition actually happens (0 < alpha < 1).
+ */
+ if (pm->assume_16_bit_calculations || (out_depth == 16 && (in_depth == 16 ||
+ !pm->calculations_use_input_precision)))
+ return pm->maxcalc16;
+ else
+ return pm->maxcalc8;
+}
+
+static double pcerr(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 || (out_depth == 16 && (in_depth == 16 ||
+ !pm->calculations_use_input_precision)))
+ 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(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 greyscale 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 (out_depth == 16 && (in_depth == 16 ||
+ !pm->calculations_use_input_precision))
+ 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(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 (out_depth == 16 && (in_depth == 16 ||
+ !pm->calculations_use_input_precision))
+ {
+ 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(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;
+}
+
+/* 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);
+}
+
+static void
+modifier_reset(png_modifier *pm)
+{
+ store_read_reset(&pm->this);
+ pm->modifications = NULL;
+ pm->state = modifier_start;
+ pm->bit_depth = pm->colour_type = 0;
+ pm->pending_len = pm->pending_chunk = 0;
+ pm->flush = pm->buffer_count = pm->buffer_position = 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);
+ png_save_uint_32(buffer+datalen+8, crc32(0L, buffer+4, datalen+4));
+}
+
+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
+modifier_read(png_structp pp, png_bytep pb, png_size_t st)
+{
+ png_modifier *pm = 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 truely 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,
+ PNG_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);
+}
+#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.
+ *
+ * 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] = (i&1) ? 255 : 0;
+ values[i][2] = (i&2) ? 255 : 0;
+ values[i][3] = (i&4) ? 255 : 0;
+ }
+
+ /* Then add 62 greys (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 greys,
+ * 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.
+ */
+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;
+
+ if (j > 0)
+ png_set_tRNS(pp, pi, tRNS, j, 0/*color*/);
+ }
+}
+
+/* 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_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_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_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_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 */
+ }
+}
+
+/* The following can only be defined here, now we have the definitions
+ * of the transform image sizes.
+ */
+static png_uint_32
+standard_width(png_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_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_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;
+}
+
+static void
+transform_row(png_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] = v & 0xff, v += 17, ++i;
+ return;
+
+ case 2:
+ while (i<128/4) buffer[i] = v & 0xff, v += 33, ++i;
+ return;
+
+ case 4:
+ while (i<128/2) buffer[i] = v & 0xff, v += 65, ++i;
+ return;
+
+ case 8:
+ /* 256 bytes total, 128 bytes in each row set as follows: */
+ while (i<128) buffer[i] = 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] = (v>>8) & 0xff, buffer[2*i+1] = 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] = (v >> 8) & 0xff;
+ buffer[3*i+1] = v & 0xff;
+ buffer[3*i+2] = ((v >> 8) ^ v) & 0xff;
+ ++v;
+ ++i;
+ }
+
+ return;
+
+ case 32:
+ /* 65535 pixels, r, g, b, a; just replicate */
+ while (i<128)
+ {
+ buffer[4*i+0] = (v >> 8) & 0xff;
+ buffer[4*i+1] = v & 0xff;
+ buffer[4*i+2] = (v >> 8) & 0xff;
+ buffer[4*i+3] = 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] = (t >> 8) & 0xff;
+ buffer[6*i+1] = t & 0xff;
+ t *= 257;
+ buffer[6*i+2] = (t >> 8) & 0xff;
+ buffer[6*i+3] = t & 0xff;
+ t *= 17;
+ buffer[6*i+4] = (t >> 8) & 0xff;
+ buffer[6*i+5] = 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] = (t >> 8) & 0xff;
+ buffer[8*i+1] = t & 0xff;
+ buffer[8*i+4] = (t >> 8) & 0xff;
+ buffer[8*i+5] = t & 0xff;
+ t *= 257;
+ buffer[8*i+2] = (t >> 8) & 0xff;
+ buffer[8*i+3] = t & 0xff;
+ buffer[8*i+6] = (t >> 8) & 0xff;
+ buffer[8*i+7] = 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)))
+
+/* Make a standardized image given a an image colour type, bit depth and
+ * interlace type. The standard images have a very restricted range of
+ * rows and heights and are used for testing transforms rather than image
+ * 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* PNG_CONST ps, png_byte PNG_CONST colour_type,
+ png_byte PNG_CONST bit_depth, int palette_number, int interlace_type,
+ png_const_charp name)
+{
+ context(ps, fault);
+
+ Try
+ {
+ png_infop pi;
+ png_structp pp = set_store_for_write(ps, &pi, name);
+ png_uint_32 h;
+
+ /* 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;
+
+ h = transform_height(pp, colour_type, bit_depth);
+
+ png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), h,
+ bit_depth, colour_type, interlace_type,
+ PNG_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 = PNG_TEXT_COMPRESSION_zTXt;
+ 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*/);
+
+ png_write_info(pp, pi);
+
+ if (png_get_rowbytes(pp, pi) !=
+ transform_rowsize(pp, colour_type, bit_depth))
+ png_error(pp, "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 = png_set_interlace_handling(pp);
+ int pass;
+
+ if (npasses != npasses_from_interlace_type(pp, interlace_type))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ 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);
+ 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 = PNG_TEXT_COMPRESSION_zTXt;
+ 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_store *ps)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ int palette_number = 0;
+
+ /* This is in case of errors. */
+ safecat(ps->test, sizeof ps->test, 0, "make standard images");
+
+ /* Use next_format to enumerate all the combinations we test, including
+ * generating multiple low bit depth palette images.
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number))
+ {
+ int interlace_type;
+
+ for (interlace_type = PNG_INTERLACE_NONE;
+ interlace_type < PNG_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, 0);
+ make_transform_image(ps, colour_type, bit_depth, palette_number,
+ interlace_type, name);
+ }
+ }
+}
+
+/* The following two routines use the PNG interlace support macros from
+ * png.h to interlace or deinterlace rows.
+ */
+static void
+interlace_row(png_bytep buffer, png_const_bytep imageRow,
+ unsigned int pixel_size, png_uint_32 w, int pass)
+{
+ png_uint_32 xin, xout, xstep;
+
+ /* Note that this can, trivially, be optimized to a memcpy on pass 7, the
+ * code is presented this way to make it easier to understand. In practice
+ * consult the code in the libpng source to see other ways of doing this.
+ */
+ xin = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xout=0; xin<w; xin+=xstep)
+ {
+ pixel_copy(buffer, xout, imageRow, xin, pixel_size);
+ ++xout;
+ }
+}
+
+static void
+deinterlace_row(png_bytep buffer, png_const_bytep row,
+ unsigned int pixel_size, png_uint_32 w, int pass)
+{
+ /* The inverse of the above, 'row' is part of row 'y' of the output image,
+ * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
+ * the pixels of row into buffer and return the number written (to allow
+ * this to be checked).
+ */
+ png_uint_32 xin, xout, xstep;
+
+ xout = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xin=0; xout<w; xout+=xstep)
+ {
+ pixel_copy(buffer, xout, row, xin, pixel_size);
+ ++xin;
+ }
+}
+
+/* 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* PNG_CONST ps, png_byte PNG_CONST colour_type,
+ png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
+ png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
+ int PNG_CONST do_interlace)
+{
+ context(ps, fault);
+
+ Try
+ {
+ png_infop pi;
+ png_structp pp;
+ unsigned int pixel_size;
+
+ /* Make a name and get an appropriate id for the store: */
+ char name[FILE_NAME_SIZE];
+ PNG_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);
+
+ 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, "row size incorrect");
+
+ else
+ {
+ int npasses = npasses_from_interlace_type(pp, interlace_type);
+ png_uint_32 y;
+ int pass;
+ png_byte image[16][SIZE_ROWMAX];
+
+ /* To help consistent error detection make the parts of this buffer
+ * that aren't set below all '1':
+ */
+ memset(image, 0xff, sizeof image);
+
+ if (!do_interlace && npasses != png_set_interlace_handling(pp))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ /* Prepare the whole image first to avoid making it 7 times: */
+ for (y=0; y<h; ++y)
+ size_row(image[y], w * pixel_size, y);
+
+ for (pass=0; pass<npasses; ++pass)
+ {
+ /* The following two are for checking the macros: */
+ PNG_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);
+ row = tempRow;
+ }
+ else
+ continue;
+ }
+
+ /* Only get to here if the row has some pixels in it. */
+ png_write_row(pp, row);
+ }
+ }
+ }
+
+ 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* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
+ int PNG_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);
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+ width, height, 0);
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+ width, height, 1);
+ }
+ }
+ }
+}
+
+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);
+}
+
+/* Return a row based on image id and 'y' for checking: */
+static void
+standard_row(png_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);
+}
+
+/* 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!
+ */
+#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 PNG_CONST struct
+{
+ void (*fn)(png_structp, png_infop);
+ PNG_CONST char *msg;
+ unsigned int warning :1; /* the error is a warning... */
+} error_test[] =
+ {
+ /* no warnings makes these errors undetectable. */
+ { sBIT0_error_fn, "sBIT(0): failed to detect error", 1 },
+ { sBIT_error_fn, "sBIT(too big): failed to detect error", 1 },
+ };
+
+static void
+make_error(png_store* volatile ps, png_byte PNG_CONST colour_type,
+ png_byte bit_depth, int interlace_type, int test, png_const_charp name)
+{
+ context(ps, fault);
+
+ Try
+ {
+ png_structp pp;
+ png_infop pi;
+
+ pp = set_store_for_write(ps, &pi, name);
+
+ if (pp == NULL)
+ Throw ps;
+
+ png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
+ transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
+ interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ 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
+ {
+ /* 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)
+ ps = fault; /* expected exit, make sure ps is not clobbered */
+#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
+ {
+ png_uint_32 h = transform_height(pp, colour_type, bit_depth);
+ int npasses = png_set_interlace_handling(pp);
+ int pass;
+
+ if (npasses != npasses_from_interlace_type(pp, interlace_type))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ 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);
+ 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* PNG_CONST pm, png_byte PNG_CONST colour_type,
+ int bdlo, int PNG_CONST bdhi)
+{
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ int interlace_type;
+
+ for (interlace_type = PNG_INTERLACE_NONE;
+ interlace_type < PNG_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, 0);
+
+ for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
+ {
+ make_error(&pm->this, colour_type, DEPTH(bdlo), interlace_type,
+ test, name);
+
+ if (fail(pm))
+ return 0;
+ }
+ }
+ }
+
+ return 1; /* keep going */
+}
+#endif
+
+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 *volatile 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;
+ 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 */
+
+ result = png_convert_to_rfc1123(pp, &pt);
+
+ 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
+}
+
+/* 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;
+ int interlace_type;
+ 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 is_transparent; /* Transparency information was present. */
+ int speed; /* Doing a speed test */
+ 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)
+{
+ dp->ps = ps;
+ dp->colour_type = COL_FROM_ID(id);
+ dp->bit_depth = DEPTH_FROM_ID(id);
+ 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);
+ 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->is_transparent = 0;
+ dp->speed = ps->speed;
+ 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_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 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 zap 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_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->is_transparent = 1;
+ break;
+
+ case 2:
+ dp->transparent.red = trans_color->red;
+ dp->transparent.green = trans_color->green;
+ dp->transparent.blue = trans_color->blue;
+ dp->is_transparent = 1;
+ 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 && dp->npasses != png_set_interlace_handling(pp))
+ png_error(pp, "validate: file changed interlace type");
+
+ /* 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_structp pp, png_infop pi,
+ int nImages)
+{
+ /* Record cbRow now that it can be found. */
+ dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
+ png_get_bit_depth(pp, pi));
+ 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.
+ */
+ 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
+standard_info(png_structp pp, png_infop pi)
+{
+ standard_display *dp = 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
+progressive_row(png_structp pp, png_bytep new_row, png_uint_32 y, int pass)
+{
+ PNG_CONST standard_display *dp = 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
+
+ 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: */
+ if (dp->do_interlace)
+ {
+ if (dp->interlace_type == PNG_INTERLACE_ADAM7)
+ deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass);
+ else
+ memcpy(row, new_row, dp->cbRow);
+ }
+ else
+ png_progressive_combine_row(pp, row, new_row);
+ } 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,
+ PNG_CONST int iImage, PNG_CONST int iDisplay)
+{
+ PNG_CONST int npasses = dp->npasses;
+ PNG_CONST int do_interlace = dp->do_interlace &&
+ dp->interlace_type == PNG_INTERLACE_ADAM7;
+ PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
+ PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
+ PNG_CONST png_store* ps = dp->ps;
+ 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.
+ */
+ memset(row, 0xff, sizeof row);
+ memset(display, 0, 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);
+
+ if (iDisplay >= 0)
+ deinterlace_row(store_image_row(ps, pp, iDisplay, y), display,
+ dp->pixel_size, dp->w, pass);
+ }
+ }
+ 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);
+}
+
+static void
+standard_row_validate(standard_display *dp, png_structp pp,
+ int iImage, int iDisplay, png_uint_32 y)
+{
+ png_byte std[STANDARD_ROWMAX];
+
+ memset(std, 0xff, 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.
+ * The side effect inside the libpng sequential reader is that the 'row'
+ * array retains the correct values for unwritten pixels within the row
+ * bytes, while the 'display' array gets bits off the end of the image (in
+ * the last byte) trashed. Unfortunately in the progressive reader the
+ * row bytes are always trashed, so we always do a pixel_cmp here even though
+ * a memcmp of all cbRow bytes will succeed for the sequential reader.
+ */
+ if (iImage >= 0 && pixel_cmp(std, store_image_row(dp->ps, pp, iImage, y),
+ dp->bit_width) != 0)
+ {
+ char msg[64];
+ sprintf(msg, "PNG image row %d changed", y);
+ png_error(pp, msg);
+ }
+
+ /* In this case use pixel_cmp because we need to compare a partial
+ * byte at the end of the row if the row is not an exact multiple
+ * of 8 bits wide.
+ */
+ if (iDisplay >= 0 && pixel_cmp(std, store_image_row(dp->ps, pp, iDisplay, y),
+ dp->bit_width) != 0)
+ {
+ char msg[64];
+ sprintf(msg, "display row %d changed", y);
+ png_error(pp, msg);
+ }
+}
+
+static void
+standard_image_validate(standard_display *dp, png_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
+standard_end(png_structp pp, png_infop pi)
+{
+ standard_display *dp = png_get_progressive_ptr(pp);
+
+ UNUSED(pi)
+
+ /* Validate the image - progressive reading only produces one variant for
+ * interlaced images.
+ */
+ 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* PNG_CONST psIn, png_uint_32 PNG_CONST id,
+ int do_interlace)
+{
+ 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);
+
+ /* 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_image_validate(&d, pp, 0, 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* PNG_CONST pm, png_byte PNG_CONST colour_type,
+ int bdlo, int PNG_CONST bdhi)
+{
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ int interlace_type;
+
+ for (interlace_type = PNG_INTERLACE_NONE;
+ interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
+ {
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ interlace_type, 0, 0, 0), 0/*do_interlace*/);
+
+ 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* PNG_CONST pm, png_byte PNG_CONST colour_type,
+ int bdlo, int PNG_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 PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
+ static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
+ 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*/);
+
+ 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*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/);
+
+ 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*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/,
+ PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+ }
+ }
+
+ 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 greyscale 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 */
+ int have_tRNS; /* tRNS chunk may need processing */
+
+ /* 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.
+ */
+ 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 max)
+{
+ this->redf = this->red / (double)max;
+ this->greenf = this->green / (double)max;
+ this->bluef = this->blue / (double)max;
+ this->alphaf = this->alpha / (double)max;
+
+ if (this->red < max)
+ this->rede = this->redf * DBL_EPSILON;
+ else
+ this->rede = 0;
+ if (this->green < max)
+ this->greene = this->greenf * DBL_EPSILON;
+ else
+ this->greene = 0;
+ if (this->blue < max)
+ this->bluee = this->bluef * DBL_EPSILON;
+ else
+ this->bluee = 0;
+ if (this->alpha < max)
+ 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)
+{
+ PNG_CONST png_byte sample_depth = (png_byte)(colour_type ==
+ PNG_COLOR_TYPE_PALETTE ? 8 : bit_depth);
+ PNG_CONST unsigned int max = (1U<<sample_depth)-1;
+
+ /* 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);
+ 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)
+ {
+ PNG_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 & 2)
+ {
+ this->green = sample(row, colour_type, bit_depth, x, 1);
+ this->blue = sample(row, colour_type, bit_depth, x, 2);
+ i = 2;
+ }
+ if (colour_type & 4)
+ this->alpha = sample(row, colour_type, bit_depth, x, ++i);
+ }
+
+ /* Calculate the scaled values, these are simply the values divided by
+ * 'max' and the error is initialized to the double precision epsilon value
+ * from the header file.
+ */
+ image_pixel_setf(this, max);
+
+ /* 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;
+}
+
+/* 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. Palette images will be converted to alpha (using the above API).
+ */
+static void
+image_pixel_add_alpha(image_pixel *this, const standard_display *display)
+{
+ 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 (this->bit_depth < 8)
+ this->bit_depth = 8;
+
+ if (this->have_tRNS)
+ {
+ 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;
+
+ 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;
+ }
+}
+
+struct transform_display;
+typedef struct image_transform
+{
+ /* The name of this transform: a string. */
+ PNG_CONST char *name;
+
+ /* Each transform can be disabled from the command line: */
+ int enable;
+
+ /* The global list of transforms; read only. */
+ struct image_transform *PNG_CONST list;
+
+ /* 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.
+ */
+ PNG_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 set the transform on the current png_read_struct:
+ */
+ void (*set)(PNG_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)(PNG_CONST struct image_transform *this, image_pixel *that,
+ png_structp pp, PNG_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,
+ PNG_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;
+ PNG_CONST image_transform* transform_list;
+
+ /* Local variables */
+ png_byte output_colour_type;
+ png_byte output_bit_depth;
+
+ /* Variables for the individual transforms. */
+ /* png_set_background */
+ image_pixel background_colour;
+} transform_display;
+
+/* Two functions to end the list: */
+static void
+image_transform_set_end(PNG_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(PNG_CONST image_transform *this, image_pixel *that,
+ png_structp pp, PNG_CONST transform_display *display)
+{
+ PNG_CONST unsigned int scale = (1U<<that->sample_depth)-1;
+
+ 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);
+
+ /* 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);
+ that->blue = sample_scale(that->bluef, scale);
+ 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->alpha = 1; /* Override this. */
+ that->alphae = 0; /* It's exact ;-) */
+ }
+}
+
+/* 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_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,
+ PNG_CONST image_transform *transform_list)
+{
+ /* Standard fields */
+ standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
+
+ /* Parameter fields */
+ dp->pm = pm;
+ dp->transform_list = transform_list;
+
+ /* Local variable fields */
+ dp->output_colour_type = 255; /* invalid */
+ dp->output_bit_depth = 255; /* invalid */
+}
+
+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: */
+ png_read_update_info(pp, pi);
+
+ /* 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);
+
+ /* 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.
+ */
+ {
+ 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 */
+ test_pixel.have_tRNS = dp->this.is_transparent;
+
+ 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.
+ * I believe these are both internal errors.
+ */
+ if (test_pixel.colour_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (test_pixel.sample_depth != 8) /* oops - internal error! */
+ png_error(pp, "pngvalid: internal: palette sample depth not 8");
+ }
+ else if (test_pixel.sample_depth != dp->output_bit_depth)
+ {
+ char message[128];
+ size_t pos = safecat(message, sizeof message, 0,
+ "internal: sample depth ");
+
+ pos = safecatn(message, sizeof message, pos, dp->output_bit_depth);
+ pos = safecat(message, sizeof message, pos, " expected ");
+ pos = safecatn(message, sizeof message, pos, test_pixel.sample_depth);
+
+ png_error(pp, message);
+ }
+ }
+}
+
+static void
+transform_info(png_structp pp, png_infop pi)
+{
+ transform_info_imp(png_get_progressive_ptr(pp), pp, pi);
+}
+
+static void
+transform_range_check(png_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, PNG_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 (the threshold is about 1/255.)
+ */
+ 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 > 4E-3 || !(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_structp pp, png_infop pi)
+{
+ /* Constants for the loop below: */
+ PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
+ PNG_CONST png_byte in_ct = dp->this.colour_type;
+ PNG_CONST png_byte in_bd = dp->this.bit_depth;
+ PNG_CONST png_uint_32 w = dp->this.w;
+ PNG_CONST png_uint_32 h = dp->this.h;
+ PNG_CONST png_byte out_ct = dp->output_colour_type;
+ PNG_CONST png_byte out_bd = dp->output_bit_depth;
+ PNG_CONST png_byte sample_depth = (png_byte)(out_ct ==
+ PNG_COLOR_TYPE_PALETTE ? 8 : out_bd);
+ PNG_CONST png_byte red_sBIT = dp->this.red_sBIT;
+ PNG_CONST png_byte green_sBIT = dp->this.green_sBIT;
+ PNG_CONST png_byte blue_sBIT = dp->this.blue_sBIT;
+ PNG_CONST png_byte alpha_sBIT = dp->this.alpha_sBIT;
+ PNG_CONST int have_tRNS = dp->this.is_transparent;
+ 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);
+
+ /* assume-8-bit-calculations means assume that if the input has 8 bit
+ * (or less) samples and the output has 16 bit samples the calculations
+ * will be done with 8 bit precision, not 16.
+ *
+ * TODO: fix this in libpng; png_set_expand_16 should cause 16 bit
+ * calculations to be used throughout.
+ */
+ 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 errors are at 8 bit precision, scale .5 in 8 bits to the 16 bits:
+ */
+ else
+ digitization_error = .5 * 257;
+ }
+
+ for (y=0; y<h; ++y)
+ {
+ png_const_bytep PNG_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);
+
+ 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;
+
+ /* For error detection, below. */
+ r = in_pixel.red;
+ g = in_pixel.green;
+ b = in_pixel.blue;
+ a = in_pixel.alpha;
+
+ 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.
+ */
+ image_pixel_init(&out_pixel, pRow, out_ct, out_bd, x, out_palette);
+
+ /* 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, "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,
+ "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, "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,
+ "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
+transform_end(png_structp pp, png_infop pi)
+{
+ transform_display *dp = png_get_progressive_ptr(pp);
+
+ transform_image_validate(dp, pp, pi);
+}
+
+/* A single test run. */
+static void
+transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn,
+ PNG_CONST image_transform* transform_listIn, PNG_CONST char *name)
+{
+ transform_display d;
+ context(&pmIn->this, fault);
+
+ transform_display_init(&d, pmIn, idIn, transform_listIn);
+
+ Try
+ {
+ png_structp pp;
+ png_infop pi;
+
+ /* 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);
+
+# 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);
+ }
+
+ modifier_reset(d.pm);
+ }
+
+ Catch(fault)
+ modifier_reset((png_modifier*)fault);
+}
+
+/* The transforms: */
+#define ITSTRUCT(name) image_transform_##name
+#define IT(name)\
+static image_transform ITSTRUCT(name) =\
+{\
+ #name,\
+ 1, /*enable*/\
+ &PT, /*list*/\
+ 0, /*global_use*/\
+ 0, /*local_use*/\
+ 0, /*next*/\
+ 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: */
+static int
+image_transform_default_add(image_transform *this,
+ PNG_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;
+}
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* png_set_palette_to_rgb */
+static void
+image_transform_png_set_palette_to_rgb_set(PNG_CONST image_transform *this,
+ 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(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_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,
+ PNG_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(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_tRNS_to_alpha(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_tRNS_to_alpha_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
+{
+ /* LIBPNG BUG: this always forces palette images to RGB. */
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+
+ /* This effectively does an 'expand' only if there is some transparency to
+ * convert to an alpha channel.
+ */
+ if (that->have_tRNS)
+ image_pixel_add_alpha(that, &display->this);
+
+ /* 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;
+ }
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_tRNS_to_alpha_add(image_transform *this,
+ PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ 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.
+ */
+ return (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(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_gray_to_rgb(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_gray_to_rgb_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_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);
+
+ /* 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,
+ PNG_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(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_expand(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_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);
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_add(image_transform *this,
+ PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ 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
+ * LIBPNG BUG: this just does an 'expand'
+ */
+static void
+image_transform_png_set_expand_gray_1_2_4_to_8_set(
+ PNG_CONST image_transform *this, transform_display *that, png_structp pp,
+ png_infop pi)
+{
+ png_set_expand_gray_1_2_4_to_8(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_gray_1_2_4_to_8_mod(
+ PNG_CONST image_transform *this, image_pixel *that, png_structp pp,
+ PNG_CONST transform_display *display)
+{
+ image_transform_png_set_expand_mod(this, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_gray_1_2_4_to_8_add(image_transform *this,
+ PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ return image_transform_png_set_expand_add(this, that, colour_type,
+ bit_depth);
+}
+
+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(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_expand_16(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_expand_16_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
+{
+ /* Expect expand_16 to expand everything to 16 bits as a result of also
+ * causing 'expand' to happen.
+ */
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+
+ if (that->have_tRNS)
+ image_pixel_add_alpha(that, &display->this);
+
+ if (that->bit_depth < 16)
+ that->sample_depth = that->bit_depth = 16;
+
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_expand_16_add(image_transform *this,
+ PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth)
+{
+ UNUSED(colour_type)
+
+ this->next = *that;
+ *that = this;
+
+ /* expand_16 does something unless the bit depth is already 16. */
+ return bit_depth < 16;
+}
+
+IT(expand_16);
+#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(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_scale_16(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_scale_16_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_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,
+ PNG_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(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_strip_16(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_strip_16_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_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!)
+ */
+ {
+ PNG_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,
+ PNG_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(PNG_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(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_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,
+ PNG_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
+ *
+ * At present the APIs are simply tested using the 16.16 fixed point conversion
+ * values known to be used inside libpng:
+ *
+ * red: 6968
+ * green: 23434
+ * blue: 2366
+ *
+ * NOTE: this currently ignores the gamma because no gamma is being set, the
+ * tests on gamma need to happen in the gamma test set.
+ */
+static void
+image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ PNG_CONST int error_action = 1; /* no error, no defines in png.h */
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_rgb_to_gray(pp, error_action, -1, -1);
+# else
+ png_set_rgb_to_gray_fixed(pp, error_action, -1, -1);
+# endif
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
+{
+ if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0)
+ {
+ if (that->colour_type == PNG_COLOR_TYPE_PALETTE)
+ image_pixel_convert_PLTE(that);
+
+ /* Image now has RGB channels... */
+ that->bluef = that->greenf = that->redf = (that->redf * 6968 +
+ that->greenf * 23434 + that->bluef * 2366) / 32768;
+ that->bluee = that->greene = that->rede = (that->rede * 6968 +
+ that->greene * 23434 + that->bluee * 2366) / 32768 *
+ (1 + DBL_EPSILON * 6);
+
+ /* 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 zap 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,
+ PNG_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(rgb_to_gray);
+#undef PT
+#define PT ITSTRUCT(rgb_to_gray)
+#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)
+ *
+ * As with rgb_to_gray this ignores the gamma.
+*/
+static void
+image_transform_png_set_background_set(PNG_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 */
+ 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;
+ }
+
+ else
+ bit_depth = that->this.bit_depth;
+
+ image_pixel_init(&that->background_colour, random_bytes, colour_type,
+ bit_depth, 0/*x*/, 0/*unused: palette*/);
+
+ /* 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)that->background_colour.red;
+ back.green = (png_uint_16)that->background_colour.green;
+ back.blue = (png_uint_16)that->background_colour.blue;
+ }
+
+ else
+ back.gray = (png_uint_16)that->background_colour.red;
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+ png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, 1/*need expand*/,
+ 0);
+# else
+ png_set_background_fixed(pp, &back, PNG_BACKGROUND_GAMMA_FILE,
+ 1/*need expand*/, 0);
+# endif
+
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_background_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
+{
+ /* Check for tRNS first: */
+ if (that->have_tRNS && that->colour_type != PNG_COLOR_TYPE_PALETTE)
+ image_pixel_add_alpha(that, &display->this);
+
+ /* This is only necessary if the alpha value is less than 1. */
+ if (that->alphaf < 1)
+ {
+ PNG_CONST image_pixel *back = &display->background_colour;
+
+ /* Now we do the background calculation without any gamma correction. */
+ if (that->alphaf <= 0)
+ {
+ that->redf = back->redf;
+ that->greenf = back->greenf;
+ that->bluef = back->bluef;
+
+ that->rede = back->rede;
+ that->greene = back->greene;
+ that->bluee = back->bluee;
+
+ that->red_sBIT= back->red_sBIT;
+ that->green_sBIT= back->green_sBIT;
+ that->blue_sBIT= back->blue_sBIT;
+ }
+
+ else /* 0 < alpha < 1 */
+ {
+ double alf = 1 - that->alphaf;
+
+ that->redf = that->redf * that->alphaf + back->redf * alf;
+ that->rede = that->rede * that->alphaf + back->rede * alf +
+ DBL_EPSILON;
+ that->greenf = that->greenf * that->alphaf + back->greenf * alf;
+ that->greene = that->greene * that->alphaf + back->greene * alf +
+ DBL_EPSILON;
+ that->bluef = that->bluef * that->alphaf + back->bluef * alf;
+ that->bluee = that->bluee * that->alphaf + back->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
+
+IT(background);
+#undef PT
+#define PT ITSTRUCT(background)
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+/* This may just be 'end' if all the transforms are disabled! */
+static image_transform *PNG_CONST image_transform_first = &PT;
+
+static void
+transform_enable(PNG_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(1);
+ }
+}
+
+static void
+transform_disable(PNG_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(1);
+}
+
+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(PNG_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 remvoe 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;
+ }
+}
+
+#ifdef THIS_IS_THE_PROFORMA
+static void
+image_transform_png_set_@_set(PNG_CONST image_transform *this,
+ transform_display *that, png_structp pp, png_infop pi)
+{
+ png_set_@(pp);
+ this->next->set(this->next, that, pp, pi);
+}
+
+static void
+image_transform_png_set_@_mod(PNG_CONST image_transform *this,
+ image_pixel *that, png_structp pp, PNG_CONST transform_display *display)
+{
+ this->next->mod(this->next, that, pp, display);
+}
+
+static int
+image_transform_png_set_@_add(image_transform *this,
+ PNG_CONST image_transform **that, char *name, size_t sizeof_name,
+ size_t *pos, png_byte colour_type, png_byte bit_depth)
+{
+ this->next = *that;
+ *that = this;
+
+ *pos = safecat(name, sizeof_name, *pos, " +@");
+
+ return 1;
+}
+
+IT(@);
+#endif
+
+/* png_set_quantize(png_structp, png_colorp palette, int num_palette,
+ * int maximum_colors, png_const_uint_16p histogram, int full_quantize)
+ *
+ * Very difficult to validate this!
+ */
+/*NOTE: TBD NYI */
+
+/* The data layout transforms are handled by swapping our own channel data,
+ * necessarily these need to happen at the end of the transform list because the
+ * semantic of the channels changes after these are executed. Some of these,
+ * like set_shift and set_packing, can't be done at present because they change
+ * the layout of the data at the sub-sample level so sample() won't get the
+ * right answer.
+ */
+/* png_set_invert_alpha */
+/*NOTE: TBD NYI */
+
+/* png_set_bgr */
+/*NOTE: TBD NYI */
+
+/* png_set_swap_alpha */
+/*NOTE: TBD NYI */
+
+/* png_set_swap */
+/*NOTE: TBD NYI */
+
+/* png_set_filler, (png_structp png_ptr, png_uint_32 filler, int flags)); */
+/*NOTE: TBD NYI */
+
+/* png_set_add_alpha, (png_structp png_ptr, png_uint_32 filler, int flags)); */
+/*NOTE: TBD NYI */
+
+/* png_set_packing */
+/*NOTE: TBD NYI */
+
+/* png_set_packswap */
+/*NOTE: TBD NYI */
+
+/* png_set_invert_mono */
+/*NOTE: TBD NYI */
+
+/* png_set_shift(png_structp, png_const_color_8p true_bits) */
+/*NOTE: TBD NYI */
+
+static void
+perform_transform_test(png_modifier *pm)
+{
+ png_byte colour_type = 0;
+ png_byte bit_depth = 0;
+ int palette_number = 0;
+
+ while (next_format(&colour_type, &bit_depth, &palette_number))
+ {
+ 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;
+ PNG_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. */
+ transform_test(pm, FILEID(colour_type, bit_depth, palette_number,
+ pm->interlace_type, 0, 0, 0), list, name);
+
+ if (fail(pm))
+ return;
+ }
+ }
+}
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+/********************************* GAMMA TESTS ********************************/
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Gamma test images. */
+typedef struct gamma_modification
+{
+ png_modification this;
+ png_fixed_point gamma;
+} gamma_modification;
+
+static int
+gamma_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, ((gamma_modification*)me)->gamma);
+ return 1;
+}
+
+static void
+gamma_modification_init(gamma_modification *me, png_modifier *pm, double gammad)
+{
+ double g;
+
+ modification_init(&me->this);
+ me->this.chunk = CHUNK_gAMA;
+ me->this.modify_fn = gamma_modify;
+ me->this.add = CHUNK_PLTE;
+ g = floor(gammad * 100000 + .5);
+ me->gamma = (png_fixed_point)g;
+ 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;
+}
+
+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;
+}
+
+/* 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, PNG_CONST png_color_16 *pointer_to_the_background_color,
+ double background_gamma)
+{
+ /* Standard fields */
+ standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
+
+ /* 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 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.
+ */
+ PNG_CONST double sg = dp->screen_gamma;
+# ifndef PNG_FLOATING_POINT_SUPPORTED
+ PNG_CONST png_fixed_point g = (png_fixed_point)(sg*100000+.5);
+# 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 = (png_fixed_point)(dp->file_gamma*100000+.5);
+ 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 = (png_fixed_point)(dp->screen_gamma*100000+.5);
+ png_fixed_point f = (png_fixed_point)(dp->file_gamma*100000+.5);
+ 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!
+ */
+ PNG_CONST double bg = dp->background_gamma;
+# ifndef PNG_FLOATING_POINT_SUPPORTED
+ PNG_CONST png_fixed_point g = (png_fixed_point)(bg*100000+.5);
+# 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
+ }
+ }
+
+ png_read_update_info(pp, pi);
+
+ /* Now we may get a different cbRow: */
+ standard_info_part2(&dp->this, pp, pi, 1 /*images*/);
+}
+
+static void
+gamma_info(png_structp pp, png_infop pi)
+{
+ gamma_info_imp(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_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_struct *pp,
+ int in_depth, int out_depth)
+{
+ PNG_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))
+ {
+ vi->do_background = dp->do_background;
+
+ if (vi->do_background != 0)
+ {
+ PNG_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
+ 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)
+ {
+ 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;
+
+#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.)
+ */
+ break;
+ }
+
+ return input_sample;
+}
+
+/* This API returns the encoded *input* component, in the range 0..1 */
+static double
+gamma_component_validate(PNG_CONST char *name, PNG_CONST validate_info *vi,
+ PNG_CONST unsigned int id, PNG_CONST unsigned int od,
+ PNG_CONST double alpha /* <0 for the alpha channel itself */,
+ PNG_CONST double background /* component background value */)
+{
+ PNG_CONST unsigned int isbit = id >> vi->isbit_shift;
+ PNG_CONST unsigned int sbit_max = vi->sbit_max;
+ PNG_CONST unsigned int outmax = vi->outmax;
+ PNG_CONST int do_background = vi->do_background;
+
+ 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)
+ {
+ /* 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.
+ */
+ double tmp = (isbit - .5)/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 + .5)/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_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)
+ {
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ case PNG_BACKGROUND_GAMMA_FILE:
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ use_background = (alpha >= 0 && alpha < 1);
+ /*FALL THROUGH*/
+# 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_structp pp, png_infop pi)
+{
+ /* Get some constants derived from the input and output file formats: */
+ PNG_CONST png_store* PNG_CONST ps = dp->this.ps;
+ PNG_CONST png_byte in_ct = dp->this.colour_type;
+ PNG_CONST png_byte in_bd = dp->this.bit_depth;
+ PNG_CONST png_uint_32 w = dp->this.w;
+ PNG_CONST png_uint_32 h = dp->this.h;
+ PNG_CONST size_t cbRow = dp->this.cbRow;
+ PNG_CONST png_byte out_ct = png_get_color_type(pp, pi);
+ PNG_CONST png_byte out_bd = png_get_bit_depth(pp, pi);
+
+ /* 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 below.
+ */
+ PNG_CONST unsigned int samples_per_pixel = (out_ct & 2U) ? 3U : 1U;
+ int processing;
+ png_uint_32 y;
+ PNG_CONST store_palette_entry *in_palette = dp->this.palette;
+ PNG_CONST int in_is_transparent = dp->this.is_transparent;
+ 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;
+
+ /* 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. */
+ PNG_CONST unsigned int in_index =
+ in_ct == 3 ? sample(std, 3, in_bd, x, 0) : 256;
+ PNG_CONST unsigned int out_index =
+ out_ct == 3 ? sample(std, 3, out_bd, x, 0) : 256;
+
+ /* 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))
+ {
+ PNG_CONST unsigned int input_alpha = in_ct == 3 ?
+ dp->this.palette[in_index].alpha :
+ sample(std, in_ct, in_bd, x, samples_per_pixel);
+
+ 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);
+
+ 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;
+ }
+ }
+
+ /* Handle greyscale or RGB components. */
+ if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* greyscale */
+ (void)gamma_component_validate("gray", &vi,
+ sample(std, in_ct, in_bd, x, 0),
+ sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/,
+ vi.background_red);
+ 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),
+ out_ct == 3 ? out_palette[out_index].red :
+ sample(pRow, out_ct, out_bd, x, 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),
+ out_ct == 3 ? out_palette[out_index].green :
+ sample(pRow, out_ct, out_bd, x, 1),
+ 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),
+ out_ct == 3 ? out_palette[out_index].blue :
+ sample(pRow, out_ct, out_bd, x, 2),
+ 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 %d changed", y);
+
+ png_error(pp, msg);
+ }
+ } /* row (y) loop */
+
+ dp->this.ps->validated = 1;
+}
+
+static void
+gamma_end(png_structp pp, png_infop pi)
+{
+ gamma_display *dp = png_get_progressive_ptr(pp);
+
+ if (!dp->this.speed)
+ gamma_image_validate(dp, pp, pi);
+}
+
+/* A single test run checking a gamma transformation.
+ *
+ * maxabs: maximum absolute error as a fraction
+ * maxout: maximum output error in the output units
+ * maxpc: maximum percentage error (as a percentage)
+ */
+static void
+gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
+ PNG_CONST png_byte bit_depthIn, PNG_CONST int palette_numberIn,
+ PNG_CONST int interlace_typeIn,
+ PNG_CONST double file_gammaIn, PNG_CONST double screen_gammaIn,
+ PNG_CONST png_byte sbitIn, PNG_CONST int threshold_testIn,
+ PNG_CONST char *name,
+ PNG_CONST int use_input_precisionIn, PNG_CONST int scale16In,
+ PNG_CONST int expand16In, PNG_CONST int do_backgroundIn,
+ PNG_CONST png_color_16 *bkgd_colorIn, double bkgd_gammaIn)
+{
+ gamma_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;
+ gamma_modification gamma_mod;
+ srgb_modification srgb_mod;
+ sbit_modification sbit_mod;
+
+ /* 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;
+ gamma_modification_init(&gamma_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 writing 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);
+ }
+
+ 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((png_modifier*)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;
+ 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!
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number))
+ if (palette_number == 0)
+ {
+ double test_gamma = 1.0;
+ while (test_gamma >= .4)
+ {
+ /* There's little point testing the interlacing vs non-interlacing,
+ * but this can be set from the command line.
+ */
+ gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
+ test_gamma, 1/test_gamma);
+ test_gamma *= .95;
+ }
+
+ /* And a special test for sRGB */
+ gamma_threshold_test(pm, colour_type, bit_depth, pm->interlace_type,
+ .45455, 2.2);
+
+ if (fail(pm))
+ return;
+ }
+}
+
+static void gamma_transform_test(png_modifier *pm,
+ PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
+ PNG_CONST int palette_number,
+ PNG_CONST int interlace_type, PNG_CONST double file_gamma,
+ PNG_CONST double screen_gamma, PNG_CONST png_byte sbit,
+ PNG_CONST int use_input_precision, PNG_CONST int scale16)
+{
+ 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;
+ int palette_number = 0;
+
+ while (next_format(&colour_type, &bit_depth, &palette_number))
+ {
+ unsigned int i, j;
+
+ for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++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, bit_depth;
+ int npalette;
+
+ colour_type = bit_depth = 0;
+ npalette = 0;
+
+ while (next_format(&colour_type, &bit_depth, &npalette))
+ 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->ngammas; ++i)
+ {
+ unsigned int j;
+
+ for (j=0; j<pm->ngammas; ++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
+ /* 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->ngammas; ++i)
+ {
+ for (j=0; j<pm->ngammas; ++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], PNG_MAX_GAMMA_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+
+ gamma_transform_test(pm, 2, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+
+ gamma_transform_test(pm, 4, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+
+ gamma_transform_test(pm, 6, 16, 0, pm->interlace_type,
+ 1/pm->gammas[i], pm->gammas[j], PNG_MAX_GAMMA_8,
+ pm->use_input_precision_16to8, 1 /*scale16*/);
+
+ if (fail(pm))
+ return;
+ }
+ }
+ }
+}
+#endif /* 16 to 8 bit conversion */
+
+#if defined PNG_READ_BACKGROUND_SUPPORTED ||\
+ defined PNG_READ_ALPHA_MODE_SUPPORTED
+static void gamma_composition_test(png_modifier *pm,
+ PNG_CONST png_byte colour_type, PNG_CONST png_byte bit_depth,
+ PNG_CONST int palette_number,
+ PNG_CONST int interlace_type, PNG_CONST double file_gamma,
+ PNG_CONST double screen_gamma,
+ PNG_CONST int use_input_precision, PNG_CONST int do_background,
+ PNG_CONST int expand_16)
+{
+ 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);
+ }
+
+ 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))
+ {
+ /* 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;
+ int palette_number = 0;
+
+ /* Skip the non-alpha cases - there is no setting of a transparency colour at
+ * present.
+ */
+ while (next_format(&colour_type, &bit_depth, &palette_number))
+ if ((colour_type & PNG_COLOR_MASK_ALPHA) != 0)
+ {
+ unsigned int i, j;
+
+ /* Don't skip the i==j case here - it's relevant. */
+ for (i=0; i<pm->ngammas; ++i) for (j=0; j<pm->ngammas; ++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)
+{
+ pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0;
+ pm->error_color_8 = 0;
+ pm->error_indexed = 0;
+ pm->error_gray_16 = pm->error_color_16 = 0;
+}
+
+static void
+summarize_gamma_errors(png_modifier *pm, png_const_charp who, int low_bit_depth)
+{
+ if (who)
+ printf("Gamma correction with %s:\n", who);
+
+ if (low_bit_depth)
+ {
+ printf(" 2 bit gray: %.5f\n", pm->error_gray_2);
+ printf(" 4 bit gray: %.5f\n", pm->error_gray_4);
+ printf(" 8 bit gray: %.5f\n", pm->error_gray_8);
+ printf(" 8 bit color: %.5f\n", pm->error_color_8);
+ printf(" indexed: %.5f\n", pm->error_indexed);
+ }
+
+#ifdef DO_16BIT
+ printf(" 16 bit gray: %.5f\n", pm->error_gray_16);
+ printf(" 16 bit color: %.5f\n", pm->error_color_16);
+#endif
+}
+
+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;
+ double maxout8 = pm->maxout8;
+
+ /* 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)
+ {
+ 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)
+ {
+ 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\n");
+
+ summarize_gamma_errors(pm, 0/*who*/, 1);
+ }
+ }
+
+ /* 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);
+ }
+
+#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)
+ {
+ printf("Gamma 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);
+ }
+ }
+#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);
+ }
+#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);
+ }
+#endif
+}
+#endif /* PNG_READ_GAMMA_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 PNG_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(1);
+ }
+
+ 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(1);
+ }
+
+ 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(1);
+ }
+
+ 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(1);
+ }
+
+ /* 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(1);
+ }
+
+ 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(1);
+ }
+
+ 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(1);
+ }
+
+ 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(1);
+ }
+
+ /* 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(1);
+ }
+
+ 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(1);
+ }
+
+ /* 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;
+ }
+ }
+ }
+}
+
+/* main program */
+int main(int argc, PNG_CONST char **argv)
+{
+ volatile int summary = 1; /* Print the error summary at the end */
+ volatile int memstats = 0; /* Print memory statistics at the end */
+
+ /* Create the given output file on success: */
+ PNG_CONST char *volatile 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];
+
+ png_modifier pm;
+ context(&pm.this, fault);
+
+ 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;
+
+ /* Store the test gammas */
+ pm.gammas = gammas;
+ pm.ngammas = 0; /* default to off */
+ pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */
+ /* The following allows results to pass if they correspond to anything in the
+ * transformed range [input-.5,input+.5]; this is is required because of the
+ * way libpng treates the 16_TO_8 flag when building the gamma tables.
+ *
+ * TODO: review this
+ */
+ pm.use_input_precision_16to8 = 1U;
+
+ /* 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 = .004; /* +/-1 in 8 bits for compose errors */
+ pm.maxpc8 = .499; /* I.e., .499% fractional error */
+ pm.maxout16 = .499; /* Error in *encoded* value */
+ pm.maxabs16 = .00005;/* 1/20000 */
+ pm.maxcalc16 =.000015;/* +/-1 in 16 bits for compose errors */
+
+ /* 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)
+ pm.this.treat_warnings_as_errors = 0;
+
+ else if (strcmp(*argv, "--speed") == 0)
+ pm.this.speed = 1, pm.ngammas = (sizeof gammas)/(sizeof gammas[0]),
+ 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.ngammas = 2U;
+ pm.test_gamma_threshold = 1;
+ pm.test_gamma_transform = 1;
+ pm.test_gamma_sbit = 1;
+ pm.test_gamma_scale16 = 1;
+ pm.test_gamma_background = 1;
+ pm.test_gamma_alpha_mode = 1;
+ }
+
+ else if (strcmp(*argv, "--nogamma") == 0)
+ pm.ngammas = 0;
+
+ else if (strcmp(*argv, "--gamma-threshold") == 0)
+ pm.ngammas = 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.ngammas = 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.ngammas = 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.ngammas = 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.ngammas = 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.ngammas = 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, "--more-gammas") == 0)
+ pm.ngammas = 3U;
+
+ else if (strcmp(*argv, "--all-gammas") == 0)
+ pm.ngammas = (sizeof gammas)/(sizeof gammas[0]);
+
+ else if (strcmp(*argv, "--progressive-read") == 0)
+ pm.this.progressive = 1;
+
+ else if (strcmp(*argv, "--interlace") == 0)
+ pm.interlace_type = PNG_INTERLACE_ADAM7;
+
+ else if (strcmp(*argv, "--use-input-precision") == 0)
+ pm.use_input_precision = 1;
+
+ else if (strcmp(*argv, "--calculations-use-input-precision") == 0)
+ pm.calculations_use_input_precision = 1;
+
+ else if (strcmp(*argv, "--assume-16-bit-calculations") == 0)
+ pm.assume_16_bit_calculations = 1;
+
+ else if (strcmp(*argv, "--calculations-follow-bit-depth") == 0)
+ pm.calculations_use_input_precision =
+ pm.assume_16_bit_calculations = 0;
+
+ 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(1);
+ }
+
+ 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;
+
+ else
+ {
+ fprintf(stderr, "pngvalid: %s: unknown argument\n", *argv);
+ exit(1);
+ }
+
+ 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.ngammas == 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.ngammas = 2U;
+ }
+
+ if (pm.ngammas > 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.ngammas == 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.this);
+
+ /* Perform the standard and gamma tests. */
+ if (pm.test_standard)
+ {
+ perform_interlace_macro_validation();
+ perform_formatting_test(&pm.this);
+ perform_standard_test(&pm);
+ perform_error_test(&pm);
+ }
+
+ /* Various oddly sized images: */
+ if (pm.test_size)
+ {
+ make_size_images(&pm.this);
+ perform_size_test(&pm);
+ }
+
+#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.ngammas > 0)
+ perform_gamma_test(&pm, summary);
+#endif
+ }
+
+ Catch(fault)
+ {
+ 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);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/pngwrite.c b/pngwrite.c
index d90f449e3..d1af9e899 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -1,7 +1,7 @@
/* pngwrite.c - general routines to write a PNG file
*
- * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -99,8 +99,10 @@ png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
int keep = png_handle_as_unknown(png_ptr, up->name);
if (keep != PNG_HANDLE_CHUNK_NEVER &&
- up->location && !(up->location & PNG_HAVE_PLTE) &&
+ up->location &&
+ !(up->location & PNG_HAVE_PLTE) &&
!(up->location & PNG_HAVE_IDAT) &&
+ !(up->location & PNG_AFTER_IDAT) &&
((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
(png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
{
@@ -273,8 +275,10 @@ png_write_info(png_structp png_ptr, png_infop info_ptr)
{
int keep = png_handle_as_unknown(png_ptr, up->name);
if (keep != PNG_HANDLE_CHUNK_NEVER &&
- up->location && (up->location & PNG_HAVE_PLTE) &&
+ up->location &&
+ (up->location & PNG_HAVE_PLTE) &&
!(up->location & PNG_HAVE_IDAT) &&
+ !(up->location & PNG_AFTER_IDAT) &&
((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
(png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
{
@@ -380,7 +384,8 @@ png_write_end(png_structp png_ptr, png_infop info_ptr)
{
int keep = png_handle_as_unknown(png_ptr, up->name);
if (keep != PNG_HANDLE_CHUNK_NEVER &&
- up->location && (up->location & PNG_AFTER_IDAT) &&
+ up->location &&
+ (up->location & PNG_AFTER_IDAT) &&
((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
(png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
{
@@ -462,10 +467,9 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
png_structp png_ptr;
#ifdef PNG_SETJMP_SUPPORTED
#ifdef USE_FAR_KEYWORD
- jmp_buf png_jmpbuf;
+ jmp_buf tmp_jmpbuf;
#endif
#endif
- int i;
png_debug(1, "in png_create_write_struct");
@@ -489,12 +493,12 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
encounter a png_error() will longjmp here. Since the jmpbuf is
then meaningless we abort instead of returning. */
#ifdef USE_FAR_KEYWORD
- if (setjmp(png_jmpbuf))
+ if (setjmp(tmp_jmpbuf))
#else
if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */
#endif
#ifdef USE_FAR_KEYWORD
- png_memcpy(png_jmpbuf(png_ptr), png_jmpbuf, png_sizeof(jmp_buf));
+ png_memcpy(png_jmpbuf(png_ptr), tmp_jmpbuf, png_sizeof(jmp_buf));
#endif
PNG_ABORT();
#endif
@@ -504,49 +508,8 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
#endif /* PNG_USER_MEM_SUPPORTED */
png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
- if (user_png_ver)
- {
- i = 0;
- do
- {
- if (user_png_ver[i] != png_libpng_ver[i])
- png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
- } while (png_libpng_ver[i++]);
- }
-
- if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
- {
- /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
- * we must recompile any applications that use any older library version.
- * For versions after libpng 1.0, we will be compatible, so we need
- * only check the first digit.
- */
- if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
- (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
- (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
- {
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- char msg[80];
-
- if (user_png_ver)
- {
- png_snprintf2(msg, 80,
- "Application built with libpng-%.20s"
- " but running with %.20s",
- user_png_ver,
- png_libpng_ver);
- png_warning(png_ptr, msg);
- }
-#else
- png_warning(png_ptr,
- "Incompatible libpng version in application and library");
-#endif
-#ifdef PNG_ERROR_NUMBERS_SUPPORTED
- png_ptr->flags = 0;
-#endif
- png_cleanup_needed = 1;
- }
- }
+ if (!png_user_version_check(png_ptr, user_png_ver))
+ png_cleanup_needed = 1;
/* Initialize zbuf - compression buffer */
png_ptr->zbuf_size = PNG_ZBUF_SIZE;
@@ -805,9 +768,11 @@ png_write_row(png_structp png_ptr, png_const_bytep row)
}
#endif
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
/* Handle other transformations */
if (png_ptr->transformations)
png_do_write_transformations(png_ptr);
+#endif
#ifdef PNG_MNG_FEATURES_SUPPORTED
/* Write filter_method 64 (intrapixel differencing) only if
@@ -884,8 +849,6 @@ png_write_flush(png_structp png_ptr)
{
/* Write the IDAT and reset the zlib output buffer */
png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
wrote_IDAT = 1;
}
} while (wrote_IDAT == 1);
@@ -896,8 +859,6 @@ png_write_flush(png_structp png_ptr)
/* Write the IDAT and reset the zlib output buffer */
png_write_IDAT(png_ptr, png_ptr->zbuf,
png_ptr->zbuf_size - png_ptr->zstream.avail_out);
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
}
png_ptr->flush_rows = 0;
png_flush(png_ptr);
@@ -983,7 +944,9 @@ png_write_destroy(png_structp png_ptr)
jmp_buf tmp_jmp; /* Save jump buffer */
#endif
png_error_ptr error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
png_error_ptr warning_fn;
+#endif
png_voidp error_ptr;
#ifdef PNG_USER_MEM_SUPPORTED
png_free_ptr free_fn;
@@ -992,7 +955,8 @@ png_write_destroy(png_structp png_ptr)
png_debug(1, "in png_write_destroy");
/* Free any memory zlib uses */
- deflateEnd(&png_ptr->zstream);
+ if (png_ptr->zlib_state != PNG_ZLIB_UNINITIALIZED)
+ deflateEnd(&png_ptr->zstream);
/* Free our memory. png_free checks NULL for us. */
png_free(png_ptr, png_ptr->zbuf);
@@ -1005,10 +969,6 @@ png_write_destroy(png_structp png_ptr)
png_free(png_ptr, png_ptr->paeth_row);
#endif
-#ifdef PNG_TIME_RFC1123_SUPPORTED
- png_free(png_ptr, png_ptr->time_buffer);
-#endif
-
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
/* Use this to save a little code space, it doesn't free the filter_costs */
png_reset_filter_heuristics(png_ptr);
@@ -1018,11 +978,13 @@ png_write_destroy(png_structp png_ptr)
#ifdef PNG_SETJMP_SUPPORTED
/* Reset structure */
- png_memcpy(tmp_jmp, png_ptr->png_jmpbuf, png_sizeof(jmp_buf));
+ png_memcpy(tmp_jmp, png_ptr->longjmp_buffer, png_sizeof(jmp_buf));
#endif
error_fn = png_ptr->error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
warning_fn = png_ptr->warning_fn;
+#endif
error_ptr = png_ptr->error_ptr;
#ifdef PNG_USER_MEM_SUPPORTED
free_fn = png_ptr->free_fn;
@@ -1031,14 +993,16 @@ png_write_destroy(png_structp png_ptr)
png_memset(png_ptr, 0, png_sizeof(png_struct));
png_ptr->error_fn = error_fn;
+#ifdef PNG_WARNINGS_SUPPORTED
png_ptr->warning_fn = warning_fn;
+#endif
png_ptr->error_ptr = error_ptr;
#ifdef PNG_USER_MEM_SUPPORTED
png_ptr->free_fn = free_fn;
#endif
#ifdef PNG_SETJMP_SUPPORTED
- png_memcpy(png_ptr->png_jmpbuf, tmp_jmp, png_sizeof(jmp_buf));
+ png_memcpy(png_ptr->longjmp_buffer, tmp_jmp, png_sizeof(jmp_buf));
#endif
}
@@ -1177,7 +1141,7 @@ png_set_filter(png_structp png_ptr, int method, int filters)
* better compression.
*/
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */
-/* Conveneince reset API. */
+/* Convenience reset API. */
static void
png_reset_filter_heuristics(png_structp png_ptr)
{
@@ -1451,6 +1415,9 @@ png_set_compression_strategy(png_structp png_ptr, int strategy)
png_ptr->zlib_strategy = strategy;
}
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
void PNGAPI
png_set_compression_window_bits(png_structp png_ptr, int window_bits)
{
@@ -1491,6 +1458,89 @@ png_set_compression_method(png_structp png_ptr, int method)
png_ptr->zlib_method = method;
}
+/* The following were added to libpng-1.5.4 */
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+void PNGAPI
+png_set_text_compression_level(png_structp png_ptr, int level)
+{
+ png_debug(1, "in png_set_text_compression_level");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_LEVEL;
+ png_ptr->zlib_text_level = level;
+}
+
+void PNGAPI
+png_set_text_compression_mem_level(png_structp png_ptr, int mem_level)
+{
+ png_debug(1, "in png_set_text_compression_mem_level");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL;
+ png_ptr->zlib_text_mem_level = mem_level;
+}
+
+void PNGAPI
+png_set_text_compression_strategy(png_structp png_ptr, int strategy)
+{
+ png_debug(1, "in png_set_text_compression_strategy");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_STRATEGY;
+ png_ptr->zlib_text_strategy = strategy;
+}
+
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
+void PNGAPI
+png_set_text_compression_window_bits(png_structp png_ptr, int window_bits)
+{
+ if (png_ptr == NULL)
+ return;
+
+ if (window_bits > 15)
+ png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+
+ else if (window_bits < 8)
+ png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
+
+#ifndef WBITS_8_OK
+ /* Avoid libpng bug with 256-byte windows */
+ if (window_bits == 8)
+ {
+ png_warning(png_ptr, "Text compression window is being reset to 512");
+ window_bits = 9;
+ }
+
+#endif
+ png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS;
+ png_ptr->zlib_text_window_bits = window_bits;
+}
+
+void PNGAPI
+png_set_text_compression_method(png_structp png_ptr, int method)
+{
+ png_debug(1, "in png_set_text_compression_method");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (method != 8)
+ png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+
+ png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_METHOD;
+ png_ptr->zlib_text_method = method;
+}
+#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
+/* end of API added to libpng-1.5.4 */
+
void PNGAPI
png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
{
@@ -1557,7 +1607,7 @@ png_write_png(png_structp png_ptr, png_infop info_ptr,
#endif
#ifdef PNG_WRITE_FILLER_SUPPORTED
- /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */
+ /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */
if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
diff --git a/pngwtran.c b/pngwtran.c
index 15888b682..124d708c4 100644
--- a/pngwtran.c
+++ b/pngwtran.c
@@ -1,7 +1,7 @@
/* pngwtran.c - transforms the data in a row for PNG writers
*
- * Last changed in libpng 1.5.2 [(PENDING RELEASE)]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -15,6 +15,7 @@
#ifdef PNG_WRITE_SUPPORTED
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
/* Transform the data according to the user's wishes. The order of
* transformations is significant.
*/
@@ -45,7 +46,7 @@ png_do_write_transformations(png_structp png_ptr)
#ifdef PNG_WRITE_FILLER_SUPPORTED
if (png_ptr->transformations & PNG_FILLER)
png_do_strip_channel(&(png_ptr->row_info), png_ptr->row_buf + 1,
- !(png_ptr->flags & PNG_FILLER_AFTER));
+ !(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
#endif
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
@@ -563,6 +564,7 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
}
}
#endif
+#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */
#ifdef PNG_MNG_FEATURES_SUPPORTED
/* Undoes intrapixel differencing */
diff --git a/pngwutil.c b/pngwutil.c
index 2c35785aa..3ae5ed4e2 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -1,7 +1,7 @@
/* pngwutil.c - utilities to write a PNG file
*
- * Last changed in libpng 1.5.0 [January 6, 2011]
+ * Last changed in libpng 1.5.4 [July 7, 2011]
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -192,7 +192,149 @@ png_write_chunk_end(png_structp png_ptr)
png_write_data(png_ptr, buf, (png_size_t)4);
}
-#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED)
+/* Initialize the compressor for the appropriate type of compression. */
+static void
+png_zlib_claim(png_structp png_ptr, png_uint_32 state)
+{
+ if (!(png_ptr->zlib_state & PNG_ZLIB_IN_USE))
+ {
+ /* If already initialized for 'state' do not re-init. */
+ if (png_ptr->zlib_state != state)
+ {
+ int ret = Z_OK;
+ png_const_charp who = "-";
+
+ /* If actually initialized for another state do a deflateEnd. */
+ if (png_ptr->zlib_state != PNG_ZLIB_UNINITIALIZED)
+ {
+ ret = deflateEnd(&png_ptr->zstream);
+ who = "end";
+ png_ptr->zlib_state = PNG_ZLIB_UNINITIALIZED;
+ }
+
+ /* zlib itself detects an incomplete state on deflateEnd */
+ if (ret == Z_OK) switch (state)
+ {
+# ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+ case PNG_ZLIB_FOR_TEXT:
+ ret = deflateInit2(&png_ptr->zstream,
+ png_ptr->zlib_text_level, png_ptr->zlib_text_method,
+ png_ptr->zlib_text_window_bits,
+ png_ptr->zlib_text_mem_level, png_ptr->zlib_text_strategy);
+ who = "text";
+ break;
+# endif
+
+ case PNG_ZLIB_FOR_IDAT:
+ ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
+ png_ptr->zlib_method, png_ptr->zlib_window_bits,
+ png_ptr->zlib_mem_level, png_ptr->zlib_strategy);
+ who = "IDAT";
+ break;
+
+ default:
+ png_error(png_ptr, "invalid zlib state");
+ }
+
+ if (ret == Z_OK)
+ png_ptr->zlib_state = state;
+
+ else /* an error in deflateEnd or deflateInit2 */
+ {
+ size_t pos = 0;
+ char msg[64];
+
+ pos = png_safecat(msg, sizeof msg, pos,
+ "zlib failed to initialize compressor (");
+ pos = png_safecat(msg, sizeof msg, pos, who);
+
+ switch (ret)
+ {
+ case Z_VERSION_ERROR:
+ pos = png_safecat(msg, sizeof msg, pos, ") version error");
+ break;
+
+ case Z_STREAM_ERROR:
+ pos = png_safecat(msg, sizeof msg, pos, ") stream error");
+ break;
+
+ case Z_MEM_ERROR:
+ pos = png_safecat(msg, sizeof msg, pos, ") memory error");
+ break;
+
+ default:
+ pos = png_safecat(msg, sizeof msg, pos, ") unknown error");
+ break;
+ }
+
+ png_error(png_ptr, msg);
+ }
+ }
+
+ /* Here on success, claim the zstream: */
+ png_ptr->zlib_state |= PNG_ZLIB_IN_USE;
+ }
+
+ else
+ png_error(png_ptr, "zstream already in use (internal error)");
+}
+
+/* The opposite: release the stream. It is also reset, this API will warn on
+ * error but will not fail.
+ */
+static void
+png_zlib_release(png_structp png_ptr)
+{
+ if (png_ptr->zlib_state & PNG_ZLIB_IN_USE)
+ {
+ int ret = deflateReset(&png_ptr->zstream);
+
+ png_ptr->zlib_state &= ~PNG_ZLIB_IN_USE;
+
+ if (ret != Z_OK)
+ {
+ png_const_charp err;
+ PNG_WARNING_PARAMETERS(p)
+
+ switch (ret)
+ {
+ case Z_VERSION_ERROR:
+ err = "version";
+ break;
+
+ case Z_STREAM_ERROR:
+ err = "stream";
+ break;
+
+ case Z_MEM_ERROR:
+ err = "memory";
+ break;
+
+ default:
+ err = "unknown";
+ break;
+ }
+
+ png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d, ret);
+ png_warning_parameter(p, 2, err);
+
+ if (png_ptr->zstream.msg)
+ err = png_ptr->zstream.msg;
+ else
+ err = "[no zlib message]";
+
+ png_warning_parameter(p, 3, err);
+
+ png_formatted_warning(png_ptr, p,
+ "zlib failed to reset compressor: @1(@2): @3");
+ }
+ }
+
+ else
+ png_warning(png_ptr, "zstream not in use (internal error)");
+}
+
+#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
/* This pair of functions encapsulates the operation of (a) compressing a
* text string, and (b) issuing it later as a series of chunk data writes.
* The compression_state structure is shared context for these functions
@@ -220,25 +362,22 @@ png_text_compress(png_structp png_ptr,
comp->max_output_ptr = 0;
comp->output_ptr = NULL;
comp->input = NULL;
- comp->input_len = 0;
+ comp->input_len = text_len;
/* We may just want to pass the text right through */
if (compression == PNG_TEXT_COMPRESSION_NONE)
{
comp->input = (png_const_bytep)text;
- comp->input_len = text_len;
return((int)text_len);
}
if (compression >= PNG_TEXT_COMPRESSION_LAST)
{
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- char msg[50];
- png_snprintf(msg, 50, "Unknown compression type %d", compression);
- png_warning(png_ptr, msg);
-#else
- png_warning(png_ptr, "Unknown compression type");
-#endif
+ PNG_WARNING_PARAMETERS(p)
+
+ png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_d,
+ compression);
+ png_formatted_warning(png_ptr, p, "Unknown compression type @1");
}
/* We can't write the chunk until we find out how much data we have,
@@ -255,10 +394,12 @@ png_text_compress(png_structp png_ptr,
* data, or if the input string is incredibly large (although this
* wouldn't cause a failure, just a slowdown due to swapping).
*/
+ png_zlib_claim(png_ptr, PNG_ZLIB_FOR_TEXT);
/* Set up the compression buffers */
/* TODO: the following cast hides a potential overflow problem. */
png_ptr->zstream.avail_in = (uInt)text_len;
+
/* NOTE: assume zlib doesn't overwrite the input */
png_ptr->zstream.next_in = (Bytef *)text;
png_ptr->zstream.avail_out = png_ptr->zbuf_size;
@@ -418,6 +559,68 @@ png_write_compressed_data_out(png_structp png_ptr, compression_state *comp)
return;
}
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+ if (comp->input_len >= 2 && comp->input_len < 16384)
+ {
+ unsigned int z_cmf; /* zlib compression method and flags */
+
+ /* Optimize the CMF field in the zlib stream. This hack of the zlib
+ * stream is compliant to the stream specification.
+ */
+
+ if (comp->num_output_ptr)
+ z_cmf = comp->output_ptr[0][0];
+ else
+ z_cmf = png_ptr->zbuf[0];
+
+ if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+ {
+ unsigned int z_cinfo;
+ unsigned int half_z_window_size;
+ png_size_t uncompressed_text_size = comp->input_len;
+
+ z_cinfo = z_cmf >> 4;
+ half_z_window_size = 1 << (z_cinfo + 7);
+
+ while (uncompressed_text_size <= half_z_window_size &&
+ half_z_window_size >= 256)
+ {
+ z_cinfo--;
+ half_z_window_size >>= 1;
+ }
+
+ z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+
+ if (comp->num_output_ptr)
+ {
+
+ if (comp->output_ptr[0][0] != z_cmf)
+ {
+ int tmp;
+
+ comp->output_ptr[0][0] = (png_byte)z_cmf;
+ tmp = comp->output_ptr[0][1] & 0xe0;
+ tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
+ comp->output_ptr[0][1] = (png_byte)tmp;
+ }
+ }
+ else
+ {
+ int tmp;
+
+ png_ptr->zbuf[0] = (png_byte)z_cmf;
+ tmp = png_ptr->zbuf[1] & 0xe0;
+ tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
+ png_ptr->zbuf[1] = (png_byte)tmp;
+ }
+ }
+
+ else
+ png_error(png_ptr,
+ "Invalid zlib compression method or flags in non-IDAT chunk");
+ }
+#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
+
/* Write saved output buffers, if any */
for (i = 0; i < comp->num_output_ptr; i++)
{
@@ -436,10 +639,9 @@ png_write_compressed_data_out(png_structp png_ptr, compression_state *comp)
(png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out));
/* Reset zlib for another zTXt/iTXt or image data */
- deflateReset(&png_ptr->zstream);
- png_ptr->zstream.data_type = Z_BINARY;
+ png_zlib_release(png_ptr);
}
-#endif
+#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
/* Write the IHDR chunk, and update the png_struct with the necessary
* information. Note that the rest of this code depends upon this
@@ -451,7 +653,6 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
int interlace_type)
{
PNG_IHDR;
- int ret;
png_byte buf[13]; /* Buffer to store the IHDR info */
@@ -632,35 +833,35 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD))
png_ptr->zlib_method = 8;
- ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
- png_ptr->zlib_method, png_ptr->zlib_window_bits,
- png_ptr->zlib_mem_level, png_ptr->zlib_strategy);
-
- if (ret != Z_OK)
- {
- if (ret == Z_VERSION_ERROR)
- png_error(png_ptr,
- "zlib failed to initialize compressor -- version error");
-
- if (ret == Z_STREAM_ERROR)
- png_error(png_ptr,
- "zlib failed to initialize compressor -- stream error");
+#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+ if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_STRATEGY))
+ png_ptr->zlib_text_strategy = Z_DEFAULT_STRATEGY;
- if (ret == Z_MEM_ERROR)
- png_error(png_ptr,
- "zlib failed to initialize compressor -- mem error");
+ if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_LEVEL))
+ png_ptr->zlib_text_level = png_ptr->zlib_level;
- png_error(png_ptr, "zlib failed to initialize compressor");
- }
+ if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL))
+ png_ptr->zlib_text_mem_level = png_ptr->zlib_mem_level;
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
- /* libpng is not interested in zstream.data_type, so set it
- * to a predefined value, to avoid its evaluation inside zlib
- */
- png_ptr->zstream.data_type = Z_BINARY;
+ if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS))
+ png_ptr->zlib_text_window_bits = png_ptr->zlib_window_bits;
- png_ptr->mode = PNG_HAVE_IHDR;
+ if (!(png_ptr->flags & PNG_FLAG_ZTXT_CUSTOM_METHOD))
+ png_ptr->zlib_text_method = png_ptr->zlib_method;
+#else
+ png_ptr->zlib_text_strategy = Z_DEFAULT_STRATEGY;
+ png_ptr->zlib_text_level = png_ptr->zlib_level;
+ png_ptr->zlib_text_mem_level = png_ptr->zlib_mem_level;
+ png_ptr->zlib_text_window_bits = png_ptr->zlib_window_bits;
+ png_ptr->zlib_text_method = png_ptr->zlib_method;
+#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
+#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
+
+ /* Record that the compressor has not yet been initialized. */
+ png_ptr->zlib_state = PNG_ZLIB_UNINITIALIZED;
+
+ png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */
}
/* Write the palette. We are careful not to trust png_color to be in the
@@ -745,12 +946,15 @@ png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
png_debug(1, "in png_write_IDAT");
- /* Optimize the CMF field in the zlib stream. */
- /* This hack of the zlib stream is compliant to the stream specification. */
+#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
{
+ /* Optimize the CMF field in the zlib stream. This hack of the zlib
+ * stream is compliant to the stream specification.
+ */
unsigned int z_cmf = data[0]; /* zlib compression method and flags */
+
if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
{
/* Avoid memory underflows and multiplication overflows.
@@ -761,11 +965,29 @@ png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
if (length >= 2 &&
png_ptr->height < 16384 && png_ptr->width < 16384)
{
+ /* Compute the maximum possible length of the datastream */
+
+ /* Number of pixels, plus for each row a filter byte
+ * and possibly a padding byte, so increase the maximum
+ * size to account for these.
+ */
+ unsigned int z_cinfo;
+ unsigned int half_z_window_size;
png_uint_32 uncompressed_idat_size = png_ptr->height *
((png_ptr->width *
png_ptr->channels * png_ptr->bit_depth + 15) >> 3);
- unsigned int z_cinfo = z_cmf >> 4;
- unsigned int half_z_window_size = 1 << (z_cinfo + 7);
+
+ /* If it's interlaced, each block of 8 rows is sent as up to
+ * 14 rows, i.e., 6 additional rows, each with a filter byte
+ * and possibly a padding byte
+ */
+ if (png_ptr->interlaced)
+ uncompressed_idat_size += ((png_ptr->height + 7)/8) *
+ (png_ptr->bit_depth < 8 ? 12 : 6);
+
+ z_cinfo = z_cmf >> 4;
+ half_z_window_size = 1 << (z_cinfo + 7);
+
while (uncompressed_idat_size <= half_z_window_size &&
half_z_window_size >= 256)
{
@@ -790,9 +1012,17 @@ png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
png_error(png_ptr,
"Invalid zlib compression method or flags in IDAT");
}
+#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
png_write_chunk(png_ptr, png_IDAT, data, length);
png_ptr->mode |= PNG_HAVE_IDAT;
+
+ /* Prior to 1.5.4 this code was replicated in every caller (except at the
+ * end, where it isn't technically necessary). Since this function has
+ * flushed the data we can safely reset the zlib output buffer here.
+ */
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
}
/* Write an IEND chunk */
@@ -918,7 +1148,10 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type,
(png_size_t)(name_len + 2));
if (profile_len)
+ {
+ comp.input_len = profile_len;
png_write_compressed_data_out(png_ptr, &comp);
+ }
png_write_chunk_end(png_ptr);
png_free(png_ptr, new_name);
@@ -1313,15 +1546,11 @@ png_check_keyword(png_structp png_ptr, png_const_charp key, png_charpp new_key)
if ((png_byte)*ikp < 0x20 ||
((png_byte)*ikp > 0x7E && (png_byte)*ikp < 0xA1))
{
-#ifdef PNG_CONSOLE_IO_SUPPORTED
- char msg[40];
+ PNG_WARNING_PARAMETERS(p)
- png_snprintf(msg, 40,
- "invalid keyword character 0x%02X", (png_byte)*ikp);
- png_warning(png_ptr, msg);
-#else
- png_warning(png_ptr, "invalid character in keyword");
-#endif
+ png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_02x,
+ (png_byte)*ikp);
+ png_formatted_warning(png_ptr, p, "invalid keyword character 0x@1");
*dp = ' ';
}
@@ -1499,6 +1728,7 @@ png_write_zTXt(png_structp png_ptr, png_const_charp key, png_const_charp text,
png_write_chunk_data(png_ptr, &buf, (png_size_t)1);
/* Write the compressed data */
+ comp.input_len = text_len;
png_write_compressed_data_out(png_ptr, &comp);
/* Close the chunk */
@@ -1871,6 +2101,7 @@ png_write_start_row(png_structp png_ptr)
png_ptr->usr_width = png_ptr->width;
}
+ png_zlib_claim(png_ptr, PNG_ZLIB_FOR_IDAT);
png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
png_ptr->zstream.next_out = png_ptr->zbuf;
}
@@ -1992,7 +2223,7 @@ png_write_finish_row(png_structp png_ptr)
png_ptr->zstream.avail_out);
}
- deflateReset(&png_ptr->zstream);
+ png_zlib_release(png_ptr);
png_ptr->zstream.data_type = Z_BINARY;
}
@@ -2181,6 +2412,8 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
* been specified by the application, and then writes the row out with the
* chosen filter.
*/
+static void png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row);
+
#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
#define PNG_HISHIFT 10
#define PNG_LOMASK ((png_uint_32)0xffffL)
@@ -2856,7 +3089,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info)
/* Do the actual writing of a previously filtered row. */
-void /* PRIVATE */
+static void
png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
{
png_size_t avail;
@@ -2916,8 +3149,6 @@ png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
{
/* Write the IDAT and reset the zlib output buffer */
png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
- png_ptr->zstream.next_out = png_ptr->zbuf;
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
}
/* Repeat until all data has been compressed */
} while (avail > 0 || png_ptr->zstream.avail_in > 0);