diff options
author | Glenn Randers-Pehrson <glennrp at users.sourceforge.net> | 2011-07-07 12:54:39 -0500 |
---|---|---|
committer | Glenn Randers-Pehrson <glennrp at users.sourceforge.net> | 2011-07-11 15:38:54 -0500 |
commit | 3db6997c9223ac257a7cd63f6d1ea7fc66a95d82 (patch) | |
tree | cf8017b2f9f62f989d3eb39e3b47b7f9ebdd905f | |
parent | 4238975d9e00ece88d5fcb5c43df7921f9130260 (diff) | |
download | libpng-3db6997c9223ac257a7cd63f6d1ea7fc66a95d82.tar.gz |
Imported from pngcrush-1.7.16.tarv1.7.16
-rw-r--r-- | ChangeLog.html | 4 | ||||
-rw-r--r-- | Makefile-nolib | 85 | ||||
-rw-r--r-- | png.c | 350 | ||||
-rw-r--r-- | png.h | 395 | ||||
-rw-r--r-- | pngconf.h | 61 | ||||
-rw-r--r-- | pngcrush.c | 47 | ||||
-rw-r--r-- | pngdebug.h | 157 | ||||
-rw-r--r-- | pngerror.c | 255 | ||||
-rw-r--r-- | pnginfo.h | 269 | ||||
-rw-r--r-- | pnglibconf.h | 187 | ||||
-rw-r--r-- | pngmem.c | 23 | ||||
-rw-r--r-- | pngpread.c | 10 | ||||
-rw-r--r-- | pngpriv.h | 272 | ||||
-rw-r--r-- | pngread.c | 107 | ||||
-rw-r--r-- | pngrtran.c | 1692 | ||||
-rw-r--r-- | pngrutil.c | 228 | ||||
-rw-r--r-- | pngset.c | 29 | ||||
-rw-r--r-- | pngstruct.h | 337 | ||||
-rw-r--r-- | pngtest.c | 24 | ||||
-rw-r--r-- | pngtrans.c | 16 | ||||
-rw-r--r-- | pngvalid.c | 8619 | ||||
-rw-r--r-- | pngwrite.c | 178 | ||||
-rw-r--r-- | pngwtran.c | 6 | ||||
-rw-r--r-- | pngwutil.c | 343 |
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) + @@ -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 */ @@ -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 @@ -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 */ @@ -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 @@ -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)); @@ -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(>est, 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(>est, 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) { @@ -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 */ @@ -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); |