diff options
-rw-r--r-- | ChangeLog.html | 7 | ||||
-rw-r--r-- | blacken.c | 57 | ||||
-rw-r--r-- | png.c | 415 | ||||
-rw-r--r-- | png.h | 63 | ||||
-rw-r--r-- | pngconf.h | 80 | ||||
-rw-r--r-- | pngcrush.c | 16 | ||||
-rw-r--r-- | pngcrush.h | 7 | ||||
-rw-r--r-- | pngget.c | 99 | ||||
-rw-r--r-- | pnglibconf.h | 4 | ||||
-rw-r--r-- | pngpriv.h | 155 | ||||
-rw-r--r-- | pngrtran.c | 404 | ||||
-rw-r--r-- | pngrutil.c | 148 | ||||
-rw-r--r-- | pngset.c | 70 | ||||
-rw-r--r-- | pngstruct.h | 11 | ||||
-rw-r--r-- | pngtest.c | 2 | ||||
-rw-r--r-- | pngvalid.c | 1546 | ||||
-rw-r--r-- | pngwutil.c | 14 |
17 files changed, 2401 insertions, 697 deletions
diff --git a/ChangeLog.html b/ChangeLog.html index 0a4038551..246e3ea22 100644 --- a/ChangeLog.html +++ b/ChangeLog.html @@ -3,6 +3,12 @@ Change log: +Version 1.7.17 (built with libpng-1.5.5beta08 and zlib-1.2.5) + Changed "#if !defined(PNG_NO_STDIO)" to "#ifdef PNG_STDIO_SUPPORTED" + as recommended in the libpng documentation. + Added PNG_UINT_32_NAME macro and used it to simplify chunk_type integer + definitions. + 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. @@ -18,6 +24,7 @@ Version 1.7.14 (built with libpng-1.5.1beta08 and zlib-1.2.5) png_get_iCCP() and png_set_iCCP() calls to be able to build with bundled libpng-1.5.x. Pngcrush cannot be built yet with a system libpng-1.5.x. + Dropped most of pngcrush.h, that eliminates various parts of libpng. Version 1.7.13 (built with libpng-1.4.5 and zlib-1.2.5) diff --git a/blacken.c b/blacken.c new file mode 100644 index 000000000..5f8396099 --- /dev/null +++ b/blacken.c @@ -0,0 +1,57 @@ +void PNGCBAPI +pngcrush_blacken_transparent(png_structp png_ptr, png_row_infop row_info, + png_bytep data); +void PNGCBAPI +pngcrush_blacken_transparent(png_structp png_ptr, png_row_infop row_info, + png_bytep data) +{ + /* Changes underlying color of GA or RGBA fully transparent pixels to black */ + + png_bytep dp = data; + if (png_ptr == NULL) + return; + + if (row_info->color_type < 4) + return; + + /* Contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if (row_info->color_type > 3)color_channels--; + + for (n = 0, nstop=row_info->width; n<nstop; n++) + { + for (channel = 0; channel < color_channels; channel++) + { + if (row_info->bit_depth == 8) + if (*dp++ == 0) + zero_samples++; + + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; + + dp+=2; + } + } + if (row_info->color_type > 3) + { + dp++; + if (row_info->bit_depth == 16) + dp++; + } + } + } +} +#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ @@ -1,7 +1,7 @@ /* png.c - location for general purpose libpng functions * - * Last changed in libpng 1.5.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * 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_4 Your_png_h_is_not_version_1_5_4; +typedef png_libpng_version_1_5_5beta08 Your_png_h_is_not_version_1_5_5beta08; /* 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 @@ -43,7 +43,7 @@ png_set_sig_bytes(png_structp png_ptr, int num_bytes) * can simply check the remaining bytes for extra assurance. Returns * an integer less than, equal to, or greater than zero if sig is found, * respectively, to be less than, to match, or be greater than the correct - * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + * PNG signature (this is the same behavior as strcmp, memcmp, etc). */ int PNGAPI png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) @@ -107,7 +107,8 @@ png_zfree(voidpf png_ptr, voidpf ptr) void /* PRIVATE */ png_reset_crc(png_structp png_ptr) { - png_ptr->crc = crc32(0, Z_NULL, 0); + /* The cast is safe because the crc is a 32 bit value. */ + png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0); } /* Calculate the CRC over a section of data. We can only pass as @@ -133,8 +134,35 @@ png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length) need_crc = 0; } - if (need_crc) - png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); + /* 'uLong' is defined as unsigned long, this means that on some systems it is + * a 64 bit value. crc32, however, returns 32 bits so the following cast is + * safe. 'uInt' may be no more than 16 bits, so it is necessary to perform a + * loop here. + */ + if (need_crc && length > 0) + { + uLong crc = png_ptr->crc; /* Should never issue a warning */ + + do + { + uInt safeLength = (uInt)length; + if (safeLength == 0) + safeLength = (uInt)-1; /* evil, but safe */ + + crc = crc32(crc, ptr, safeLength); + + /* The following should never issue compiler warnings, if they do the + * target system has characteristics that will probably violate other + * assumptions within the libpng code. + */ + ptr += safeLength; + length -= safeLength; + } + while (length > 0); + + /* And the following is always safe because the crc is only 32 bits. */ + png_ptr->crc = (png_uint_32)crc; + } } /* Check a user supplied version number, called from both read and write @@ -542,8 +570,8 @@ png_get_io_ptr(png_structp png_ptr) /* Initialize the default input/output functions for the PNG file. If you * use your own read or write routines, you can call either png_set_read_fn() * or png_set_write_fn() instead of png_init_io(). If you have defined - * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't - * necessarily available. + * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a + * function of your own because "FILE *" isn't necessarily available. */ void PNGAPI png_init_io(png_structp png_ptr, png_FILE_p fp) @@ -617,13 +645,13 @@ png_get_copyright(png_const_structp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.5.4 - July 7, 2011" PNG_STRING_NEWLINE \ + "libpng version 1.5.5beta08 - September 10, 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.4 - July 7, 2011\ + return "libpng version 1.5.5beta08 - September 10, 2011\ Copyright (c) 1998-2011 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; @@ -713,18 +741,9 @@ png_access_version_number(void) #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -# ifdef PNG_SIZE_T -/* Added at libpng version 1.2.6 */ - PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); -png_size_t PNGAPI -png_convert_size(size_t size) -{ - if (size > (png_size_t)-1) - PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ - - return ((png_size_t)size); -} -# endif /* PNG_SIZE_T */ +/* png_convert_size: a PNGAPI but no longer in png.h, so deleted + * at libpng 1.5.5! + */ /* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ # ifdef PNG_CHECK_cHRM_SUPPORTED @@ -798,6 +817,326 @@ png_check_cHRM_fixed(png_structp png_ptr, } # endif /* PNG_CHECK_cHRM_SUPPORTED */ +#ifdef PNG_cHRM_SUPPORTED +/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for + * cHRM, as opposed to using chromaticities. These internal APIs return + * non-zero on a parameter error. The X, Y and Z values are required to be + * positive and less than 1.0. + */ +int png_xy_from_XYZ(png_xy *xy, png_XYZ XYZ) +{ + png_int_32 d, dwhite, whiteX, whiteY; + + d = XYZ.redX + XYZ.redY + XYZ.redZ; + if (!png_muldiv(&xy->redx, XYZ.redX, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->redy, XYZ.redY, PNG_FP_1, d)) return 1; + dwhite = d; + whiteX = XYZ.redX; + whiteY = XYZ.redY; + + d = XYZ.greenX + XYZ.greenY + XYZ.greenZ; + if (!png_muldiv(&xy->greenx, XYZ.greenX, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->greeny, XYZ.greenY, PNG_FP_1, d)) return 1; + dwhite += d; + whiteX += XYZ.greenX; + whiteY += XYZ.greenY; + + d = XYZ.blueX + XYZ.blueY + XYZ.blueZ; + if (!png_muldiv(&xy->bluex, XYZ.blueX, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->bluey, XYZ.blueY, PNG_FP_1, d)) return 1; + dwhite += d; + whiteX += XYZ.blueX; + whiteY += XYZ.blueY; + + /* The reference white is simply the same of the end-point (X,Y,Z) vectors, + * thus: + */ + if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1; + if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1; + + return 0; +} + +int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy) +{ + png_fixed_point red_inverse, green_inverse, blue_scale; + png_fixed_point left, right, denominator; + + /* Check xy and, implicitly, z. Note that wide gamut color spaces typically + * have end points with 0 tristimulus values (these are impossible end + * points, but they are used to cover the possible colors.) + */ + if (xy.redx < 0 || xy.redx > PNG_FP_1) return 1; + if (xy.redy < 0 || xy.redy > PNG_FP_1-xy.redx) return 1; + if (xy.greenx < 0 || xy.greenx > PNG_FP_1) return 1; + if (xy.greeny < 0 || xy.greeny > PNG_FP_1-xy.greenx) return 1; + if (xy.bluex < 0 || xy.bluex > PNG_FP_1) return 1; + if (xy.bluey < 0 || xy.bluey > PNG_FP_1-xy.bluex) return 1; + if (xy.whitex < 0 || xy.whitex > PNG_FP_1) return 1; + if (xy.whitey < 0 || xy.whitey > PNG_FP_1-xy.whitex) return 1; + + /* The reverse calculation is more difficult because the original tristimulus + * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8 + * derived values were recorded in the cHRM chunk; + * (red,green,blue,white)x(x,y). This loses one degree of freedom and + * therefore an arbitrary ninth value has to be introduced to undo the + * original transformations. + * + * Think of the original end-points as points in (X,Y,Z) space. The + * chromaticity values (c) have the property: + * + * C + * c = --------- + * X + Y + Z + * + * For each c (x,y,z) from the corresponding original C (X,Y,Z). Thus the + * three chromaticity values (x,y,z) for each end-point obey the + * relationship: + * + * x + y + z = 1 + * + * This describes the plane in (X,Y,Z) space that intersects each axis at the + * value 1.0; call this the chromaticity plane. Thus the chromaticity + * calculation has scaled each end-point so that it is on the x+y+z=1 plane + * and chromaticity is the intersection of the vector from the origin to the + * (X,Y,Z) value with the chromaticity plane. + * + * To fully invert the chromaticity calculation we would need the three + * end-point scale factors, (red-scale, green-scale, blue-scale), but these + * were not recorded. Instead we calculated the reference white (X,Y,Z) and + * recorded the chromaticity of this. The reference white (X,Y,Z) would have + * given all three of the scale factors since: + * + * color-C = color-c * color-scale + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * But cHRM records only white-x and white-y, so we have lost the white scale + * factor: + * + * white-C = white-c*white-scale + * + * To handle this the inverse transformation makes an arbitrary assumption + * about white-scale: + * + * Assume: white-Y = 1.0 + * Hence: white-scale = 1/white-y + * Or: red-Y + green-Y + blue-Y = 1.0 + * + * Notice the last statement of the assumption gives an equation in three of + * the nine values we want to calculate. 8 more equations come from the + * above routine as summarised at the top above (the chromaticity + * calculation): + * + * Given: color-x = color-X / (color-X + color-Y + color-Z) + * Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0 + * + * This is 9 simultaneous equations in the 9 variables "color-C" and can be + * solved by Cramer's rule. Cramer's rule requires calculating 10 9x9 matrix + * determinants, however this is not as bad as it seems because only 28 of + * the total of 90 terms in the various matrices are non-zero. Nevertheless + * Cramer's rule is notoriously numerically unstable because the determinant + * calculation involves the difference of large, but similar, numbers. It is + * difficult to be sure that the calculation is stable for real world values + * and it is certain that it becomes unstable where the end points are close + * together. + * + * So this code uses the perhaps slighly less optimal but more understandable + * and totally obvious approach of calculating color-scale. + * + * This algorithm depends on the precision in white-scale and that is + * (1/white-y), so we can immediately see that as white-y approaches 0 the + * accuracy inherent in the cHRM chunk drops off substantially. + * + * libpng arithmetic: a simple invertion of the above equations + * ------------------------------------------------------------ + * + * white_scale = 1/white-y + * white-X = white-x * white-scale + * white-Y = 1.0 + * white-Z = (1 - white-x - white-y) * white_scale + * + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * This gives us three equations in (red-scale,green-scale,blue-scale) where + * all the coefficients are now known: + * + * red-x*red-scale + green-x*green-scale + blue-x*blue-scale + * = white-x/white-y + * red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1 + * red-z*red-scale + green-z*green-scale + blue-z*blue-scale + * = (1 - white-x - white-y)/white-y + * + * In the last equation color-z is (1 - color-x - color-y) so we can add all + * three equations together to get an alternative third: + * + * red-scale + green-scale + blue-scale = 1/white-y = white-scale + * + * So now we have a Cramer's rule solution where the determinants are just + * 3x3 - far more tractible. Unfortunately 3x3 determinants still involve + * multiplication of three coefficients so we can't guarantee to avoid + * overflow in the libpng fixed point representation. Using Cramer's rule in + * floating point is probably a good choice here, but it's not an option for + * fixed point. Instead proceed to simplify the first two equations by + * eliminating what is likely to be the largest value, blue-scale: + * + * blue-scale = white-scale - red-scale - green-scale + * + * Hence: + * + * (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale = + * (white-x - blue-x)*white-scale + * + * (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale = + * 1 - blue-y*white-scale + * + * And now we can trivially solve for (red-scale,green-scale): + * + * green-scale = + * (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale + * ----------------------------------------------------------- + * green-x - blue-x + * + * red-scale = + * 1 - blue-y*white-scale - (green-y - blue-y) * green-scale + * --------------------------------------------------------- + * red-y - blue-y + * + * Hence: + * + * red-scale = + * ( (green-x - blue-x) * (white-y - blue-y) - + * (green-y - blue-y) * (white-x - blue-x) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * green-scale = + * ( (red-y - blue-y) * (white-x - blue-x) - + * (red-x - blue-x) * (white-y - blue-y) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * Accuracy: + * The input values have 5 decimal digits of accuracy. The values are all in + * the range 0 < value < 1, so simple products are in the same range but may + * need up to 10 decimal digits to preserve the original precision and avoid + * underflow. Because we are using a 32-bit signed representation we cannot + * match this; the best is a little over 9 decimal digits, less than 10. + * + * The approach used here is to preserve the maximum precision within the + * signed representation. Because the red-scale calculation above uses the + * difference between two products of values that must be in the range -1..+1 + * it is sufficient to divide the product by 7; ceil(100,000/32767*2). The + * factor is irrelevant in the calculation because it is applied to both + * numerator and denominator. + * + * Note that the values of the differences of the products of the + * chromaticities in the above equations tend to be small, for example for + * the sRGB chromaticities they are: + * + * red numerator: -0.04751 + * green numerator: -0.08788 + * denominator: -0.2241 (without white-y multiplication) + * + * The resultant Y coefficients from the chromaticities of some widely used + * color space definitions are (to 15 decimal places): + * + * sRGB + * 0.212639005871510 0.715168678767756 0.072192315360734 + * Kodak ProPhoto + * 0.288071128229293 0.711843217810102 0.000085653960605 + * Adobe RGB + * 0.297344975250536 0.627363566255466 0.075291458493998 + * Adobe Wide Gamut RGB + * 0.258728243040113 0.724682314948566 0.016589442011321 + */ + /* By the argument, above overflow should be impossible here. The return + * value of 2 indicates an internal error to the caller. + */ + if (!png_muldiv(&left, xy.greenx-xy.bluex, xy.redy - xy.bluey, 7)) return 2; + if (!png_muldiv(&right, xy.greeny-xy.bluey, xy.redx - xy.bluex, 7)) return 2; + denominator = left - right; + + /* Now find the red numerator. */ + if (!png_muldiv(&left, xy.greenx-xy.bluex, xy.whitey-xy.bluey, 7)) return 2; + if (!png_muldiv(&right, xy.greeny-xy.bluey, xy.whitex-xy.bluex, 7)) return 2; + + /* Overflow is possible here and it indicates an extreme set of PNG cHRM + * chunk values. This calculation actually returns the reciprocal of the + * scale value because this allows us to delay the multiplication of white-y + * into the denominator, which tends to produce a small number. + */ + if (!png_muldiv(&red_inverse, xy.whitey, denominator, left-right) || + red_inverse <= xy.whitey /* r+g+b scales = white scale */) + return 1; + + /* Similarly for green_inverse: */ + if (!png_muldiv(&left, xy.redy-xy.bluey, xy.whitex-xy.bluex, 7)) return 2; + if (!png_muldiv(&right, xy.redx-xy.bluex, xy.whitey-xy.bluey, 7)) return 2; + if (!png_muldiv(&green_inverse, xy.whitey, denominator, left-right) || + green_inverse <= xy.whitey) + return 1; + + /* And the blue scale, the checks above guarantee this can't overflow but it + * can still produce 0 for extreme cHRM values. + */ + blue_scale = png_reciprocal(xy.whitey) - png_reciprocal(red_inverse) - + png_reciprocal(green_inverse); + if (blue_scale <= 0) return 1; + + + /* And fill in the png_XYZ: */ + if (!png_muldiv(&XYZ->redX, xy.redx, PNG_FP_1, red_inverse)) return 1; + if (!png_muldiv(&XYZ->redY, xy.redy, PNG_FP_1, red_inverse)) return 1; + if (!png_muldiv(&XYZ->redZ, PNG_FP_1 - xy.redx - xy.redy, PNG_FP_1, + red_inverse)) + return 1; + + if (!png_muldiv(&XYZ->greenX, xy.greenx, PNG_FP_1, green_inverse)) return 1; + if (!png_muldiv(&XYZ->greenY, xy.greeny, PNG_FP_1, green_inverse)) return 1; + if (!png_muldiv(&XYZ->greenZ, PNG_FP_1 - xy.greenx - xy.greeny, PNG_FP_1, + green_inverse)) + return 1; + + if (!png_muldiv(&XYZ->blueX, xy.bluex, blue_scale, PNG_FP_1)) return 1; + if (!png_muldiv(&XYZ->blueY, xy.bluey, blue_scale, PNG_FP_1)) return 1; + if (!png_muldiv(&XYZ->blueZ, PNG_FP_1 - xy.bluex - xy.bluey, blue_scale, + PNG_FP_1)) + return 1; + + return 0; /*success*/ +} + +int png_XYZ_from_xy_checked(png_structp png_ptr, png_XYZ *XYZ, png_xy xy) +{ + switch (png_XYZ_from_xy(XYZ, xy)) + { + case 0: /* success */ + return 1; + + case 1: + /* The chunk may be technically valid, but we got png_fixed_point + * overflow while trying to get XYZ values out of it. This is + * entirely benign - the cHRM chunk is pretty extreme. + */ + png_warning(png_ptr, + "extreme cHRM chunk cannot be converted to tristimulus values"); + break; + + default: + /* libpng is broken; this should be a warning but if it happens we + * want error reports so for the moment it is an error. + */ + png_error(png_ptr, "internal error in png_XYZ_from_xy"); + break; + } + + /* ERROR RETURN */ + return 0; +} +#endif + void /* PRIVATE */ png_check_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, @@ -1383,18 +1722,30 @@ 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 */ - if (exp_b10 < 0) + + /* The following use of an unsigned temporary avoids ambiguities in + * the signed arithmetic on exp_b10 and permits GCC at least to do + * better optimization. + */ { - *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */ - exp_b10 = -exp_b10; - } + unsigned int uexp_b10; + + if (exp_b10 < 0) + { + *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */ + uexp_b10 = -exp_b10; + } - cdigits = 0; + else + uexp_b10 = exp_b10; - while (exp_b10 > 0) - { - exponent[cdigits++] = (char)(48 + exp_b10 % 10); - exp_b10 /= 10; + cdigits = 0; + + while (uexp_b10 > 0) + { + exponent[cdigits++] = (char)(48 + uexp_b10 % 10); + uexp_b10 /= 10; + } } /* Need another size check here for the exponent digits, so @@ -1452,7 +1803,7 @@ png_ascii_from_fixed(png_structp png_ptr, png_charp ascii, png_size_t size, else num = fp; - if (num <= 0x80000000U) /* else overflowed */ + if (num <= 0x80000000) /* else overflowed */ { unsigned int ndigits = 0, first = 16 /* flag value */; char digits[10]; @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.5.4 - July 7, 2011 + * libpng version 1.5.5beta08 - September 10, 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.4 - July 7, 2011: Glenn + * libpng versions 0.97, January 1998, through 1.5.5beta08 - September 10, 2011: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: @@ -157,6 +157,8 @@ * 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] + * 1.5.5beta01-08 15 10505 15.so.15.5[.0] + * 1.5.5rc01 15 10505 15.so.15.5[.0] * * Henceforth the source version will match the shared-library major * and minor numbers; the shared-library major version number will be @@ -188,7 +190,7 @@ * * This code is released under the libpng license. * - * libpng versions 1.2.6, August 15, 2004, through 1.5.4, July 7, 2011, are + * libpng versions 1.2.6, August 15, 2004, through 1.5.5beta08, September 10, 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: @@ -300,13 +302,13 @@ * Y2K compliance in libpng: * ========================= * - * July 7, 2011 + * September 10, 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.4 are Y2K compliant. It is my belief that + * upward through 1.5.5beta08 are Y2K compliant. It is my belief that * earlier versions were also Y2K compliant. * * Libpng only has two year fields. One is a 2-byte unsigned integer @@ -361,9 +363,9 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.5.4" +#define PNG_LIBPNG_VER_STRING "1.5.5beta08" #define PNG_HEADER_VERSION_STRING \ - " libpng version 1.5.4 - July 7, 2011\n" + " libpng version 1.5.5beta08 - September 10, 2011\n" #define PNG_LIBPNG_VER_SONUM 15 #define PNG_LIBPNG_VER_DLLNUM 15 @@ -371,12 +373,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 4 +#define PNG_LIBPNG_VER_RELEASE 5 /* This should match the numeric part of the final component of * PNG_LIBPNG_VER_STRING, omitting any leading zero: */ -#define PNG_LIBPNG_VER_BUILD 0 +#define PNG_LIBPNG_VER_BUILD 08 /* Release Status */ #define PNG_LIBPNG_BUILD_ALPHA 1 @@ -401,7 +403,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 10504 /* 1.5.4 */ +#define PNG_LIBPNG_VER 10505 /* 1.5.5 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -523,7 +525,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_4; +typedef char* png_libpng_version_1_5_5beta08; /* 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 @@ -793,7 +795,7 @@ typedef png_info FAR * FAR * png_infopp; #define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ #define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ #define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ -#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000 /* ESR, 1.0.6 */ /* This is used for the transformation routines, as some of them * change these values for the row. It also should enable using @@ -2047,6 +2049,10 @@ PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structp png_ptr, png_const_infop info_ptr, double *white_x, double *white_y, double *red_x, double *red_y, double *green_x, double *green_y, double *blue_x, double *blue_y)); +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_structp png_ptr, + png_const_infop info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)); #ifdef PNG_FIXED_POINT_SUPPORTED /* Otherwise not implemented */ PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, (png_const_structp png_ptr, @@ -2056,6 +2062,13 @@ PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, png_fixed_point *int_green_y, png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)); #endif +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_structp png_ptr, png_const_infop info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)); #endif #ifdef PNG_cHRM_SUPPORTED @@ -2063,12 +2076,22 @@ PNG_FP_EXPORT(135, void, png_set_cHRM, (png_structp png_ptr, png_infop info_ptr, double white_x, double white_y, double red_x, double red_y, double green_x, double green_y, double blue_x, double blue_y)); +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_structp png_ptr, + png_infop info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)); PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_structp png_ptr, png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, png_fixed_point int_blue_y)); +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)); #endif #ifdef PNG_gAMA_SUPPORTED @@ -2282,7 +2305,7 @@ PNG_EXPORT(171, void, png_set_sCAL_s, handling or default unknown chunk handling is not desired. Any chunks not listed will be handled in the default manner. The IHDR and IEND chunks must not be listed. - keep = 0: follow default behaviour + keep = 0: follow default behavior = 1: do not keep = 2: keep only if safe-to-copy = 3: keep even if unsafe-to-copy @@ -2492,14 +2515,14 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ * (png_uint_16)(alpha) \ + (png_uint_16)(bg)*(png_uint_16)(255 \ - - (png_uint_16)(alpha)) + (png_uint_16)128); \ + - (png_uint_16)(alpha)) + 128); \ (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } # define png_composite_16(composite, fg, alpha, bg) \ { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ * (png_uint_32)(alpha) \ - + (png_uint_32)(bg)*(png_uint_32)(65535L \ - - (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } #else /* Standard method using integer division */ @@ -2507,12 +2530,12 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, # define png_composite(composite, fg, alpha, bg) \ (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ - (png_uint_16)127) / 255) + 127) / 255) # define png_composite_16(composite, fg, alpha, bg) \ (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ - (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ - (png_uint_32)32767) / (png_uint_32)65535L) + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535) #endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED @@ -2576,7 +2599,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(229); + PNG_EXPORT_LAST_ORDINAL(233); #endif #ifdef __cplusplus @@ -1,7 +1,7 @@ /* pngconf.h - machine configurable file for libpng * - * libpng version 1.5.4 - July 7, 2011 + * libpng version 1.5.5beta08 - September 10, 2011 * * Copyright (c) 1998-2011 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) @@ -14,7 +14,6 @@ * 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 @@ -170,7 +169,9 @@ * 'type', compiler specific. * * PNG_DLL_EXPORT Set to the magic to use during a libpng build to - * make a symbol exported from the DLL. + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. * * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come * from a DLL - used to define PNG_IMPEXP when @@ -264,25 +265,14 @@ # define PNGAPI PNGCAPI #endif -/* The default for PNG_IMPEXP depends on whether the library is - * being built or used. +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. */ #ifndef PNG_IMPEXP -# ifdef PNGLIB_BUILD - /* Building the library */ -# if (defined(DLL_EXPORT)/*from libtool*/ ||\ - defined(_WINDLL) || defined(_DLL) || defined(__DLL__) ||\ - defined(_USRDLL) ||\ - defined(PNG_BUILD_DLL)) && defined(PNG_DLL_EXPORT) - /* Building a DLL. */ -# define PNG_IMPEXP PNG_DLL_EXPORT -# endif /* DLL */ -# else - /* Using the library */ -# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) - /* This forces use of a DLL, disallowing static linking */ -# define PNG_IMPEXP PNG_DLL_IMPORT -# endif +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT # endif # ifndef PNG_IMPEXP @@ -362,25 +352,18 @@ # ifndef PNG_ALLOCATED # define PNG_ALLOCATED __attribute__((__malloc__)) # endif - - /* This specifically protects structure members that should only be - * accessed from within the library, therefore should be empty during - * a library build. - */ -# ifndef PNGLIB_BUILD -# ifndef PNG_DEPRECATED -# define PNG_DEPRECATED __attribute__((__deprecated__)) -# endif -# ifndef PNG_PRIVATE -# if 0 /* Doesn't work so we use deprecated instead*/ -# define PNG_PRIVATE \ - __attribute__((warning("This function is not exported by libpng."))) -# else -# define PNG_PRIVATE \ - __attribute__((__deprecated__)) -# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) # endif -# endif /* PNGLIB_BUILD */ +# endif # endif /* __GNUC__ */ # if defined(_MSC_VER) && (_MSC_VER >= 1300) @@ -391,23 +374,16 @@ # define PNG_NORETURN __declspec(noreturn) # endif # ifndef PNG_ALLOCATED -# if (_MSC_VER >= 1400) +# if defined(_MSC_VER) && (_MSC_VER >= 1300) # define PNG_ALLOCATED __declspec(restrict) # endif # endif - - /* This specifically protects structure members that should only be - * accessed from within the library, therefore should be empty during - * a library build. - */ -# ifndef PNGLIB_BUILD -# ifndef PNG_DEPRECATED -# define PNG_DEPRECATED __declspec(deprecated) -# endif -# ifndef PNG_PRIVATE -# define PNG_PRIVATE __declspec(deprecated) -# endif -# endif /* PNGLIB_BUILD */ +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif # endif /* _MSC_VER */ #endif /* PNG_PEDANTIC_WARNINGS */ diff --git a/pngcrush.c b/pngcrush.c index 1ed4865ca..04bf20658 100644 --- a/pngcrush.c +++ b/pngcrush.c @@ -185,7 +185,7 @@ Change log: -Version 1.7.17 (built with libpng-1.5.4 and zlib-1.2.5) +Version 1.7.17 (built with libpng-1.5.5beta08 and zlib-1.2.5) Changed "#if !defined(PNG_NO_STDIO)" to "#ifdef PNG_STDIO_SUPPORTED" as recommended in the libpng documentation. Added PNG_UINT_32_NAME macro and used it to simplify chunk_type integer @@ -3970,9 +3970,11 @@ int main(int argc, char *argv[]) need_expand = 1; #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - if ((color_type == 2 || color_type == 6 - || color_type == 3) && - (output_color_type == 0 || output_color_type == 4)) + if ((color_type == 2 || + color_type == 6 || + color_type == 3) && + (output_color_type == 0 || + output_color_type == 4)) { if (verbose > 0 && first_trial) { @@ -3986,9 +3988,11 @@ int main(int argc, char *argv[]) "image to grayscale.\n"); } #ifdef PNG_FIXED_POINT_SUPPORTED - png_set_rgb_to_gray_fixed(read_ptr, 1, -1, -1); + png_set_rgb_to_gray_fixed(read_ptr, 1, + 21260, 71520); #else - png_set_rgb_to_gray(read_ptr, 1, 0., 0.); + png_set_rgb_to_gray(read_ptr, 1, + 0.21260, 0.71520); #endif if (output_bit_depth < 8) output_bit_depth = 8; diff --git a/pngcrush.h b/pngcrush.h index 62890f977..f00311d7a 100644 --- a/pngcrush.h +++ b/pngcrush.h @@ -5,16 +5,11 @@ * license (see LICENSE, in pngcrush.c). */ -/* Special defines for pngcrush, mostly just to reduce the size of the - static executable. */ +/* Special defines for pngcrush. */ #ifndef PNGCRUSH_H #define PNGCRUSH_H -/* -#include <malloc.h> -*/ - #define TOO_FAR 32767 /* This allows png_default_error() to return, when it is called after our @@ -1,7 +1,7 @@ /* pngget.c - retrieval of values from info struct * - * Last changed in libpng 1.5.1 [February 3, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * 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.) @@ -459,6 +459,65 @@ png_get_bKGD(png_const_structp png_ptr, png_infop info_ptr, #endif #ifdef PNG_cHRM_SUPPORTED +/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the + * same time to correct the rgb grayscale coefficient defaults obtained from the + * cHRM chunk in 1.5.4 + */ +png_uint_32 PNGFAPI +png_get_cHRM_XYZ_fixed(png_structp png_ptr, png_const_infop info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_xy xy; + png_XYZ XYZ; + + png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); + + xy.whitex = info_ptr->x_white; + xy.whitey = info_ptr->y_white; + xy.redx = info_ptr->x_red; + xy.redy = info_ptr->y_red; + xy.greenx = info_ptr->x_green; + xy.greeny = info_ptr->y_green; + xy.bluex = info_ptr->x_blue; + xy.bluey = info_ptr->y_blue; + + /* The *_checked function handles error reporting, so just return 0 if + * there is a failure here. + */ + if (png_XYZ_from_xy_checked(png_ptr, &XYZ, xy)) + { + if (int_red_X != NULL) + *int_red_X = XYZ.redX; + if (int_red_Y != NULL) + *int_red_Y = XYZ.redY; + if (int_red_Z != NULL) + *int_red_Z = XYZ.redZ; + if (int_green_X != NULL) + *int_green_X = XYZ.greenX; + if (int_green_Y != NULL) + *int_green_Y = XYZ.greenY; + if (int_green_Z != NULL) + *int_green_Z = XYZ.greenZ; + if (int_blue_X != NULL) + *int_blue_X = XYZ.blueX; + if (int_blue_Y != NULL) + *int_blue_Y = XYZ.blueY; + if (int_blue_Z != NULL) + *int_blue_Z = XYZ.blueZ; + + return (PNG_INFO_cHRM); + } + } + + return (0); +} + # ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_cHRM(png_const_structp png_ptr, png_const_infop info_ptr, @@ -490,6 +549,42 @@ png_get_cHRM(png_const_structp png_ptr, png_const_infop info_ptr, return (0); } + +png_uint_32 PNGAPI +png_get_cHRM_XYZ(png_structp png_ptr, png_const_infop info_ptr, + double *red_X, double *red_Y, double *red_Z, double *green_X, + double *green_Y, double *green_Z, double *blue_X, double *blue_Y, + double *blue_Z) +{ + png_XYZ XYZ; + + if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, + &XYZ.redX, &XYZ.redY, &XYZ.redZ, &XYZ.greenX, &XYZ.greenY, &XYZ.greenZ, + &XYZ.blueX, &XYZ.blueY, &XYZ.blueZ) & PNG_INFO_cHRM) + { + if (red_X != NULL) + *red_X = png_float(png_ptr, XYZ.redX, "cHRM red X"); + if (red_Y != NULL) + *red_Y = png_float(png_ptr, XYZ.redY, "cHRM red Y"); + if (red_Z != NULL) + *red_Z = png_float(png_ptr, XYZ.redZ, "cHRM red Z"); + if (green_X != NULL) + *green_X = png_float(png_ptr, XYZ.greenX, "cHRM green X"); + if (green_Y != NULL) + *green_Y = png_float(png_ptr, XYZ.greenY, "cHRM green Y"); + if (green_Z != NULL) + *green_Z = png_float(png_ptr, XYZ.greenZ, "cHRM green Z"); + if (blue_X != NULL) + *blue_X = png_float(png_ptr, XYZ.blueX, "cHRM blue X"); + if (blue_Y != NULL) + *blue_Y = png_float(png_ptr, XYZ.blueY, "cHRM blue Y"); + if (blue_Z != NULL) + *blue_Z = png_float(png_ptr, XYZ.blueZ, "cHRM blue Z"); + return (PNG_INFO_cHRM); + } + + return (0); +} # endif # ifdef PNG_FIXED_POINT_SUPPORTED @@ -971,7 +1066,7 @@ png_get_user_chunk_ptr(png_const_structp png_ptr) png_size_t PNGAPI png_get_compression_buffer_size(png_const_structp png_ptr) { - return (png_ptr ? png_ptr->zbuf_size : 0L); + return (png_ptr ? png_ptr->zbuf_size : 0); } diff --git a/pnglibconf.h b/pnglibconf.h index 827d17e54..8022a5a88 100644 --- a/pnglibconf.h +++ b/pnglibconf.h @@ -33,8 +33,8 @@ #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_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 #define PNG_WEIGHT_SHIFT 8 #define PNG_ZBUF_SIZE 8192 /* end of settings */ @@ -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.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -44,7 +44,8 @@ */ #include <stdlib.h> -#define PNGLIB_BUILD +#define PNGLIB_BUILD /*libpng is being built, not used*/ + #ifdef PNG_USER_CONFIG # include "pngusr.h" /* These should have been defined in pngusr.h */ @@ -55,10 +56,80 @@ # define PNG_USER_DLLFNAME_POSTFIX "Cb" # endif #endif + +/* Is this a build of a DLL where compilation of the object modules requires + * different preprocessor settings to those required for a simple library? If + * so PNG_BUILD_DLL must be set. + * + * If libpng is used inside a DLL but that DLL does not export the libpng APIs + * PNG_BUILD_DLL must not be set. To avoid the code below kicking in build a + * static library of libpng then link the DLL against that. + */ +#ifndef PNG_BUILD_DLL +# ifdef DLL_EXPORT + /* This is set by libtool when files are compiled for a DLL; libtool + * always compiles twice, even on systems where it isn't necessary. Set + * PNG_BUILD_DLL in case it is necessary: + */ +# define PNG_BUILD_DLL +# else +# ifdef _WINDLL + /* This is set by the Microsoft Visual Studio IDE in projects that + * build a DLL. It can't easily be removed from those projects (it + * isn't visible in the Visual Studio UI) so it is a fairly reliable + * indication that PNG_IMPEXP needs to be set to the DLL export + * attributes. + */ +# define PNG_BUILD_DLL +# else +# ifdef __DLL__ + /* This is set by the Borland C system when compiling for a DLL + * (as above.) + */ +# define PNG_BUILD_DLL +# else + /* Add additional compiler cases here. */ +# endif +# endif +# endif +#endif /* Setting PNG_BUILD_DLL if required */ + +/* See pngconf.h for more details: the builder of the library may set this on + * the command line to the right thing for the specific compilation system or it + * may be automagically set above (at present we know of no system where it does + * need to be set on the command line.) + * + * PNG_IMPEXP must be set here when building the library to prevent pngconf.h + * setting it to the "import" setting for a DLL build. + */ +#ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP PNG_DLL_EXPORT +# else + /* Not building a DLL, or the DLL doesn't require specific export + * definitions. + */ +# define PNG_IMPEXP +# endif +#endif + +/* No warnings for private or deprecated functions in the build: */ +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE +#endif + #include "png.h" #include "pnginfo.h" #include "pngstruct.h" +/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */ +#ifndef PNG_DLL_EXPORT +# define PNG_DLL_EXPORT +#endif + /* This is used for 16 bit gamma tables - only the top level pointers are const, * this could be changed: */ @@ -301,16 +372,15 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp; #define PNG_USER_TRANSFORM 0x100000L #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 */ -#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 */ -#define PNG_SCALE_16_TO_8 0x4000000L /* Added to libpng-1.5.4 */ - /* 0x8000000L unused */ - /* 0x10000000L unused */ - /* 0x20000000L unused */ - /* 0x40000000L unused */ - +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ +#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 */ +#define PNG_SCALE_16_TO_8 0x4000000L /* Added to libpng-1.5.4 */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ /* Flags for png_create_struct */ #define PNG_STRUCT_PNG 0x0001 #define PNG_STRUCT_INFO 0x0002 @@ -335,22 +405,22 @@ typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp; #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 -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L -#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L -#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L - /* 0x200000L unused */ - /* 0x400000L unused */ -#define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000L /* Added to libpng-1.4.0 */ -#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_KEEP_UNKNOWN_CHUNKS 0x8000 +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000 + /* 0x200000 unused */ + /* 0x400000 unused */ +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000 /* Added to libpng-1.4.0 */ +#define PNG_FLAG_ZTXT_CUSTOM_STRATEGY 0x1000000 /* 5 lines added */ +#define PNG_FLAG_ZTXT_CUSTOM_LEVEL 0x2000000 /* to libpng-1.5.4 */ +#define PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL 0x4000000 +#define PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS 0x8000000 +#define PNG_FLAG_ZTXT_CUSTOM_METHOD 0x10000000 + /* 0x20000000 unused */ + /* 0x40000000 unused */ #define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ PNG_FLAG_CRC_ANCILLARY_NOWARN) @@ -1078,6 +1148,35 @@ PNG_EXTERN void png_64bit_product PNGARG((long v1, long v2, unsigned long *hi_product, unsigned long *lo_product)); #endif +#ifdef PNG_cHRM_SUPPORTED +/* Added at libpng version 1.5.5 */ +typedef struct png_xy +{ + png_fixed_point redx, redy; + png_fixed_point greenx, greeny; + png_fixed_point bluex, bluey; + png_fixed_point whitex, whitey; +} png_xy; + +typedef struct png_XYZ +{ + png_fixed_point redX, redY, redZ; + png_fixed_point greenX, greenY, greenZ; + png_fixed_point blueX, blueY, blueZ; +} png_XYZ; + +/* The conversion APIs return 0 on success, non-zero on a parameter error. They + * allow conversion between the above representations of a color encoding. When + * converting from XYZ end points to chromaticities the absolute magnitude of + * the end points is lost, when converting back the sum of the Y values of the + * three end points will be 1.0 + */ +PNG_EXTERN int png_xy_from_XYZ PNGARG((png_xy *xy, png_XYZ XYZ)); +PNG_EXTERN int png_XYZ_from_xy PNGARG((png_XYZ *XYZ, png_xy xy)); +PNG_EXTERN int png_XYZ_from_xy_checked PNGARG((png_structp png_ptr, + png_XYZ *XYZ, png_xy xy)); +#endif + /* Added at libpng version 1.4.0 */ PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, diff --git a/pngrtran.c b/pngrtran.c index 4e0401aa9..c6dd28808 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.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * 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.) @@ -968,13 +968,17 @@ png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, { png_uint_16 red_int, green_int; - red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); - green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + /* NOTE: this calculation does not round, but this behavior is retained + * for consistency, the inaccuracy is very small. The code here always + * overwrites the coefficients, regardless of whether they have been + * defaulted or set already. + */ + red_int = (png_uint_16)(((png_uint_32)red*32768)/100000); + green_int = (png_uint_16)(((png_uint_32)green*32768)/100000); 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); + png_ptr->rgb_to_gray_coefficients_set = 1; } else @@ -983,17 +987,18 @@ png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); - /* 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!) + /* Use the defaults, from the cHRM chunk if set, else the historical + * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See + * png_do_rgb_to_gray for more discussion of the values. In this case + * the coefficients are not marked as 'set' and are not overwritten if + * something has already provided a default. */ 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_green_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; + png_ptr->rgb_to_gray_red_coeff = 6968; + png_ptr->rgb_to_gray_green_coeff = 23434; + /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ } } } @@ -1401,7 +1406,7 @@ png_init_read_transformations(png_structp png_ptr) 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. + * the file was grayscale the background value is gray. */ if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; @@ -1635,7 +1640,7 @@ png_init_read_transformations(png_structp png_ptr) /* Prevent the transformations being done again. * - * NOTE: this is highly dubious; it zaps the transformations in + * NOTE: this is highly dubious; it removes the transformations in * place. This seems inconsistent with the general treatment of the * transformations elsewhere. */ @@ -3066,33 +3071,61 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB files to grayscale, with or without alpha - * using the equation given in Poynton's ColorFAQ at - * <http://www.inforamp.net/~poynton/> (THIS LINK IS DEAD June 2008) - * New link: - * <http://www.poynton.com/notes/colour_and_gamma/> + * using the equation given in Poynton's ColorFAQ of 1998-01-04 at + * <http://www.inforamp.net/~poynton/> (THIS LINK IS DEAD June 2008 but + * versions dated 1998 through November 2002 have been archived at + * http://web.archive.org/web/20000816232553/http://www.inforamp.net/ + * ~poynton/notes/colour_and_gamma/ColorFAQ.txt ) * Charles Poynton poynton at poynton.com * * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B * - * We approximate this with + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * Poynton's current link (as of January 2003 through July 2011): + * <http://www.poynton.com/notes/colour_and_gamma/> + * has changed the numbers slightly: * - * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * Y = 0.2126*R + 0.7152*G + 0.0722*B * * which can be expressed with integers as * - * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * Y = (6966 * R + 23436 * G + 2366 * B)/32768 * - * The calculation is to be done in a linear colorspace. + * Historically, however, libpng uses numbers derived from the ITU-R Rec 709 + * end point chromaticities and the D65 white point. Depending on the + * precision used for the D65 white point this produces a variety of different + * numbers, however if the four decimal place value used in ITU-R Rec 709 is + * used (0.3127,0.3290) the Y calculation would be: * - * Other integer coefficents can be used via png_set_rgb_to_gray(). + * Y = (6968 * R + 23435 * G + 2366 * B)/32768 + * + * While this is correct the rounding results in an overflow for white, because + * the sum of the rounded coefficients is 32769, not 32768. Consequently + * libpng uses, instead, the closest non-overflowing approximation: + * + * Y = (6968 * R + 23434 * G + 2366 * B)/32768 + * + * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk + * (including an sRGB chunk) then the chromaticities are used to calculate the + * coefficients. See the chunk handling in pngrutil.c for more information. + * + * In all cases the calculation is to be done in a linear colorspace. If no + * gamma information is available to correct the encoding of the original RGB + * values this results in an implicit assumption that the original PNG RGB + * values were linear. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). Because + * the API takes just red and green coefficients the blue coefficient is + * calculated to make the sum 32768. This will result in different rounding + * to that used above. */ int /* PRIVATE */ png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) { - png_uint_32 i; - - png_uint_32 row_width = row_info->width; int rgb_error = 0; png_debug(1, "in png_do_rgb_to_gray"); @@ -3100,234 +3133,179 @@ png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) && (row_info->color_type & PNG_COLOR_MASK_COLOR)) { - png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; - png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; - png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + PNG_CONST png_uint_32 bc = 32768 - rc - gc; + PNG_CONST png_uint_32 row_width = row_info->width; + PNG_CONST int have_alpha = + (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0; - if (row_info->color_type == PNG_COLOR_TYPE_RGB) + if (row_info->bit_depth == 8) { - if (row_info->bit_depth == 8) - { #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + /* Notice that gamma to/from 1 are not necessarily inverses (if + * there is an overall gamma correction). Prior to 1.5.5 this code + * checked the linearized values for equality; this doesn't match + * the documentation, the original values must be checked. + */ + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) { - png_bytep sp = row; - png_bytep dp = row; + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); - for (i = 0; i < row_width; i++) + if (red != green || red != blue) { - png_byte red = png_ptr->gamma_to_1[*(sp++)]; - png_byte green = png_ptr->gamma_to_1[*(sp++)]; - png_byte blue = png_ptr->gamma_to_1[*(sp++)]; - - if (red != green || red != blue) - { - rgb_error |= 1; - *(dp++) = png_ptr->gamma_from_1[ - (rc*red + gc*green + bc*blue)>>15]; - } + red = png_ptr->gamma_to_1[red]; + green = png_ptr->gamma_to_1[green]; + blue = png_ptr->gamma_to_1[blue]; - else - *(dp++) = *(sp - 1); + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue + 16384)>>15]; } - } - else -#endif - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - if (red != green || red != blue) - { - rgb_error |= 1; - *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); - } + else + { + /* If there is no overall correction the table will not be + * set. + */ + if (png_ptr->gamma_table != NULL) + red = png_ptr->gamma_table[red]; - else - *(dp++) = *(sp - 1); + *(dp++) = red; } + + if (have_alpha) + *(dp++) = *(sp++); } } - - else /* RGB bit_depth == 16 */ + else +#endif { -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_16_to_1 != NULL && - png_ptr->gamma_16_from_1 != NULL) - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, w; - - red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - - if (red == green && red == blue) - w = red; - - else - { - png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) - >> png_ptr->gamma_shift][red>>8]; - png_uint_16 green_1 = - png_ptr->gamma_16_to_1[(green&0xff) >> - png_ptr->gamma_shift][green>>8]; - png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) - >> png_ptr->gamma_shift][blue>>8]; - png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 - + bc*blue_1)>>15); - w = png_ptr->gamma_16_from_1[(gray16&0xff) >> - png_ptr->gamma_shift][gray16 >> 8]; - rgb_error |= 1; - } + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; - *(dp++) = (png_byte)((w>>8) & 0xff); - *(dp++) = (png_byte)(w & 0xff); - } - } - else -#endif + for (i = 0; i < row_width; i++) { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, gray16; + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); - red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + if (red != green || red != blue) + { + rgb_error |= 1; + /*NOTE: this is the historical approach which simply + * truncates the results. + */ + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + } - if (red != green || red != blue) - rgb_error |= 1; + else + *(dp++) = red; - gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); - *(dp++) = (png_byte)((gray16>>8) & 0xff); - *(dp++) = (png_byte)(gray16 & 0xff); - } + if (have_alpha) + *(dp++) = *(sp++); } } } - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + + else /* RGB bit_depth == 16 */ { - if (row_info->bit_depth == 8) - { #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_byte red = png_ptr->gamma_to_1[*(sp++)]; - png_byte green = png_ptr->gamma_to_1[*(sp++)]; - png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + png_uint_16 red, green, blue, w; - if (red != green || red != blue) - rgb_error |= 1; + red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - *(dp++) = png_ptr->gamma_from_1 - [(rc*red + gc*green + bc*blue)>>15]; + if (red == green && red == blue) + { + if (png_ptr->gamma_16_table != NULL) + w = png_ptr->gamma_16_table[(red&0xff) + >> png_ptr->gamma_shift][red>>8]; - *(dp++) = *(sp++); /* alpha */ + else + w = red; } - } - else -#endif - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) + + else { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - if (red != green || red != blue) - rgb_error |= 1; - - *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); - *(dp++) = *(sp++); /* alpha */ + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) + >> png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) + >> png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1 + 16384)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + + if (have_alpha) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); } } } - else /* RGBA bit_depth == 16 */ + else +#endif { -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_16_to_1 != NULL && - png_ptr->gamma_16_from_1 != NULL) - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, w; - - red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - - if (red == green && red == blue) - w = red; - - else - { - png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> - png_ptr->gamma_shift][red>>8]; - - png_uint_16 green_1 = - png_ptr->gamma_16_to_1[(green&0xff) >> - png_ptr->gamma_shift][green>>8]; + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; - png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> - png_ptr->gamma_shift][blue>>8]; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; - png_uint_16 gray16 = (png_uint_16)((rc * red_1 - + gc * green_1 + bc * blue_1)>>15); + red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; - w = png_ptr->gamma_16_from_1[(gray16&0xff) >> - png_ptr->gamma_shift][gray16 >> 8]; + if (red != green || red != blue) + rgb_error |= 1; - rgb_error |= 1; - } + /* From 1.5.5 in the 16 bit case do the accurate convertion even + * in the 'fast' case - this is because this is where the code + * ends up when handling linear 16 bit data. + */ + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >> + 15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); - *(dp++) = (png_byte)((w>>8) & 0xff); - *(dp++) = (png_byte)(w & 0xff); - *(dp++) = *(sp++); /* alpha */ - *(dp++) = *(sp++); - } - } - else -#endif - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) + if (have_alpha) { - png_uint_16 red, green, blue, gray16; - red = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2; - green = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2; - blue = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2; - - if (red != green || red != blue) - rgb_error |= 1; - - gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); - *(dp++) = (png_byte)((gray16>>8) & 0xff); - *(dp++) = (png_byte)(gray16 & 0xff); - *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); *(dp++) = *(sp++); } } } } + row_info->channels -= 2; row_info->color_type = (png_byte)(row_info->color_type & ~PNG_COLOR_MASK_COLOR); @@ -4762,7 +4740,7 @@ png_do_expand(png_row_infop row_info, png_bytep row, #endif #ifdef PNG_READ_EXPAND_16_SUPPORTED -/* If the bit depth is 8 and the colour type is not a palette type expand the +/* If the bit depth is 8 and the color type is not a palette type expand the * whole row to 16 bits. Has no effect otherwise. */ void /* PRIVATE */ @@ -4939,8 +4917,8 @@ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); - png_uint_32 red = (png_uint_32)((s0 + s1 + 65536L) & 0xffffL); - png_uint_32 blue = (png_uint_32)((s2 + s1 + 65536L) & 0xffffL); + png_uint_32 red = (s0 + s1 + 65536) & 0xffff; + png_uint_32 blue = (s2 + s1 + 65536) & 0xffff; *(rp ) = (png_byte)((red >> 8) & 0xff); *(rp + 1) = (png_byte)(red & 0xff); *(rp + 4) = (png_byte)((blue >> 8) & 0xff); diff --git a/pngrutil.c b/pngrutil.c index 07e46e2fe..a74353de1 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.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * 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.) @@ -87,10 +87,10 @@ png_int_32 (PNGAPI png_get_int_32)(png_const_bytep buf) { png_uint_32 uval = png_get_uint_32(buf); - if ((uval & 0x80000000L) == 0) /* non-negative */ + if ((uval & 0x80000000) == 0) /* non-negative */ return uval; - uval = (uval ^ 0xffffffffL) + 1; /* 2's complement: -x = ~x+1 */ + uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ return -(png_int_32)uval; } @@ -458,7 +458,7 @@ png_decompress_chunk(png_structp png_ptr, int comp_type, { /* Success (maybe) - really uncompress the chunk. */ png_size_t new_size = 0; - png_charp text = png_malloc_warn(png_ptr, + png_charp text = (png_charp)png_malloc_warn(png_ptr, prefix_size + expanded_size + 1); if (text != NULL) @@ -501,7 +501,7 @@ png_decompress_chunk(png_structp png_ptr, int comp_type, * amount of compressed data. */ { - png_charp text = png_malloc_warn(png_ptr, prefix_size + 1); + png_charp text = (png_charp)png_malloc_warn(png_ptr, prefix_size + 1); if (text != NULL) { @@ -827,7 +827,7 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) # ifdef PNG_READ_sRGB_SUPPORTED if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) { - if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + if (PNG_OUT_OF_RANGE(igamma, 45500, 500)) { PNG_WARNING_PARAMETERS(p) png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, igamma); @@ -994,10 +994,10 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { if (PNG_OUT_OF_RANGE(x_white, 31270, 1000) || PNG_OUT_OF_RANGE(y_white, 32900, 1000) || - PNG_OUT_OF_RANGE(x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(x_red, 64000, 1000) || PNG_OUT_OF_RANGE(y_red, 33000, 1000) || PNG_OUT_OF_RANGE(x_green, 30000, 1000) || - PNG_OUT_OF_RANGE(y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(y_green, 60000, 1000) || PNG_OUT_OF_RANGE(x_blue, 15000, 1000) || PNG_OUT_OF_RANGE(y_blue, 6000, 1000)) { @@ -1022,27 +1022,80 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Store the _white values as default coefficients for the rgb to gray - * operation if it is supported. + * operation if it is supported. Check if the transform is already set to + * avoid destroying the transform values. */ - if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) + if (!png_ptr->rgb_to_gray_coefficients_set) { - /* png_set_background has not been called, the coefficients must be in - * range for the following to work without overflow. + /* png_set_background has not been called and we haven't seen an sRGB + * chunk yet. Find the XYZ of the three end points. */ - if (y_red <= (1<<17) && y_green <= (1<<17) && y_blue <= (1<<17)) + png_XYZ XYZ; + png_xy xy; + + xy.redx = x_red; + xy.redy = y_red; + xy.greenx = x_green; + xy.greeny = y_green; + xy.bluex = x_blue; + xy.bluey = y_blue; + xy.whitex = x_white; + xy.whitey = y_white; + + if (png_XYZ_from_xy_checked(png_ptr, &XYZ, xy)) { - /* 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. + /* The success case, because XYZ_from_xy normalises to a reference + * white Y of 1.0 we just need to scale the numbers. This should + * always work just fine. It is an internal error if this overflows. */ - 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); + { + png_fixed_point r, g, b; + if (png_muldiv(&r, XYZ.redY, 32768, PNG_FP_1) && + r >= 0 && r <= 32768 && + png_muldiv(&g, XYZ.greenY, 32768, PNG_FP_1) && + g >= 0 && g <= 32768 && + png_muldiv(&b, XYZ.blueY, 32768, PNG_FP_1) && + b >= 0 && b <= 32768 && + r+g+b <= 32769) + { + /* We allow 0 coefficients here. r+g+b may be 32769 if two or + * all of the coefficients were rounded up. Handle this by + * reducing the *largest* coefficient by 1; this matches the + * approach used for the default coefficients in pngrtran.c + */ + int add = 0; + + if (r+g+b > 32768) + add = -1; + else if (r+g+b < 32768) + add = 1; + + if (add != 0) + { + if (g >= r && g >= b) + g += add; + else if (r >= g && r >= b) + r += add; + else + b += add; + } + + /* Check for an internal error. */ + if (r+g+b != 32768) + png_error(png_ptr, + "internal error handling cHRM coefficients"); + + png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; + png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + } + + /* This is a png_error at present even though it could be ignored - + * it should never happen, but it is important that if it does, the + * bug is fixed. + */ + else + png_error(png_ptr, "internal error handling cHRM->XYZ"); + } } } #endif @@ -1106,7 +1159,7 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) { - if (PNG_OUT_OF_RANGE(info_ptr->gamma, 45500L, 500)) + if (PNG_OUT_OF_RANGE(info_ptr->gamma, 45500, 500)) { PNG_WARNING_PARAMETERS(p) @@ -1123,10 +1176,10 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) if (PNG_OUT_OF_RANGE(info_ptr->x_white, 31270, 1000) || PNG_OUT_OF_RANGE(info_ptr->y_white, 32900, 1000) || - PNG_OUT_OF_RANGE(info_ptr->x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->x_red, 64000, 1000) || PNG_OUT_OF_RANGE(info_ptr->y_red, 33000, 1000) || PNG_OUT_OF_RANGE(info_ptr->x_green, 30000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->y_green, 60000, 1000) || PNG_OUT_OF_RANGE(info_ptr->x_blue, 15000, 1000) || PNG_OUT_OF_RANGE(info_ptr->y_blue, 6000, 1000)) { @@ -1135,6 +1188,47 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #endif /* PNG_READ_cHRM_SUPPORTED */ + /* This is recorded for use when handling the cHRM chunk above. An sRGB + * chunk unconditionally overwrites the coefficients for grayscale conversion + * too. + */ + png_ptr->is_sRGB = 1; + +# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Don't overwrite user supplied values: */ + if (!png_ptr->rgb_to_gray_coefficients_set) + { + /* These numbers come from the sRGB specification (or, since one has to + * pay much money to get a copy, the wikipedia sRGB page) the + * chromaticity values quoted have been inverted to get the reverse + * transformation from RGB to XYZ and the 'Y' coefficients scaled by + * 32768 (then rounded). + * + * sRGB and ITU Rec-709 both truncate the values for the D65 white + * point to four digits and, even though it actually stores five + * digits, the PNG spec gives the truncated value. + * + * This means that when the chromaticities are converted back to XYZ + * end points we end up with (6968,23435,2366), which, as described in + * pngrtran.c, would overflow. If the five digit precision and up is + * used we get, instead: + * + * 6968*R + 23435*G + 2365*B + * + * (Notice that this rounds the blue coefficient down, rather than the + * choice used in pngrtran.c which is to round the green one down.) + */ + png_ptr->rgb_to_gray_red_coeff = 6968; /* 0.212639005871510 */ + png_ptr->rgb_to_gray_green_coeff = 23434; /* 0.715168678767756 */ + /* png_ptr->rgb_to_gray_blue_coeff = 2366; 0.072192315360734 */ + + /* The following keeps the cHRM chunk from destroying the + * coefficients again in the event that it follows the sRGB chunk. + */ + png_ptr->rgb_to_gray_coefficients_set = 1; + } +# endif + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); } #endif /* PNG_READ_sRGB_SUPPORTED */ @@ -1,7 +1,7 @@ /* pngset.c - storage of image information into info struct * - * Last changed in libpng 1.5.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * 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.) @@ -64,6 +64,39 @@ png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, } } +void PNGFAPI +png_set_cHRM_XYZ_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z) +{ + png_XYZ XYZ; + png_xy xy; + + png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + XYZ.redX = int_red_X; + XYZ.redY = int_red_Y; + XYZ.redZ = int_red_Z; + XYZ.greenX = int_green_X; + XYZ.greenY = int_green_Y; + XYZ.greenZ = int_green_Z; + XYZ.blueX = int_blue_X; + XYZ.blueY = int_blue_Y; + XYZ.blueZ = int_blue_Z; + + if (png_xy_from_XYZ(&xy, XYZ)) + png_error(png_ptr, "XYZ values out of representable range"); + + png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy, + xy.greenx, xy.greeny, xy.bluex, xy.bluey); +} + # ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI png_set_cHRM(png_structp png_ptr, png_infop info_ptr, @@ -80,6 +113,23 @@ png_set_cHRM(png_structp png_ptr, png_infop info_ptr, png_fixed(png_ptr, blue_x, "cHRM Blue X"), png_fixed(png_ptr, blue_y, "cHRM Blue Y")); } + +void PNGAPI +png_set_cHRM_XYZ(png_structp png_ptr, png_infop info_ptr, double red_X, + double red_Y, double red_Z, double green_X, double green_Y, double green_Z, + double blue_X, double blue_Y, double blue_Z) +{ + png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, red_X, "cHRM Red X"), + png_fixed(png_ptr, red_Y, "cHRM Red Y"), + png_fixed(png_ptr, red_Z, "cHRM Red Z"), + png_fixed(png_ptr, green_X, "cHRM Red X"), + png_fixed(png_ptr, green_Y, "cHRM Red Y"), + png_fixed(png_ptr, green_Z, "cHRM Red Z"), + png_fixed(png_ptr, blue_X, "cHRM Red X"), + png_fixed(png_ptr, blue_Y, "cHRM Red Y"), + png_fixed(png_ptr, blue_Z, "cHRM Red Z")); +} # endif /* PNG_FLOATING_POINT_SUPPORTED */ #endif /* PNG_cHRM_SUPPORTED */ @@ -99,7 +149,7 @@ png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point * 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 + * 6250.0, which are truly ridiculous gammma values (and will produce * displays that are all black or all white.) */ if (file_gamma < 16 || file_gamma > 625000000) @@ -552,10 +602,10 @@ png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, # ifdef PNG_cHRM_SUPPORTED png_set_cHRM_fixed(png_ptr, info_ptr, /* color x y */ - /* white */ 31270L, 32900L, - /* red */ 64000L, 33000L, - /* green */ 30000L, 60000L, - /* blue */ 15000L, 6000L + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000 ); # endif /* cHRM */ } @@ -570,7 +620,7 @@ png_set_iCCP(png_structp png_ptr, png_infop info_ptr, { png_charp new_iccp_name; png_bytep new_iccp_profile; - png_uint_32 length; + png_size_t length; png_debug1(1, "in %s storage function", "iCCP"); @@ -916,10 +966,10 @@ png_set_sPLT(png_structp png_ptr, { png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; png_const_sPLT_tp from = entries + i; - png_uint_32 length; + png_size_t length; length = png_strlen(from->name) + 1; - to->name = (png_charp)png_malloc_warn(png_ptr, (png_size_t)length); + to->name = (png_charp)png_malloc_warn(png_ptr, length); if (to->name == NULL) { @@ -930,7 +980,7 @@ png_set_sPLT(png_structp png_ptr, png_memcpy(to->name, from->name, length); to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, - (png_size_t)(from->nentries * png_sizeof(png_sPLT_entry))); + from->nentries * png_sizeof(png_sPLT_entry)); if (to->entries == NULL) { diff --git a/pngstruct.h b/pngstruct.h index 93b2b3eb0..b4e5f3b60 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -5,7 +5,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.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer @@ -255,13 +255,20 @@ struct png_struct_def png_bytep chunk_list; #endif +#ifdef PNG_READ_sRGB_SUPPORTED + /* Added in 1.5.5 to record an sRGB chunk in the png. */ + png_byte is_sRGB; +#endif + /* New members added in libpng-1.0.3 */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED png_byte rgb_to_gray_status; + /* Added in libpng 1.5.5 to record setting of coefficients: */ + png_byte rgb_to_gray_coefficients_set; /* 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; + /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */ #endif /* New member added in libpng-1.0.4 (renamed in 1.0.9) */ @@ -1794,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_4 Your_png_h_is_not_version_1_5_4; +typedef png_libpng_version_1_5_5beta08 Your_png_h_is_not_version_1_5_5beta08; diff --git a/pngvalid.c b/pngvalid.c index 6d1736c15..9d1ce798c 100644 --- a/pngvalid.c +++ b/pngvalid.c @@ -1,7 +1,7 @@ /* pngvalid.c - validate libpng by constructing then reading png files. * - * Last changed in libpng 1.5.4 [July 7, 2011] + * Last changed in libpng 1.5.5 [(PENDING RELEASE)] * Copyright (c) 2011 Glenn Randers-Pehrson * Written by John Cunningham Bowler * @@ -53,8 +53,9 @@ typedef png_byte *png_const_bytep; #define PNG_READ_16BIT_SUPPORTED /* This comes from pnglibconf.h afer 1.5: */ +#define PNG_FP_1 100000 #define PNG_GAMMA_THRESHOLD_FIXED\ - ((png_fixed_point)(PNG_GAMMA_THRESHOLD * 100000)) + ((png_fixed_point)(PNG_GAMMA_THRESHOLD * PNG_FP_1)) #endif #include "zlib.h" /* For crc32 */ @@ -77,6 +78,15 @@ typedef png_byte *png_const_bytep; /***************************** EXCEPTION HANDLING *****************************/ #include "contrib/visupng/cexcept.h" + +#ifdef __cplusplus +# define this not_the_cpp_this +# define new not_the_cpp_new +# define voidcast(type, value) static_cast<type>(value) +#else +# define voidcast(type, value) (value) +#endif /* __cplusplus */ + struct png_store; define_exception_type(struct png_store*); @@ -129,10 +139,18 @@ 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 + "grayscale", invalid, "truecolour", "indexed-colour", + "grayscale with alpha", invalid, "truecolour with alpha", invalid }; +/* Convert a double precision value to fixed point. */ +static png_fixed_point +fix(double d) +{ + d = floor(d * PNG_FP_1 + .5); + return (png_fixed_point)d; +} + /* 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 @@ -143,7 +161,7 @@ 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; + png_bytep bytes = voidcast(png_bytep, pv); /* There are thirty three bits, the next bit in the sequence is bit-33 XOR * bit-20. The top 1 bit is in u1, the bottom 32 are in u0. @@ -179,6 +197,26 @@ randomize(void *pv, size_t size) #define RANDOMIZE(this) randomize(&(this), sizeof (this)) +static unsigned int +random_mod(unsigned int max) +{ + unsigned int x; + + RANDOMIZE(x); + + return x % max; /* 0 .. max-1 */ +} + +static int +random_choice(void) +{ + unsigned char x; + + RANDOMIZE(x); + + return x & 1; +} + /* 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 @@ -631,7 +669,7 @@ store_storenew(png_store *ps) if (ps->writepos != STORE_BUFFER_SIZE) png_error(ps->pwrite, "invalid store call"); - pb = malloc(sizeof *pb); + pb = voidcast(png_store_buffer*, malloc(sizeof *pb)); if (pb == NULL) png_error(ps->pwrite, "store new: OOM"); @@ -667,7 +705,7 @@ store_freefile(png_store_file **ppf) static void store_storefile(png_store *ps, png_uint_32 id) { - png_store_file *pf = malloc(sizeof *pf); + png_store_file *pf = voidcast(png_store_file*, malloc(sizeof *pf)); if (pf == NULL) png_error(ps->pwrite, "storefile: OOM"); safecat(pf->name, sizeof pf->name, 0, ps->wname); @@ -761,7 +799,7 @@ store_log(png_store* ps, png_structp pp, png_const_charp message, int is_error) static void store_error(png_structp pp, png_const_charp message) /* PNG_NORETURN */ { - png_store *ps = png_get_error_ptr(pp); + png_store *ps = voidcast(png_store*, png_get_error_ptr(pp)); if (!ps->expect_error) store_log(ps, pp, message, 1 /* error */); @@ -776,7 +814,7 @@ store_error(png_structp pp, png_const_charp message) /* PNG_NORETURN */ static void store_warning(png_structp pp, png_const_charp message) { - png_store *ps = png_get_error_ptr(pp); + png_store *ps = voidcast(png_store*, png_get_error_ptr(pp)); if (!ps->expect_warning) store_log(ps, pp, message, 0 /* warning */); @@ -838,7 +876,7 @@ store_ensure_image(png_store *ps, png_structp pp, int nImages, png_size_t cbRow, store_image_free(ps, pp); /* The buffer is deliberately mis-aligned. */ - image = malloc(cb+2); + image = voidcast(png_bytep, malloc(cb+2)); if (image == NULL) { /* Called from the startup - ignore the error for the moment. */ @@ -922,7 +960,7 @@ store_image_check(PNG_CONST png_store* ps, png_structp pp, int iImage) static void store_write(png_structp pp, png_bytep pb, png_size_t st) { - png_store *ps = png_get_io_ptr(pp); + png_store *ps = voidcast(png_store*, png_get_io_ptr(pp)); if (ps->pwrite != pp) png_error(pp, "store state damaged"); @@ -1042,7 +1080,7 @@ store_read_imp(png_store *ps, png_bytep pb, png_size_t st) static void store_read(png_structp pp, png_bytep pb, png_size_t st) { - png_store *ps = png_get_io_ptr(pp); + png_store *ps = voidcast(png_store*, png_get_io_ptr(pp)); if (ps == NULL || ps->pread != pp) png_error(pp, "bad store read call"); @@ -1082,7 +1120,8 @@ store_write_palette(png_store *ps, int npalette) /* This function can only return NULL if called with '0'! */ if (npalette > 0) { - ps->palette = malloc(npalette * sizeof *ps->palette); + ps->palette = voidcast(store_palette_entry*, malloc(npalette * + sizeof *ps->palette)); if (ps->palette == NULL) png_error(ps->pwrite, "store new palette: OOM"); @@ -1229,8 +1268,9 @@ store_pool_delete(png_store *ps, store_pool *pool) 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)); + store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp)); + store_memory *new = voidcast(store_memory*, malloc(cb + (sizeof *new) + + (sizeof pool->mark))); if (new != NULL) { @@ -1262,8 +1302,8 @@ store_malloc(png_structp pp, png_alloc_size_t cb) static void store_free(png_structp pp, png_voidp memory) { - store_pool *pool = png_get_mem_ptr(pp); - store_memory *this = memory, **test; + store_pool *pool = voidcast(store_pool*, png_get_mem_ptr(pp)); + store_memory *this = voidcast(store_memory*, 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. @@ -1483,17 +1523,125 @@ store_delete(png_store *ps) * png_store. There is a special read function, set_modifier_for_read, which * replaces set_store_for_read. */ +typedef enum modifier_state +{ + modifier_start, /* Initial value */ + modifier_signature, /* Have a signature */ + modifier_IHDR /* Have an IHDR */ +} modifier_state; + +typedef struct CIE_color +{ + /* A single CIE tristimulus value, representing the unique response of a + * standard observer to a variety of light spectra. The observer recognizes + * all spectra that produce this response as the same color, therefore this + * is effectively a description of a color. + */ + double X, Y, Z; +} CIE_color; + +static double +chromaticity_x(CIE_color c) +{ + return c.X / (c.X + c.Y + c.Z); +} + +static double +chromaticity_y(CIE_color c) +{ + return c.Y / (c.X + c.Y + c.Z); +} + +typedef struct color_encoding +{ + /* A description of an (R,G,B) encoding of color (as defined above); this + * includes the actual colors of the (R,G,B) triples (1,0,0), (0,1,0) and + * (0,0,1) plus an encoding value that is used to encode the linear + * components R, G and B to give the actual values R^gamma, G^gamma and + * B^gamma that are stored. + */ + double gamma; /* Encoding (file) gamma of space */ + CIE_color red, green, blue; /* End points */ +} color_encoding; + +static CIE_color +white_point(PNG_CONST color_encoding *encoding) +{ + CIE_color white; + + white.X = encoding->red.X + encoding->green.X + encoding->blue.X; + white.Y = encoding->red.Y + encoding->green.Y + encoding->blue.Y; + white.Z = encoding->red.Z + encoding->green.Z + encoding->blue.Z; + + return white; +} + +static void +normalize_color_encoding(color_encoding *encoding) +{ + PNG_CONST double whiteY = encoding->red.Y + encoding->green.Y + + encoding->blue.Y; + + if (whiteY != 1) + { + encoding->red.X /= whiteY; + encoding->red.Y /= whiteY; + encoding->red.Z /= whiteY; + encoding->green.X /= whiteY; + encoding->green.Y /= whiteY; + encoding->green.Z /= whiteY; + encoding->blue.X /= whiteY; + encoding->blue.Y /= whiteY; + encoding->blue.Z /= whiteY; + } +} + +static size_t +safecat_color_encoding(char *buffer, size_t bufsize, size_t pos, + PNG_CONST color_encoding *e, double encoding_gamma) +{ + if (e != 0) + { + if (encoding_gamma != 0) + pos = safecat(buffer, bufsize, pos, "("); + pos = safecat(buffer, bufsize, pos, "R("); + pos = safecatd(buffer, bufsize, pos, e->red.X, 4); + pos = safecat(buffer, bufsize, pos, ","); + pos = safecatd(buffer, bufsize, pos, e->red.Y, 4); + pos = safecat(buffer, bufsize, pos, ","); + pos = safecatd(buffer, bufsize, pos, e->red.Z, 4); + pos = safecat(buffer, bufsize, pos, "),G("); + pos = safecatd(buffer, bufsize, pos, e->green.X, 4); + pos = safecat(buffer, bufsize, pos, ","); + pos = safecatd(buffer, bufsize, pos, e->green.Y, 4); + pos = safecat(buffer, bufsize, pos, ","); + pos = safecatd(buffer, bufsize, pos, e->green.Z, 4); + pos = safecat(buffer, bufsize, pos, "),B("); + pos = safecatd(buffer, bufsize, pos, e->blue.X, 4); + pos = safecat(buffer, bufsize, pos, ","); + pos = safecatd(buffer, bufsize, pos, e->blue.Y, 4); + pos = safecat(buffer, bufsize, pos, ","); + pos = safecatd(buffer, bufsize, pos, e->blue.Z, 4); + pos = safecat(buffer, bufsize, pos, ")"); + if (encoding_gamma != 0) + pos = safecat(buffer, bufsize, pos, ")"); + } + + if (encoding_gamma != 0) + { + pos = safecat(buffer, bufsize, pos, "^"); + pos = safecatd(buffer, bufsize, pos, encoding_gamma, 5); + } + + return pos; +} + 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 */ + modifier_state state; /* My state */ /* Information from IHDR: */ png_byte bit_depth; /* From IHDR */ @@ -1506,8 +1654,22 @@ typedef struct png_modifier png_uint_32 pending_chunk; /* Test values */ - double *gammas; - unsigned int ngammas; + double *gammas; + unsigned int ngammas; + unsigned int ngamma_tests; /* Number of gamma tests to run*/ + double current_gamma; /* 0 if not set */ + PNG_CONST color_encoding *encodings; + unsigned int nencodings; + PNG_CONST color_encoding *current_encoding; /* If an encoding has been set */ + unsigned int encoding_counter; /* For iteration */ + int encoding_ignored; /* Something overwrote it */ + + /* Control variables used to iterate through possible encodings, the + * following must be set to 0 and tested by the function that uses the + * png_modifier because the modifier only sets it to 1 (true.) + */ + unsigned int repeat :1; /* Repeat this transform test. */ + unsigned int test_uses_encoding :1; /* Lowest sbit to test (libpng fails for sbit < 8) */ png_byte sbitlow; @@ -1524,6 +1686,13 @@ typedef struct png_modifier double maxcalc16;/* Absolute sample error 0..1 */ double maxpc16; /* Percentage sample error 0..100% */ + /* This is set by transforms that need to allow a higher limit, it is an + * internal check on pngvalid to ensure that the calculated error limits are + * not ridiculous; without this it is too easy to make a mistake in pngvalid + * that allows any value through. + */ + double limit; /* limit on error values, normally 4E-3 */ + /* Log limits - values above this are logged, but not necessarily * warned. */ @@ -1575,6 +1744,7 @@ typedef struct png_modifier unsigned int test_gamma_background :1; unsigned int test_gamma_alpha_mode :1; unsigned int test_gamma_expand16 :1; + unsigned int test_exhaustive :1; unsigned int log :1; /* Log max error */ @@ -1605,9 +1775,19 @@ modifier_init(png_modifier *pm) pm->state = modifier_start; pm->sbitlow = 1U; pm->ngammas = 0; + pm->ngamma_tests = 0; pm->gammas = 0; + pm->current_gamma = 0; + pm->encodings = 0; + pm->nencodings = 0; + pm->current_encoding = 0; + pm->encoding_counter = 0; + pm->encoding_ignored = 0; + pm->repeat = 0; + pm->test_uses_encoding = 0; pm->maxout8 = pm->maxpc8 = pm->maxabs8 = pm->maxcalc8 = 0; pm->maxout16 = pm->maxpc16 = pm->maxabs16 = pm->maxcalc16 = 0; + pm->limit = 4E-3; pm->log8 = pm->log16 = 0; /* Means 'off' */ pm->error_gray_2 = pm->error_gray_4 = pm->error_gray_8 = 0; pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0; @@ -1627,6 +1807,7 @@ modifier_init(png_modifier *pm) pm->test_gamma_background = 0; pm->test_gamma_alpha_mode = 0; pm->test_gamma_expand16 = 0; + pm->test_exhaustive = 0; pm->log = 0; /* Rely on the memset for all the other fields - there are no pointers */ @@ -1638,9 +1819,35 @@ modifier_init(png_modifier *pm) * * 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! + * to a calculation - not a digitization operation - unless the following API is + * called directly. */ -static double abserr(png_modifier *pm, int in_depth, int out_depth) +static double digitize(PNG_CONST png_modifier *pm, double value, + int sample_depth, int do_round) +{ + /* 'value' is in the range 0 to 1, the result is the same value rounded to a + * multiple of the digitization factor - 8 or 16 bits depending on both the + * sample depth and the 'assume' setting. Digitization is normally by + * rounding and 'do_round' should be 1, if it is 0 the digitized value will + * be truncated. + */ + PNG_CONST unsigned int digitization_factor = + (pm->assume_16_bit_calculations || sample_depth == 16) ? 65535 : 255; + + /* Limiting the range is done as a convenience to the caller - it's easier to + * do it once here than every time at the call site. + */ + if (value <= 0) + value = 0; + else if (value >= 1) + value = 1; + + value *= digitization_factor; + if (do_round) value += .5; + return floor(value)/digitization_factor; +} + +static double abserr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { /* Absolute error permitted in linear values - affected by the bit depth of * the calculations. @@ -1652,7 +1859,7 @@ static double abserr(png_modifier *pm, int in_depth, int out_depth) return pm->maxabs8; } -static double calcerr(png_modifier *pm, int in_depth, int out_depth) +static double calcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { /* Error in the linear composition arithmetic - only relevant when * composition actually happens (0 < alpha < 1). @@ -1664,7 +1871,7 @@ static double calcerr(png_modifier *pm, int in_depth, int out_depth) return pm->maxcalc8; } -static double pcerr(png_modifier *pm, int in_depth, int out_depth) +static double pcerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { /* Percentage error permitted in the linear values. Note that the specified * value is a percentage but this routine returns a simple number. @@ -1687,11 +1894,11 @@ static double pcerr(png_modifier *pm, int in_depth, int out_depth) * 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) +static double outerr(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { /* There is a serious error in the 2 and 4 bit grayscale transform because * the gamma table value (8 bits) is simply shifted, not rounded, so the - * error in 4 bit greyscale gamma is up to the value below. This is a hack + * error in 4 bit grayscale gamma is up to the value below. This is a hack * to allow pngvalid to succeed: * * TODO: fix this in libpng @@ -1720,7 +1927,7 @@ static double outerr(png_modifier *pm, int in_depth, int out_depth) * 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) +static double outlog(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { /* The command line parameters are either 8 bit (0..255) or 16 bit (0..65535) * and so must be adjusted for low bit depth grayscale: @@ -1759,7 +1966,7 @@ static double outlog(png_modifier *pm, int in_depth, int out_depth) * 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, +static int output_quantization_factor(PNG_CONST png_modifier *pm, int in_depth, int out_depth) { if (out_depth == 16 && in_depth != 16 @@ -1797,7 +2004,8 @@ typedef struct png_modification unsigned int removed :1; /* Chunk was removed */ } png_modification; -static void modification_reset(png_modification *pmm) +static void +modification_reset(png_modification *pmm) { if (pmm != NULL) { @@ -1820,14 +2028,174 @@ modification_init(png_modification *pmm) } static void +modifier_current_encoding(PNG_CONST png_modifier *pm, color_encoding *ce) +{ + if (pm->current_encoding != 0) + *ce = *pm->current_encoding; + + else + memset(ce, 0, sizeof *ce); + + ce->gamma = pm->current_gamma; +} + +static size_t +safecat_current_encoding(char *buffer, size_t bufsize, size_t pos, + PNG_CONST png_modifier *pm) +{ + pos = safecat_color_encoding(buffer, bufsize, pos, pm->current_encoding, + pm->current_gamma); + + if (pm->encoding_ignored) + pos = safecat(buffer, bufsize, pos, "[overridden]"); + + return pos; +} + +/* Iterate through the usefully testable color encodings. An encoding is one + * of: + * + * 1) Nothing (no color space, no gamma). + * 2) Just a gamma value from the gamma array (including 1.0) + * 3) A color space from the encodings array with the corresponding gamma. + * 4) The same, but with gamma 1.0 (only really useful with 16 bit calculations) + * + * The iterator selects these in turn, the randomizer selects one at random, + * which is used depends on the setting of the 'test_exhaustive' flag. Notice + * that this function changes the colour space encoding so it must only be + * called on completion of the previous test. This is what 'modifier_reset' + * does, below. + * + * After the function has been called the 'repeat' flag will still be set; the + * caller of modifier_reset must reset it at the start of each run of the test! + */ +static unsigned int +modifier_total_encodings(PNG_CONST png_modifier *pm) +{ + return 1 + /* (1) nothing */ + pm->ngammas + /* (2) gamma values to test */ + pm->nencodings + /* (3) total number of encodings */ + /* The following test only works after the first time through the + * png_modifier code because 'bit_depth' is set when the IHDR is read. + * modifier_reset, below, preserves the setting until after it has called + * the iterate function (also below.) + * + * For this reason do not rely on this function outside a call to + * modifier_reset. + */ + ((pm->bit_depth == 16 || pm->assume_16_bit_calculations) ? + pm->nencodings : 0); /* (4) encodings with gamma == 1.0 */ +} + +static void +modifier_encoding_iterate(png_modifier *pm) +{ + if (!pm->repeat && /* Else something needs the current encoding again. */ + pm->test_uses_encoding) /* Some transform is encoding dependent */ + { + if (pm->test_exhaustive) + { + if (++pm->encoding_counter >= modifier_total_encodings(pm)) + pm->encoding_counter = 0; /* This will stop the repeat */ + } + + else + { + /* Not exhaustive - choose an encoding at random; generate a number in + * the range 1..(max-1), so the result is always non-zero: + */ + if (pm->encoding_counter == 0) + pm->encoding_counter = random_mod(modifier_total_encodings(pm)-1)+1; + else + pm->encoding_counter = 0; + } + + if (pm->encoding_counter > 0) + pm->repeat = 1; + } + + else if (!pm->repeat) + pm->encoding_counter = 0; +} + +static void modifier_reset(png_modifier *pm) { store_read_reset(&pm->this); + pm->limit = 4E-3; + pm->pending_len = pm->pending_chunk = 0; + pm->flush = pm->buffer_count = pm->buffer_position = 0; pm->modifications = NULL; pm->state = modifier_start; + modifier_encoding_iterate(pm); + /* The following must be set in the next run. In particular + * test_uses_encodings must be set in the _ini function of each transform + * that looks at the encodings. (Not the 'add' function!) + */ + pm->test_uses_encoding = 0; + pm->current_gamma = 0; + pm->current_encoding = 0; + pm->encoding_ignored = 0; + /* These only become value after IHDR is read: */ pm->bit_depth = pm->colour_type = 0; - pm->pending_len = pm->pending_chunk = 0; - pm->flush = pm->buffer_count = pm->buffer_position = 0; +} + +/* The following must be called before anything else to get the encoding set up + * on the modifier. In particular it must be called before the transform init + * functions are called. + */ +static void +modifier_set_encoding(png_modifier *pm) +{ + /* Set the encoding to the one specified by the current encoding counter, + * first clear out all the settings - this corresponds to an encoding_counter + * of 0. + */ + pm->current_gamma = 0; + pm->current_encoding = 0; + pm->encoding_ignored = 0; /* not ignored yet - happens in _ini functions. */ + + /* Now, if required, set the gamma and encoding fields. */ + if (pm->encoding_counter > 0) + { + /* The gammas[] array is an array of screen gammas, not encoding gammas, + * so we need the inverse: + */ + if (pm->encoding_counter <= pm->ngammas) + pm->current_gamma = 1/pm->gammas[pm->encoding_counter-1]; + + else + { + unsigned int i = pm->encoding_counter - pm->ngammas; + + if (i >= pm->nencodings) + { + i %= pm->nencodings; + pm->current_gamma = 1; /* Linear, only in the 16 bit case */ + } + + else + pm->current_gamma = pm->encodings[i].gamma; + + pm->current_encoding = pm->encodings + i; + } + } +} + +/* Enquiry functions to find out what is set. Notice that there is an implicit + * assumption below that the first encoding in the list is the one for sRGB. + */ +static int +modifier_color_encoding_is_sRGB(PNG_CONST png_modifier *pm) +{ + return pm->current_encoding != 0 && pm->current_encoding == pm->encodings && + pm->current_encoding->gamma == pm->current_gamma; +} + +static int +modifier_color_encoding_is_set(PNG_CONST png_modifier *pm) +{ + return pm->current_gamma != 0; } /* Convenience macros. */ @@ -1849,7 +2217,10 @@ modifier_crc(png_bytep buffer) * 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)); + uLong crc = crc32(0, buffer+4, datalen+4); + /* The cast to png_uint_32 is safe because a crc32 is always a 32 bit value. + */ + png_save_uint_32(buffer+datalen+8, (png_uint_32)crc); } static void @@ -2072,7 +2443,7 @@ modifier_read_imp(png_modifier *pm, png_bytep pb, png_size_t st) static void modifier_read(png_structp pp, png_bytep pb, png_size_t st) { - png_modifier *pm = png_get_io_ptr(pp); + png_modifier *pm = voidcast(png_modifier*, png_get_io_ptr(pp)); if (pm == NULL || pm->this.pread != pp) png_error(pp, "bad modifier_read call"); @@ -2091,7 +2462,7 @@ modifier_progressive_read(png_modifier *pm, png_structp pp, png_infop pi) 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 + * the aim is to stress the progressive reader with truly horrible variable * buffer sizes in the range 1..500, so a sequence of 9 bit random numbers * is generated. We could probably just count from 1 to 32767 and get as * good a result. @@ -2158,6 +2529,205 @@ set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id, return set_store_for_read(&pm->this, ppi, id, name); } + + +/******************************** MODIFICATIONS *******************************/ +/* Standard modifications to add chunks. These do not require the _SUPPORTED + * macros because the chunks can be there regardless of whether this specific + * libpng supports them. + */ +typedef struct gama_modification +{ + png_modification this; + png_fixed_point gamma; +} gama_modification; + +static int +gama_modify(png_modifier *pm, png_modification *me, int add) +{ + UNUSED(add) + /* This simply dumps the given gamma value into the buffer. */ + png_save_uint_32(pm->buffer, 4); + png_save_uint_32(pm->buffer+4, CHUNK_gAMA); + png_save_uint_32(pm->buffer+8, ((gama_modification*)me)->gamma); + return 1; +} + +static void +gama_modification_init(gama_modification *me, png_modifier *pm, double gammad) +{ + double g; + + modification_init(&me->this); + me->this.chunk = CHUNK_gAMA; + me->this.modify_fn = gama_modify; + me->this.add = CHUNK_PLTE; + g = fix(gammad); + me->gamma = (png_fixed_point)g; + me->this.next = pm->modifications; + pm->modifications = &me->this; +} + +typedef struct chrm_modification +{ + png_modification this; + PNG_CONST color_encoding *encoding; + png_fixed_point wx, wy, rx, ry, gx, gy, bx, by; +} chrm_modification; + +static int +chrm_modify(png_modifier *pm, png_modification *me, int add) +{ + UNUSED(add) + /* As with gAMA this just adds the required cHRM chunk to the buffer. */ + png_save_uint_32(pm->buffer , 32); + png_save_uint_32(pm->buffer+ 4, CHUNK_cHRM); + png_save_uint_32(pm->buffer+ 8, ((chrm_modification*)me)->wx); + png_save_uint_32(pm->buffer+12, ((chrm_modification*)me)->wy); + png_save_uint_32(pm->buffer+16, ((chrm_modification*)me)->rx); + png_save_uint_32(pm->buffer+20, ((chrm_modification*)me)->ry); + png_save_uint_32(pm->buffer+24, ((chrm_modification*)me)->gx); + png_save_uint_32(pm->buffer+28, ((chrm_modification*)me)->gy); + png_save_uint_32(pm->buffer+32, ((chrm_modification*)me)->bx); + png_save_uint_32(pm->buffer+36, ((chrm_modification*)me)->by); + return 1; +} + +static void +chrm_modification_init(chrm_modification *me, png_modifier *pm, + PNG_CONST color_encoding *encoding) +{ + CIE_color white = white_point(encoding); + + /* Original end points: */ + me->encoding = encoding; + + /* Chromaticities (in fixed point): */ + me->wx = fix(chromaticity_x(white)); + me->wy = fix(chromaticity_y(white)); + + me->rx = fix(chromaticity_x(encoding->red)); + me->ry = fix(chromaticity_y(encoding->red)); + me->gx = fix(chromaticity_x(encoding->green)); + me->gy = fix(chromaticity_y(encoding->green)); + me->bx = fix(chromaticity_x(encoding->blue)); + me->by = fix(chromaticity_y(encoding->blue)); + + modification_init(&me->this); + me->this.chunk = CHUNK_cHRM; + me->this.modify_fn = chrm_modify; + me->this.add = CHUNK_PLTE; + me->this.next = pm->modifications; + pm->modifications = &me->this; +} + +typedef struct srgb_modification +{ + png_modification this; + png_byte intent; +} srgb_modification; + +static int +srgb_modify(png_modifier *pm, png_modification *me, int add) +{ + UNUSED(add) + /* As above, ignore add and just make a new chunk */ + png_save_uint_32(pm->buffer, 1); + png_save_uint_32(pm->buffer+4, CHUNK_sRGB); + pm->buffer[8] = ((srgb_modification*)me)->intent; + return 1; +} + +static void +srgb_modification_init(srgb_modification *me, png_modifier *pm, png_byte intent) +{ + modification_init(&me->this); + me->this.chunk = CHUNK_sBIT; + + if (intent <= 3) /* if valid, else *delete* sRGB chunks */ + { + me->this.modify_fn = srgb_modify; + me->this.add = CHUNK_PLTE; + me->intent = intent; + } + + else + { + me->this.modify_fn = 0; + me->this.add = 0; + me->intent = 0; + } + + me->this.next = pm->modifications; + pm->modifications = &me->this; +} + +typedef struct sbit_modification +{ + png_modification this; + png_byte sbit; +} sbit_modification; + +static int +sbit_modify(png_modifier *pm, png_modification *me, int add) +{ + png_byte sbit = ((sbit_modification*)me)->sbit; + if (pm->bit_depth > sbit) + { + int cb = 0; + switch (pm->colour_type) + { + case 0: + cb = 1; + break; + + case 2: + case 3: + cb = 3; + break; + + case 4: + cb = 2; + break; + + case 6: + cb = 4; + break; + + default: + png_error(pm->this.pread, + "unexpected colour type in sBIT modification"); + } + + png_save_uint_32(pm->buffer, cb); + png_save_uint_32(pm->buffer+4, CHUNK_sBIT); + + while (cb > 0) + (pm->buffer+8)[--cb] = sbit; + + return 1; + } + else if (!add) + { + /* Remove the sBIT chunk */ + pm->buffer_count = pm->buffer_position = 0; + return 1; + } + else + return 0; /* do nothing */ +} + +static void +sbit_modification_init(sbit_modification *me, png_modifier *pm, png_byte sbit) +{ + modification_init(&me->this); + me->this.chunk = CHUNK_sBIT; + me->this.modify_fn = sbit_modify; + me->this.add = CHUNK_PLTE; + me->sbit = sbit; + me->this.next = pm->modifications; + pm->modifications = &me->this; +} #endif /* PNG_READ_TRANSFORMS_SUPPORTED */ /***************************** STANDARD PNG FILES *****************************/ @@ -2224,7 +2794,7 @@ make_standard_palette(png_store* ps, int npalette, int do_tRNS) values[i][3] = (i&4) ? 255 : 0; } - /* Then add 62 greys (one quarter of the remaining 256 slots). */ + /* Then add 62 grays (one quarter of the remaining 256 slots). */ { int j = 0; png_byte random_bytes[4]; @@ -2255,7 +2825,7 @@ make_standard_palette(png_store* ps, int npalette, int do_tRNS) } /* 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, + * already have, chance is less than 1/65536. Don't worry about grays, * chance is the same, so we get a duplicate or extra gray less than 1 time * in 170. */ @@ -3323,6 +3893,8 @@ static void standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id, int do_interlace) { + memset(dp, 0, sizeof *dp); + dp->ps = ps; dp->colour_type = COL_FROM_ID(id); dp->bit_depth = DEPTH_FROM_ID(id); @@ -3429,8 +4001,8 @@ read_palette(store_palette palette, int *npalette, png_structp pp, png_infop pi) 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 + * far as 1.5.4) does not remove the trans_alpha pointer, only num_trans, + * so in the above call we get a success, we get a pointer (who knows what * to) and we get num_trans == 0: */ !(trans_alpha != NULL && num == 0)) /* TODO: fix this in libpng. */ @@ -3711,7 +4283,8 @@ standard_info_imp(standard_display *dp, png_structp pp, png_infop pi, static void standard_info(png_structp pp, png_infop pi) { - standard_display *dp = png_get_progressive_ptr(pp); + standard_display *dp = voidcast(standard_display*, + png_get_progressive_ptr(pp)); /* Call with nImages==1 because the progressive reader can only produce one * image. @@ -3722,7 +4295,8 @@ standard_info(png_structp pp, png_infop pi) 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); + PNG_CONST standard_display *dp = voidcast(standard_display*, + png_get_progressive_ptr(pp)); /* When handling interlacing some rows will be absent in each pass, the * callback still gets called, but with a NULL pointer. This is checked @@ -3897,7 +4471,8 @@ standard_image_validate(standard_display *dp, png_structp pp, int iImage, static void standard_end(png_structp pp, png_infop pi) { - standard_display *dp = png_get_progressive_ptr(pp); + standard_display *dp = voidcast(standard_display*, + png_get_progressive_ptr(pp)); UNUSED(pi) @@ -4119,7 +4694,7 @@ perform_size_test(png_modifier *pm) return; /* For the moment don't do the palette test - it's a waste of time when - * compared to the greyscale test. + * compared to the grayscale test. */ #if 0 if (!test_size(pm, 3, 0, 3)) @@ -4290,7 +4865,7 @@ image_pixel_convert_PLTE(image_pixel *this) * 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) +image_pixel_add_alpha(image_pixel *this, PNG_CONST standard_display *display) { if (this->colour_type == PNG_COLOR_TYPE_PALETTE) image_pixel_convert_PLTE(this); @@ -4376,7 +4951,13 @@ typedef struct image_transform /* 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: + * First a callback to add any required modifications to the png_modifier; + * this gets called just before the modifier is set up for read. + */ + void (*ini)(PNG_CONST struct image_transform *this, + struct transform_display *that); + + /* And 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); @@ -4412,12 +4993,48 @@ typedef struct transform_display png_byte output_colour_type; png_byte output_bit_depth; - /* Variables for the individual transforms. */ - /* png_set_background */ - image_pixel background_colour; + /* Modifications (not necessarily used.) */ + gama_modification gama_mod; + chrm_modification chrm_mod; + srgb_modification srgb_mod; } transform_display; -/* Two functions to end the list: */ +/* Set sRGB, cHRM and gAMA transforms as required by the current encoding. */ +static void +transform_set_encoding(transform_display *this) +{ + /* Set up the png_modifier '_current' fields then use these to determine how + * to add appropriate chunks. + */ + png_modifier *pm = this->pm; + + modifier_set_encoding(pm); + + if (modifier_color_encoding_is_set(pm)) + { + if (modifier_color_encoding_is_sRGB(pm)) + srgb_modification_init(&this->srgb_mod, pm, PNG_sRGB_INTENT_ABSOLUTE); + + else + { + /* Set gAMA and cHRM separately. */ + gama_modification_init(&this->gama_mod, pm, pm->current_gamma); + + if (pm->current_encoding != 0) + chrm_modification_init(&this->chrm_mod, pm, pm->current_encoding); + } + } +} + +/* Three functions to end the list: */ +static void +image_transform_ini_end(PNG_CONST image_transform *this, + transform_display *that) +{ + UNUSED(this) + UNUSED(that) +} + static void image_transform_set_end(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) @@ -4513,6 +5130,7 @@ static image_transform image_transform_end = 0, /* global_use */ 0, /* local_use */ 0, /* next */ + image_transform_ini_end, image_transform_set_end, image_transform_mod_end, 0 /* never called, I want it to crash if it is! */ @@ -4525,6 +5143,8 @@ static void transform_display_init(transform_display *dp, png_modifier *pm, png_uint_32 id, PNG_CONST image_transform *transform_list) { + memset(dp, 0, sizeof dp); + /* Standard fields */ standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/); @@ -4657,25 +5277,26 @@ transform_info_imp(transform_display *dp, png_structp pp, png_infop pi) static void transform_info(png_structp pp, png_infop pi) { - transform_info_imp(png_get_progressive_ptr(pp), pp, pi); + transform_info_imp(voidcast(transform_display*, 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) + unsigned int out, png_byte sample_depth, double err, double limit, + 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.) + * internal errors in pngvalid itself. */ unsigned int max = (1U<<sample_depth)-1; double in_min = ceil((in-err)*max - digitization_error); double in_max = floor((in+err)*max + digitization_error); - if (err > 4E-3 || !(out >= in_min && out <= in_max)) + if (err > limit || !(out >= in_min && out <= in_max)) { char message[256]; size_t pos; @@ -4830,26 +5451,30 @@ transform_image_validate(transform_display *dp, png_structp pp, png_infop pi) */ 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", + out_pixel.red, sample_depth, in_pixel.rede, + dp->pm->limit + 1./(2*((1U<<in_pixel.red_sBIT)-1)), "red/gray", digitization_error); if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 && in_pixel.green != out_pixel.green) transform_range_check(pp, r, g, b, a, in_pixel.green, in_pixel.greenf, out_pixel.green, sample_depth, in_pixel.greene, - "green", digitization_error); + dp->pm->limit + 1./(2*((1U<<in_pixel.green_sBIT)-1)), "green", + digitization_error); if ((out_ct & PNG_COLOR_MASK_COLOR) != 0 && in_pixel.blue != out_pixel.blue) transform_range_check(pp, r, g, b, a, in_pixel.blue, in_pixel.bluef, - out_pixel.blue, sample_depth, in_pixel.bluee, "blue", + out_pixel.blue, sample_depth, in_pixel.bluee, + dp->pm->limit + 1./(2*((1U<<in_pixel.blue_sBIT)-1)), "blue", digitization_error); if ((out_ct & PNG_COLOR_MASK_ALPHA) != 0 && in_pixel.alpha != out_pixel.alpha) transform_range_check(pp, r, g, b, a, in_pixel.alpha, in_pixel.alphaf, out_pixel.alpha, sample_depth, in_pixel.alphae, - "alpha", digitization_error); + dp->pm->limit + 1./(2*((1U<<in_pixel.alpha_sBIT)-1)), "alpha", + digitization_error); } /* pixel (x) loop */ } /* row (y) loop */ @@ -4860,7 +5485,8 @@ transform_image_validate(transform_display *dp, png_structp pp, png_infop pi) static void transform_end(png_structp pp, png_infop pi) { - transform_display *dp = png_get_progressive_ptr(pp); + transform_display *dp = voidcast(transform_display*, + png_get_progressive_ptr(pp)); transform_image_validate(dp, pp, pi); } @@ -4868,7 +5494,7 @@ transform_end(png_structp pp, png_infop 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) + PNG_CONST image_transform* transform_listIn, PNG_CONST char * volatile name) { transform_display d; context(&pmIn->this, fault); @@ -4877,11 +5503,25 @@ transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn, Try { + size_t pos = 0; png_structp pp; png_infop pi; + char full_name[256]; + + /* Make sure the encoding fields are correct and enter the required + * modifications. + */ + transform_set_encoding(&d); + + /* Add any modifications required by the transform list. */ + d.transform_list->ini(d.transform_list, &d); + + /* Add the color space information, if any, to the name. */ + pos = safecat(full_name, sizeof full_name, pos, name); + pos = safecat_current_encoding(full_name, sizeof full_name, pos, d.pm); /* Get a png_struct for reading the image. */ - pp = set_modifier_for_read(d.pm, &pi, d.this.id, name); + pp = set_modifier_for_read(d.pm, &pi, d.this.id, full_name); standard_palette_init(&d.this); # if 0 @@ -4927,11 +5567,15 @@ transform_test(png_modifier *pmIn, PNG_CONST png_uint_32 idIn, } Catch(fault) + { modifier_reset((png_modifier*)fault); + } } /* The transforms: */ #define ITSTRUCT(name) image_transform_##name +#define ITDATA(name) image_transform_data_##name +#define image_transform_ini image_transform_default_ini #define IT(name)\ static image_transform ITSTRUCT(name) =\ {\ @@ -4941,6 +5585,7 @@ static image_transform ITSTRUCT(name) =\ 0, /*global_use*/\ 0, /*local_use*/\ 0, /*next*/\ + image_transform_ini,\ image_transform_png_set_##name##_set,\ image_transform_png_set_##name##_mod,\ image_transform_png_set_##name##_add\ @@ -4948,6 +5593,13 @@ static image_transform ITSTRUCT(name) =\ #define PT ITSTRUCT(end) /* stores the previous transform */ /* To save code: */ +static void +image_transform_default_ini(PNG_CONST image_transform *this, + transform_display *that) +{ + this->next->ini(this->next, that); +} + static int image_transform_default_add(image_transform *this, PNG_CONST image_transform **that, png_byte colour_type, png_byte bit_depth) @@ -5397,16 +6049,173 @@ IT(strip_alpha); * 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: + * The 'default' test here uses 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. + * These values are being retained for compatibility, along with the somewhat + * broken truncation calculation in the fast-and-inaccurate code path. Older + * versions of libpng will fail the accuracy tests below because they use the + * truncation algorithm everywhere. */ +#define data ITDATA(rgb_to_gray) +static struct +{ + double gamma; /* File gamma to use in processing */ + + /* The following are the parameters for png_set_rgb_to_gray: */ +# ifdef PNG_FLOATING_POINT_SUPPORTED + double red_to_set; + double green_to_set; +# else + png_fixed_point red_to_set; + png_fixed_point green_to_set; +# endif + + /* The actual coefficients: */ + double red_coefficient; + double green_coefficient; + double blue_coefficient; + + /* Set if the coeefficients have been overridden. */ + int coefficients_overridden; +} data; + +#undef image_transform_ini +#define image_transform_ini image_transform_png_set_rgb_to_gray_ini +static void +image_transform_png_set_rgb_to_gray_ini(PNG_CONST image_transform *this, + transform_display *that) +{ + png_modifier *pm = that->pm; + PNG_CONST color_encoding *e = pm->current_encoding; + + UNUSED(this) + + /* Since we check the encoding this flag must be set: */ + pm->test_uses_encoding = 1; + + /* If 'e' is not NULL chromaticity information is present and either a cHRM + * or an sRGB chunk will be inserted. + */ + if (e != 0) + { + /* Coefficients come from the encoding, but may need to be normalized to a + * white point Y of 1.0 + */ + PNG_CONST double whiteY = e->red.Y + e->green.Y + e->blue.Y; + + data.red_coefficient = e->red.Y; + data.green_coefficient = e->green.Y; + data.blue_coefficient = e->blue.Y; + + if (whiteY != 1) + { + data.red_coefficient /= whiteY; + data.green_coefficient /= whiteY; + data.blue_coefficient /= whiteY; + } + } + + else + { + /* The default (built in) coeffcients, as above: */ + data.red_coefficient = 6968 / 32768.; + data.green_coefficient = 23434 / 32768.; + data.blue_coefficient = 2366 / 32768.; + } + + data.gamma = pm->current_gamma; + + /* If not set then the calculations assume linear encoding (implicitly): */ + if (data.gamma == 0) + data.gamma = 1; + + /* The arguments to png_set_rgb_to_gray can override the coefficients implied + * by the color space encoding. If doing exhaustive checks do the override + * in each case, otherwise do it randomly. + */ + if (pm->test_exhaustive) + { + /* First time in coefficients_overridden is 0, the following sets it to 1, + * so repeat if it is set. If a test fails this may mean we subsequently + * skip a non-override test, ignore that. + */ + data.coefficients_overridden = !data.coefficients_overridden; + pm->repeat = data.coefficients_overridden != 0; + } + + else + data.coefficients_overridden = random_choice(); + + if (data.coefficients_overridden) + { + /* These values override the color encoding defaults, simply use random + * numbers. + */ + png_uint_32 ru; + double total; + + RANDOMIZE(ru); + data.green_coefficient = total = (ru & 0xffff) / 65535.; + ru >>= 16; + data.red_coefficient = (1 - total) * (ru & 0xffff) / 65535.; + total += data.red_coefficient; + data.blue_coefficient = 1 - total; + +# ifdef PNG_FLOATING_POINT_SUPPORTED + data.red_to_set = data.red_coefficient; + data.green_to_set = data.green_coefficient; +# else + data.red_to_set = fix(data.red_coefficient); + data.green_to_set = fix(data.green_coefficient); +# endif + + /* The following just changes the error messages: */ + pm->encoding_ignored = 1; + } + + else + { + data.red_to_set = -1; + data.green_to_set = -1; + } + + /* Adjust the error limit in the png_modifier because of the larger errors + * produced in the digitization during the gamma handling. + */ + if (data.gamma != 1) /* Use gamma tables */ + { + if (that->this.bit_depth == 16 || pm->assume_16_bit_calculations) + { + /* The 16 bit case ends up producing a maximum error of about + * +/-5 in 65535, allow for +/-8 with the given gamma. + */ + that->pm->limit += pow(8./65535, data.gamma); + } + + else + { + /* Rounding to 8 bits in the linear space causes massive errors which + * will trigger the error check in transform_range_check. Fix that + * here by taking the gamma encoding into account. + */ + that->pm->limit += pow(1./255, data.gamma); + } + } + + else + { + /* With no gamma correction a large error comes from the truncation of the + * calculation in the 8 bit case, allow for that here. + */ + if (that->this.bit_depth != 16) + that->pm->limit += 4E-3; + } +} + static void image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) @@ -5414,11 +6223,123 @@ image_transform_png_set_rgb_to_gray_set(PNG_CONST image_transform *this, 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); + png_set_rgb_to_gray(pp, error_action, data.red_to_set, data.green_to_set); # else - png_set_rgb_to_gray_fixed(pp, error_action, -1, -1); + png_set_rgb_to_gray_fixed(pp, error_action, data.red_to_set, + data.green_to_set); # endif +# ifdef PNG_READ_cHRM_SUPPORTED + if (that->pm->current_encoding != 0) + { + /* We have an encoding so a cHRM chunk may have been set; if so then + * check that the libpng APIs give the correct (X,Y,Z) values within + * some margin of error for the round trip through the chromaticity + * form. + */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define API_function png_get_cHRM_XYZ +# define API_form "FP" +# define API_type double +# define API_cvt(x) (x) +# else +# define API_function png_get_cHRM_XYZ_fixed +# define API_form "fixed" +# define API_type png_fixed_point +# define API_cvt(x) ((double)(x)/PNG_FP_1) +# endif + + API_type rX, gX, bX; + API_type rY, gY, bY; + API_type rZ, gZ, bZ; + + if ((API_function(pp, pi, &rX, &rY, &rZ, &gX, &gY, &gZ, &bX, &bY, &bZ) + & PNG_INFO_cHRM) != 0) + { + double maxe; + PNG_CONST char *el; + color_encoding e, o; + + /* Expect libpng to return a normalized result, but the original + * color space encoding may not be normalized. + */ + modifier_current_encoding(that->pm, &o); + normalize_color_encoding(&o); + + /* Sanity check the pngvalid code - the coefficients should match + * the normalized Y values of the encoding unless they were + * overridden. + */ + if (data.red_to_set == -1 && data.green_to_set == -1 && + (fabs(o.red.Y - data.red_coefficient) > DBL_EPSILON || + fabs(o.green.Y - data.green_coefficient) > DBL_EPSILON || + fabs(o.blue.Y - data.blue_coefficient) > DBL_EPSILON)) + png_error(pp, "internal pngvalid cHRM coefficient error"); + + /* Generate a colour space encoding. */ + e.gamma = o.gamma; /* not used */ + e.red.X = API_cvt(rX); + e.red.Y = API_cvt(rY); + e.red.Z = API_cvt(rZ); + e.green.X = API_cvt(gX); + e.green.Y = API_cvt(gY); + e.green.Z = API_cvt(gZ); + e.blue.X = API_cvt(bX); + e.blue.Y = API_cvt(bY); + e.blue.Z = API_cvt(bZ); + + /* This should match the original one from the png_modifier, within + * the range permitted by the libpng fixed point representation. + */ + maxe = 0; + el = "-"; /* Set to element name with error */ + +# define CHECK(col,x)\ + {\ + double err = fabs(o.col.x - e.col.x);\ + if (err > maxe)\ + {\ + maxe = err;\ + el = #col "(" #x ")";\ + }\ + } + + CHECK(red,X) + CHECK(red,Y) + CHECK(red,Z) + CHECK(green,X) + CHECK(green,Y) + CHECK(green,Z) + CHECK(blue,X) + CHECK(blue,Y) + CHECK(blue,Z) + + /* Here in both fixed and floating cases to check the values read + * from the cHRm chunk. PNG uses fixed point in the cHRM chunk, so + * we can't expect better than +/-.5E-5 on the result, allow 1E-5. + */ + if (maxe >= 1E-5) + { + size_t pos = 0; + char buffer[256]; + + pos = safecat(buffer, sizeof buffer, pos, API_form); + pos = safecat(buffer, sizeof buffer, pos, " cHRM "); + pos = safecat(buffer, sizeof buffer, pos, el); + pos = safecat(buffer, sizeof buffer, pos, " error: "); + pos = safecatd(buffer, sizeof buffer, pos, maxe, 7); + pos = safecat(buffer, sizeof buffer, pos, " "); + /* Print the color space without the gamma value: */ + pos = safecat_color_encoding(buffer, sizeof buffer, pos, &o, 0); + pos = safecat(buffer, sizeof buffer, pos, " -> "); + pos = safecat_color_encoding(buffer, sizeof buffer, pos, &e, 0); + + png_error(pp, buffer); + } + } + } +# endif /* READ_cHRM */ + this->next->set(this->next, that, pp, pi); } @@ -5428,15 +6349,144 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, { if ((that->colour_type & PNG_COLOR_MASK_COLOR) != 0) { + double gray, err; + 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); + { + PNG_CONST png_modifier *pm = display->pm; + PNG_CONST unsigned int sample_depth = that->sample_depth; + int isgray; + double r, g, b; + double rlo, rhi, glo, ghi, blo, bhi, graylo, grayhi; + + /* Do this using interval arithmetic, otherwise it is too difficult to + * handle the errors correctly. + * + * To handle the gamma correction work out the upper and lower bounds + * of the digitized value. Assume rounding here - normally the values + * will be identical after this operation if there is only one + * transform, feel free to delete the png_error checks on this below in + * the future (this is just me trying to ensure it works!) + */ + r = rlo = rhi = that->redf; + rlo -= that->rede; + rlo = digitize(pm, rlo, sample_depth, 1/*round*/); + rhi += that->rede; + rhi = digitize(pm, rhi, sample_depth, 1/*round*/); + + g = glo = ghi = that->greenf; + glo -= that->greene; + glo = digitize(pm, glo, sample_depth, 1/*round*/); + ghi += that->greene; + ghi = digitize(pm, ghi, sample_depth, 1/*round*/); + + b = blo = bhi = that->bluef; + blo -= that->bluee; + blo = digitize(pm, blo, sample_depth, 1/*round*/); + bhi += that->greene; + bhi = digitize(pm, bhi, sample_depth, 1/*round*/); + + isgray = r==g && g==b; + + if (data.gamma != 1) + { + PNG_CONST double power = 1/data.gamma; + PNG_CONST double abse = abserr(pm, sample_depth, sample_depth); + + /* 'abse' is the absolute error permitted in linear calculations. It + * is used here to capture the error permitted in the handling + * (undoing) of the gamma encoding. Once again digitization occurs + * to handle the upper and lower bounds of the values. This is + * where the real errors are introduced. + */ + r = pow(r, power); + rlo = digitize(pm, pow(rlo, power)-abse, sample_depth, 1); + rhi = digitize(pm, pow(rhi, power)+abse, sample_depth, 1); + + g = pow(g, power); + glo = digitize(pm, pow(glo, power)-abse, sample_depth, 1); + ghi = digitize(pm, pow(ghi, power)+abse, sample_depth, 1); + + b = pow(b, power); + blo = digitize(pm, pow(blo, power)-abse, sample_depth, 1); + bhi = digitize(pm, pow(bhi, power)+abse, sample_depth, 1); + } + + /* Now calculate the actual gray values. Although the error in the + * coefficients depends on whether they were specified on the command + * line (in which case truncation to 15 bits happened) or not (rounding + * was used) the maxium error in an individual coefficient is always + * 1/32768, because even in the rounding case the requirement that + * coefficients add up to 32768 can cause a larger rounding error. + * + * The only time when rounding doesn't occur in 1.5.5 and later is when + * the non-gamma code path is used for less than 16 bit data. + */ + gray = r * data.red_coefficient + g * data.green_coefficient + + b * data.blue_coefficient; + + { + PNG_CONST int do_round = data.gamma != 1 || sample_depth == 16; + PNG_CONST double ce = 1. / 32768; + + graylo = digitize(pm, rlo * (data.red_coefficient-ce) + + glo * (data.green_coefficient-ce) + + blo * (data.blue_coefficient-ce), sample_depth, do_round); + if (graylo <= 0) + graylo = 0; + + grayhi = digitize(pm, rhi * (data.red_coefficient+ce) + + ghi * (data.green_coefficient+ce) + + bhi * (data.blue_coefficient+ce), sample_depth, do_round); + if (grayhi >= 1) + grayhi = 1; + } + + /* And invert the gamma. */ + if (data.gamma != 1) + { + PNG_CONST double power = data.gamma; + + gray = pow(gray, power); + graylo = digitize(pm, pow(graylo, power), sample_depth, 1); + grayhi = digitize(pm, pow(grayhi, power), sample_depth, 1); + } + + /* Now the error can be calculated. + * + * If r==g==b because there is no overall gamma correction libpng + * currently preserves the original value. + */ + if (isgray) + err = (that->rede + that->greene + that->bluee)/3; + + else + { + err = fabs(grayhi-gray); + if (fabs(gray - graylo) > err) + err = fabs(graylo-gray); + + /* Check that this worked: */ + if (err > display->pm->limit) + { + size_t pos = 0; + char buffer[128]; + + pos = safecat(buffer, sizeof buffer, pos, "rgb_to_gray error "); + pos = safecatd(buffer, sizeof buffer, pos, err, 6); + pos = safecat(buffer, sizeof buffer, pos, " exceeds limit "); + pos = safecatd(buffer, sizeof buffer, pos, + display->pm->limit, 6); + png_error(pp, buffer); + } + } + } + + that->bluef = that->greenf = that->redf = gray; + that->bluee = that->greene = that->rede = err; /* The sBIT is the minium of the three colour channel sBITs. */ if (that->red_sBIT > that->green_sBIT) @@ -5445,7 +6495,7 @@ image_transform_png_set_rgb_to_gray_mod(PNG_CONST image_transform *this, that->red_sBIT = that->blue_sBIT; that->blue_sBIT = that->green_sBIT = that->red_sBIT; - /* And zap the colour bit in the type: */ + /* And remove the colour bit in the type: */ if (that->colour_type == PNG_COLOR_TYPE_RGB) that->colour_type = PNG_COLOR_TYPE_GRAY; else if (that->colour_type == PNG_COLOR_TYPE_RGB_ALPHA) @@ -5467,9 +6517,12 @@ image_transform_png_set_rgb_to_gray_add(image_transform *this, return (colour_type & PNG_COLOR_MASK_COLOR) != 0; } +#undef data IT(rgb_to_gray); #undef PT #define PT ITSTRUCT(rgb_to_gray) +#undef image_transform_ini +#define image_transform_ini image_transform_default_ini #endif /* PNG_READ_RGB_TO_GRAY_SUPPORTED */ #ifdef PNG_READ_BACKGROUND_SUPPORTED @@ -5479,8 +6532,11 @@ IT(rgb_to_gray); * int background_gamma_code, int need_expand, * png_fixed_point background_gamma) * - * As with rgb_to_gray this ignores the gamma. + * As with rgb_to_gray this ignores the gamma (at present.) */ +#define data ITDATA(background) +static image_pixel data; + static void image_transform_png_set_background_set(PNG_CONST image_transform *this, transform_display *that, png_structp pp, png_infop pi) @@ -5509,7 +6565,7 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, else bit_depth = that->this.bit_depth; - image_pixel_init(&that->background_colour, random_bytes, colour_type, + image_pixel_init(&data, random_bytes, colour_type, bit_depth, 0/*x*/, 0/*unused: palette*/); /* Extract the background colour from this image_pixel, but make sure the @@ -5519,13 +6575,13 @@ image_transform_png_set_background_set(PNG_CONST image_transform *this, 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; + back.red = (png_uint_16)data.red; + back.green = (png_uint_16)data.green; + back.blue = (png_uint_16)data.blue; } else - back.gray = (png_uint_16)that->background_colour.red; + back.gray = (png_uint_16)data.red; # ifdef PNG_FLOATING_POINT_SUPPORTED png_set_background(pp, &back, PNG_BACKGROUND_GAMMA_FILE, 1/*need expand*/, @@ -5549,36 +6605,34 @@ image_transform_png_set_background_mod(PNG_CONST image_transform *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->redf = data.redf; + that->greenf = data.greenf; + that->bluef = data.bluef; - that->rede = back->rede; - that->greene = back->greene; - that->bluee = back->bluee; + that->rede = data.rede; + that->greene = data.greene; + that->bluee = data.bluee; - that->red_sBIT= back->red_sBIT; - that->green_sBIT= back->green_sBIT; - that->blue_sBIT= back->blue_sBIT; + that->red_sBIT= data.red_sBIT; + that->green_sBIT= data.green_sBIT; + that->blue_sBIT= data.blue_sBIT; } else /* 0 < alpha < 1 */ { double alf = 1 - that->alphaf; - that->redf = that->redf * that->alphaf + back->redf * alf; - that->rede = that->rede * that->alphaf + back->rede * alf + + that->redf = that->redf * that->alphaf + data.redf * alf; + that->rede = that->rede * that->alphaf + data.rede * alf + DBL_EPSILON; - that->greenf = that->greenf * that->alphaf + back->greenf * alf; - that->greene = that->greene * that->alphaf + back->greene * alf + + that->greenf = that->greenf * that->alphaf + data.greenf * alf; + that->greene = that->greene * that->alphaf + data.greene * alf + DBL_EPSILON; - that->bluef = that->bluef * that->alphaf + back->bluef * alf; - that->bluee = that->bluee * that->alphaf + back->bluee * alf + + that->bluef = that->bluef * that->alphaf + data.bluef * alf; + that->bluee = that->bluee * that->alphaf + data.bluee * alf + DBL_EPSILON; } @@ -5598,6 +6652,7 @@ image_transform_png_set_background_mod(PNG_CONST image_transform *this, #define image_transform_png_set_background_add image_transform_default_add +#undef data IT(background); #undef PT #define PT ITSTRUCT(background) @@ -5755,7 +6810,7 @@ image_transform_add(PNG_CONST image_transform **this, unsigned int max, else { - /* Not useful and max>0, so remvoe it from *this: */ + /* Not useful and max>0, so remove it from *this: */ *this = list->next; list->next = 0; @@ -5886,11 +6941,16 @@ perform_transform_test(png_modifier *pm) 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); + do + { + pm->repeat = 0; + transform_test(pm, FILEID(colour_type, bit_depth, palette_number, + pm->interlace_type, 0, 0, 0), list, name); - if (fail(pm)) - return; + if (fail(pm)) + return; + } + while (pm->repeat); } } } @@ -5898,147 +6958,6 @@ perform_transform_test(png_modifier *pm) /********************************* 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. */ @@ -6140,7 +7059,7 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) */ PNG_CONST double sg = dp->screen_gamma; # ifndef PNG_FLOATING_POINT_SUPPORTED - PNG_CONST png_fixed_point g = (png_fixed_point)(sg*100000+.5); + PNG_CONST png_fixed_point g = fix(sg); # endif # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -6158,7 +7077,7 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) # 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_fixed_point f = fix(dp->file_gamma); png_set_gamma_fixed(pp, g, f); # endif } @@ -6175,8 +7094,8 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) 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_fixed_point s = fix(dp->screen_gamma); + png_fixed_point f = fix(dp->file_gamma); png_set_gamma_fixed(pp, s, f); } # endif @@ -6188,7 +7107,7 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) */ PNG_CONST double bg = dp->background_gamma; # ifndef PNG_FLOATING_POINT_SUPPORTED - PNG_CONST png_fixed_point g = (png_fixed_point)(bg*100000+.5); + PNG_CONST png_fixed_point g = fix(bg); # endif # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -6213,7 +7132,8 @@ gamma_info_imp(gamma_display *dp, png_structp pp, png_infop pi) static void gamma_info(png_structp pp, png_infop pi) { - gamma_info_imp(png_get_progressive_ptr(pp), pp, pi); + gamma_info_imp(voidcast(gamma_display*, png_get_progressive_ptr(pp)), pp, + pi); } /* Validate a single component value - the routine gets the input and output @@ -7107,8 +8027,8 @@ gamma_image_validate(gamma_display *dp, png_structp pp, png_infop pi) } } - /* Handle greyscale or RGB components. */ - if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* greyscale */ + /* Handle grayscale or RGB components. */ + if ((in_ct & PNG_COLOR_MASK_COLOR) == 0) /* grayscale */ (void)gamma_component_validate("gray", &vi, sample(std, in_ct, in_bd, x, 0), sample(pRow, out_ct, out_bd, x, 0), alpha/*component*/, @@ -7156,7 +8076,7 @@ gamma_image_validate(gamma_display *dp, png_structp pp, png_infop pi) static void gamma_end(png_structp pp, png_infop pi) { - gamma_display *dp = png_get_progressive_ptr(pp); + gamma_display *dp = voidcast(gamma_display*, png_get_progressive_ptr(pp)); if (!dp->this.speed) gamma_image_validate(dp, pp, pi); @@ -7192,15 +8112,20 @@ gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn, { png_structp pp; png_infop pi; - gamma_modification gamma_mod; + gama_modification gama_mod; srgb_modification srgb_mod; sbit_modification sbit_mod; + /* For the moment don't use the png_modifier support here. */ + d.pm->encoding_counter = 0; + modifier_set_encoding(d.pm); /* Just resets everything */ + d.pm->current_gamma = d.file_gamma; + /* Make an appropriate modifier to set the PNG file gamma to the * given gamma value and the sBIT chunk to the given precision. */ d.pm->modifications = NULL; - gamma_modification_init(&gamma_mod, d.pm, d.file_gamma); + gama_modification_init(&gama_mod, d.pm, d.file_gamma); srgb_modification_init(&srgb_mod, d.pm, 127 /*delete*/); if (d.sbit > 0) sbit_modification_init(&sbit_mod, d.pm, d.sbit); @@ -7410,15 +8335,16 @@ static void perform_gamma_transform_tests(png_modifier *pm) { 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*/); + for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j) + if (i != j) + { + gamma_transform_test(pm, colour_type, bit_depth, palette_number, + pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], 0/*sBIT*/, + pm->use_input_precision, 0 /*do not scale16*/); - if (fail(pm)) - return; - } + if (fail(pm)) + return; + } } } @@ -7445,11 +8371,11 @@ static void perform_gamma_sbit_tests(png_modifier *pm) { unsigned int i; - for (i=0; i<pm->ngammas; ++i) + for (i=0; i<pm->ngamma_tests; ++i) { unsigned int j; - for (j=0; j<pm->ngammas; ++j) if (i != j) + for (j=0; j<pm->ngamma_tests; ++j) if (i != j) { gamma_transform_test(pm, colour_type, bit_depth, npalette, pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], @@ -7482,9 +8408,9 @@ static void perform_gamma_scale16_tests(png_modifier *pm) * 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 (i=0; i<pm->ngamma_tests; ++i) { - for (j=0; j<pm->ngammas; ++j) + for (j=0; j<pm->ngamma_tests; ++j) { if (i != j && fabs(pm->gammas[j]/pm->gammas[i]-1) >= PNG_GAMMA_THRESHOLD) @@ -7663,7 +8589,7 @@ perform_gamma_composition_tests(png_modifier *pm, int do_background, 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) + for (i=0; i<pm->ngamma_tests; ++i) for (j=0; j<pm->ngamma_tests; ++j) { gamma_composition_test(pm, colour_type, bit_depth, palette_number, pm->interlace_type, 1/pm->gammas[i], pm->gammas[j], @@ -8179,6 +9105,41 @@ perform_interlace_macro_validation(void) } } +/* Test color encodings. These values are back-calculated from the published + * chromaticities. The values are accurate to about 14 decimal places; 15 are + * given. These values are much more accurate than the ones given in the spec, + * which typically don't exceed 4 decimal places. This allows testing of the + * libpng code to its theoretical accuracy of 4 decimal places. (If pngvalid + * used the published errors the 'slack' permitted would have to be +/-.5E-4 or + * more.) + * + * The png_modifier code assumes that encodings[0] is sRGB and treats it + * specially: do not change the first entry in this list! + */ +static PNG_CONST color_encoding test_encodings[] = +{ +/* sRGB: must be first in this list! */ +/*gamma:*/ { 1/2.2, +/*red: */ { 0.412390799265959, 0.212639005871510, 0.019330818715592 }, +/*green:*/ { 0.357584339383878, 0.715168678767756, 0.119194779794626 }, +/*blue: */ { 0.180480788401834, 0.072192315360734, 0.950532152249660} }, +/* Kodak ProPhoto (wide gamut) */ +/*gamma:*/ { 1/1.6 /*approximate: uses 1.8 power law compared to sRGB 2.4*/, +/*red: */ { 0.797760489672303, 0.288071128229293, 0.000000000000000 }, +/*green:*/ { 0.135185837175740, 0.711843217810102, 0.000000000000000 }, +/*blue: */ { 0.031349349581525, 0.000085653960605, 0.825104602510460} }, +/* Adobe RGB (1998) */ +/*gamma:*/ { 1/(2+51./256), +/*red: */ { 0.576669042910131, 0.297344975250536, 0.027031361386412 }, +/*green:*/ { 0.185558237906546, 0.627363566255466, 0.070688852535827 }, +/*blue: */ { 0.188228646234995, 0.075291458493998, 0.991337536837639} }, +/* Adobe Wide Gamut RGB */ +/*gamma:*/ { 1/(2+51./256), +/*red: */ { 0.716500716779386, 0.258728243040113, 0.000000000000000 }, +/*green:*/ { 0.101020574397477, 0.724682314948566, 0.051211818965388 }, +/*blue: */ { 0.146774385252705, 0.016589442011321, 0.773892783545073} }, +}; + /* main program */ int main(int argc, PNG_CONST char **argv) { @@ -8220,7 +9181,13 @@ int main(int argc, PNG_CONST char **argv) /* Store the test gammas */ pm.gammas = gammas; - pm.ngammas = 0; /* default to off */ + pm.ngammas = (sizeof gammas) / (sizeof gammas[0]); + pm.ngamma_tests = 0; /* default to off */ + + /* And the test encodings */ + pm.encodings = test_encodings; + pm.nencodings = (sizeof test_encodings) / (sizeof test_encodings[0]); + pm.sbitlow = 8U; /* because libpng doesn't do sBIT below 8! */ /* The following allows results to pass if they correspond to anything in the * transformed range [input-.5,input+.5]; this is is required because of the @@ -8277,8 +9244,8 @@ int main(int argc, PNG_CONST char **argv) 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; + pm.this.speed = 1, pm.ngamma_tests = pm.ngammas, pm.test_standard = 0, + summary = 0; else if (strcmp(*argv, "--memory") == 0) memstats = 1; @@ -8320,7 +9287,7 @@ int main(int argc, PNG_CONST char **argv) else if (strcmp(*argv, "--gamma") == 0) { /* Just do two gamma tests here (2.2 and linear) for speed: */ - pm.ngammas = 2U; + pm.ngamma_tests = 2U; pm.test_gamma_threshold = 1; pm.test_gamma_transform = 1; pm.test_gamma_sbit = 1; @@ -8330,40 +9297,40 @@ int main(int argc, PNG_CONST char **argv) } else if (strcmp(*argv, "--nogamma") == 0) - pm.ngammas = 0; + pm.ngamma_tests = 0; else if (strcmp(*argv, "--gamma-threshold") == 0) - pm.ngammas = 2U, pm.test_gamma_threshold = 1; + pm.ngamma_tests = 2U, pm.test_gamma_threshold = 1; else if (strcmp(*argv, "--nogamma-threshold") == 0) pm.test_gamma_threshold = 0; else if (strcmp(*argv, "--gamma-transform") == 0) - pm.ngammas = 2U, pm.test_gamma_transform = 1; + pm.ngamma_tests = 2U, pm.test_gamma_transform = 1; else if (strcmp(*argv, "--nogamma-transform") == 0) pm.test_gamma_transform = 0; else if (strcmp(*argv, "--gamma-sbit") == 0) - pm.ngammas = 2U, pm.test_gamma_sbit = 1; + pm.ngamma_tests = 2U, pm.test_gamma_sbit = 1; else if (strcmp(*argv, "--nogamma-sbit") == 0) pm.test_gamma_sbit = 0; else if (strcmp(*argv, "--gamma-16-to-8") == 0) - pm.ngammas = 2U, pm.test_gamma_scale16 = 1; + pm.ngamma_tests = 2U, pm.test_gamma_scale16 = 1; else if (strcmp(*argv, "--nogamma-16-to-8") == 0) pm.test_gamma_scale16 = 0; else if (strcmp(*argv, "--gamma-background") == 0) - pm.ngammas = 2U, pm.test_gamma_background = 1; + pm.ngamma_tests = 2U, pm.test_gamma_background = 1; else if (strcmp(*argv, "--nogamma-background") == 0) pm.test_gamma_background = 0; else if (strcmp(*argv, "--gamma-alpha-mode") == 0) - pm.ngammas = 2U, pm.test_gamma_alpha_mode = 1; + pm.ngamma_tests = 2U, pm.test_gamma_alpha_mode = 1; else if (strcmp(*argv, "--nogamma-alpha-mode") == 0) pm.test_gamma_alpha_mode = 0; @@ -8375,10 +9342,10 @@ int main(int argc, PNG_CONST char **argv) pm.test_gamma_expand16 = 0; else if (strcmp(*argv, "--more-gammas") == 0) - pm.ngammas = 3U; + pm.ngamma_tests = 3U; else if (strcmp(*argv, "--all-gammas") == 0) - pm.ngammas = (sizeof gammas)/(sizeof gammas[0]); + pm.ngamma_tests = pm.ngammas; else if (strcmp(*argv, "--progressive-read") == 0) pm.this.progressive = 1; @@ -8399,6 +9366,9 @@ int main(int argc, PNG_CONST char **argv) pm.calculations_use_input_precision = pm.assume_16_bit_calculations = 0; + else if (strcmp(*argv, "--exhaustive") == 0) + pm.test_exhaustive = 1; + else if (argc > 1 && strcmp(*argv, "--sbitlow") == 0) --argc, pm.sbitlow = (png_byte)atoi(*++argv), catmore = 1; @@ -8465,7 +9435,7 @@ int main(int argc, PNG_CONST char **argv) * tests. */ if (pm.test_standard == 0 && pm.test_size == 0 && pm.test_transform == 0 && - pm.ngammas == 0) + pm.ngamma_tests == 0) { /* Make this do all the tests done in the test shell scripts with the same * parameters, where possible. The limitation is that all the progressive @@ -8475,10 +9445,10 @@ int main(int argc, PNG_CONST char **argv) pm.test_standard = 1; pm.test_size = 1; pm.test_transform = 1; - pm.ngammas = 2U; + pm.ngamma_tests = 2U; } - if (pm.ngammas > 0 && + if (pm.ngamma_tests > 0 && pm.test_gamma_threshold == 0 && pm.test_gamma_transform == 0 && pm.test_gamma_sbit == 0 && pm.test_gamma_scale16 == 0 && pm.test_gamma_background == 0 && pm.test_gamma_alpha_mode == 0) @@ -8491,7 +9461,7 @@ int main(int argc, PNG_CONST char **argv) pm.test_gamma_alpha_mode = 1; } - else if (pm.ngammas == 0) + else if (pm.ngamma_tests == 0) { /* Nothing to test so turn everything off: */ pm.test_gamma_threshold = 0; @@ -8530,7 +9500,7 @@ int main(int argc, PNG_CONST char **argv) #endif /* PNG_READ_TRANSFORMS_SUPPORTED */ #ifdef PNG_READ_GAMMA_SUPPORTED - if (pm.ngammas > 0) + if (pm.ngamma_tests > 0) perform_gamma_test(&pm, summary); #endif } diff --git a/pngwutil.c b/pngwutil.c index 3ae5ed4e2..4e422ce68 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1860,7 +1860,7 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, { PNG_pCAL; png_size_t purpose_len, units_len, total_len; - png_uint_32p params_len; + png_size_tp params_len; png_byte buf[10]; png_charp new_purpose; int i; @@ -1876,8 +1876,8 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, png_debug1(3, "pCAL units length = %d", (int)units_len); total_len = purpose_len + units_len + 10; - params_len = (png_uint_32p)png_malloc(png_ptr, - (png_alloc_size_t)(nparams * png_sizeof(png_uint_32))); + params_len = (png_size_tp)png_malloc(png_ptr, + (png_alloc_size_t)(nparams * png_sizeof(png_size_t))); /* Find the length of each parameter, making sure we don't count the * null terminator for the last parameter. @@ -1887,13 +1887,12 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); png_debug2(3, "pCAL parameter %d length = %lu", i, (unsigned long)params_len[i]); - total_len += (png_size_t)params_len[i]; + total_len += params_len[i]; } png_debug1(3, "pCAL total length = %d", (int)total_len); png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len); - png_write_chunk_data(png_ptr, (png_const_bytep)new_purpose, - (png_size_t)purpose_len); + png_write_chunk_data(png_ptr, (png_const_bytep)new_purpose, purpose_len); png_save_int_32(buf, X0); png_save_int_32(buf + 4, X1); buf[8] = (png_byte)type; @@ -1905,8 +1904,7 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, for (i = 0; i < nparams; i++) { - png_write_chunk_data(png_ptr, (png_const_bytep)params[i], - (png_size_t)params_len[i]); + png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]); } png_free(png_ptr, params_len); |