From b11b31aea2ed879cf36332883cdd1edc80622baf Mon Sep 17 00:00:00 2001 From: John Bowler Date: Wed, 21 Mar 2012 07:55:46 -0500 Subject: [PATCH] [libpng16] Changed chunk handler warnings into benign errors, incrementally load iCCP --- ANNOUNCE | 5 +- CHANGES | 3 +- contrib/libtests/makepng.c | 11 +- contrib/libtests/pngstest.c | 15 +- png.c | 935 ++++++++++++++++++++++++---- png.h | 23 +- pngconf.h | 2 +- pngget.c | 227 +++---- pnginfo.h | 52 +- pngpriv.h | 135 +++-- pngread.c | 57 +- pngrtran.c | 63 +- pngrutil.c | 1078 ++++++++++++++++----------------- pngset.c | 110 ++-- pngstruct.h | 89 ++- pngwrite.c | 71 ++- pngwutil.c | 44 +- scripts/options.awk | 23 +- scripts/pnglibconf.dfa | 31 +- scripts/pnglibconf.h.prebuilt | 5 +- 20 files changed, 1914 insertions(+), 1065 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index 4b57d0450..d2ceaedf5 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,5 +1,5 @@ -Libpng 1.6.0beta20 - March 19, 2012 +Libpng 1.6.0beta20 - March 21, 2012 This is not intended to be a public release. It will be replaced within a few weeks by a public version or by another test version. @@ -339,7 +339,8 @@ Version 1.6.0beta19 [March 18,2012] Avoid the double gamma correction warning in the simplified API. This allows the --strict option to pass in the pngstest checks -Version 1.6.0beta20 [March 19, 2012] +Version 1.6.0beta20 [March 21, 2012] + Changed chunk handler warnings into benign errors, incrementally load iCCP Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 067b38f8e..c32200ca1 100644 --- a/CHANGES +++ b/CHANGES @@ -4090,7 +4090,8 @@ Version 1.6.0beta19 [March 18, 2012] Avoid the double gamma correction warning in the simplified API. This allows the --strict option to pass in the pngstest checks -Version 1.6.0beta20 [March 19, 2012] +Version 1.6.0beta20 [March 21, 2012] + Changed chunk handler warnings into benign errors, incrementally load iCCP Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/contrib/libtests/makepng.c b/contrib/libtests/makepng.c index 1c96ab541..b1a2ef830 100644 --- a/contrib/libtests/makepng.c +++ b/contrib/libtests/makepng.c @@ -1061,6 +1061,7 @@ int main(int argc, char **argv) { FILE *fp = stdout; + const char *file_name = NULL; int color_type = 8; /* invalid */ int bit_depth = 32; /* invalid */ png_fixed_point gamma = 0; /* not set */ @@ -1189,6 +1190,7 @@ main(int argc, char **argv) exit(1); } + file_name = arg; continue; } @@ -1204,5 +1206,12 @@ main(int argc, char **argv) exit(1); } - return write_png(fp, color_type, bit_depth, gamma, head_insert); + { + int ret = write_png(fp, color_type, bit_depth, gamma, head_insert); + + if (ret != 0 && file_name != NULL) + remove(file_name); + + return ret; + } } diff --git a/contrib/libtests/pngstest.c b/contrib/libtests/pngstest.c index c16de9642..eb90682c2 100644 --- a/contrib/libtests/pngstest.c +++ b/contrib/libtests/pngstest.c @@ -3099,7 +3099,7 @@ read_one_file(Image *image) { long int cb = ftell(f); - if (cb >= 0 && (unsigned long int)cb < (size_t)~(size_t)0) + if (cb > 0 && (unsigned long int)cb < (size_t)~(size_t)0) { png_bytep b = voidcast(png_bytep, malloc((size_t)cb)); @@ -3118,17 +3118,22 @@ read_one_file(Image *image) { free(b); return logclose(image, f, image->file_name, - ": read failed"); + ": read failed: "); } } else return logclose(image, f, image->file_name, - ": out of memory"); + ": out of memory: "); } + else if (cb == 0) + return logclose(image, f, image->file_name, + ": zero length: "); + else - return logclose(image, f, image->file_name, ": tell failed"); + return logclose(image, f, image->file_name, + ": tell failed: "); } else @@ -3169,7 +3174,7 @@ write_one_file(Image *output, Image *image, int convert_to_8bit) } else - return logclose(image, f, "tmpfile", ": flush"); + return logclose(image, f, "tmpfile", ": flush: "); } else diff --git a/png.c b/png.c index 59aba4cd1..24f4a17b9 100644 --- a/png.c +++ b/png.c @@ -412,6 +412,7 @@ png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size), png_memset(info_ptr, 0, sizeof *info_ptr); } +/* The following API is not called internally */ void PNGAPI png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr, int freer, png_uint_32 mask) @@ -428,8 +429,7 @@ png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->free_me &= ~mask; else - png_warning(png_ptr, - "Unknown freer parameter in png_data_freer"); + png_error(png_ptr, "Unknown freer parameter in png_data_freer"); } void PNGAPI @@ -512,7 +512,7 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, #endif #ifdef PNG_iCCP_SUPPORTED - /* Free any iCCP entry */ + /* Free any profile entry */ if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) { png_free(png_ptr, info_ptr->iccp_name); @@ -747,13 +747,13 @@ png_get_copyright(png_const_structrp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.6.0beta20 - March 19, 2012" PNG_STRING_NEWLINE \ + "libpng version 1.6.0beta20 - March 21, 2012" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2012 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.6.0beta20 - March 19, 2012\ + return "libpng version 1.6.0beta20 - March 21, 2012\ Copyright (c) 1998-2012 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; @@ -937,9 +937,158 @@ png_zstream_error(png_structrp png_ptr, int ret) */ /* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ -# ifdef PNG_CHECK_cHRM_SUPPORTED +#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ +static int +png_colorspace_check_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA, int preferred) +{ + /* The 'invalid' flag needs to be sticky, doing things this way avoids having + * many messages caused by just one invalid colorspace chunk. + */ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA)) + { + png_fixed_point gtest; + + if (!png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) || + png_gamma_significant(gtest)) + { + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "inconsistent gamma values"); + return 0; /* failed */ + } + + else if (!preferred) + return 1; /* ok, use existing gamma */ + } + + return 2; /* ok, write gamma */ +} int /* PRIVATE */ +png_colorspace_set_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA, int preferred) +{ + int result = png_colorspace_check_gamma(png_ptr, colorspace, gAMA, + preferred); + + if (result == 2) + { + colorspace->gamma = gAMA; + colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + return result; +} + +void /* PRIVATE */ +png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + { + /* Everything is invalid */ + info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| + PNG_INFO_iCCP); + +# ifdef PNG_COLORSPACE_SUPPORTED + /* Clean up the iCCP profile now if it won't be used. */ + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/); +# else + PNG_UNUSED(png_ptr) +# endif + } + + else + { +# ifdef PNG_COLORSPACE_SUPPORTED + /* Leave the INFO_iCCP flag set if the pngset.c code has already set + * it; this allows a PNG to contain a profile which matches sRGB and + * yet still have that profile retrievable by the application. + */ + if (info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) + info_ptr->valid |= PNG_INFO_sRGB; + + else + info_ptr->valid &= ~PNG_INFO_sRGB; + + if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) + info_ptr->valid |= PNG_INFO_cHRM; + + else + info_ptr->valid &= ~PNG_INFO_cHRM; + + /* TODO: at present it is possible to png_set a detectably inconsistent + * ICC profile, this should be handled, somehow. + */ +# endif + + if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) + info_ptr->valid |= PNG_INFO_gAMA; + + else + info_ptr->valid &= ~PNG_INFO_gAMA; + } +} + +#ifdef PNG_READ_SUPPORTED +void /* PRIVATE */ +png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr == NULL) /* reduce code size; check here not in the caller */ + return; + + info_ptr->colorspace = png_ptr->colorspace; + png_colorspace_sync_info(png_ptr, info_ptr); +} +#endif +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED +#if 0 +/* Added at libpng version 1.2.34 (Dec 8, 2008) and 1.4.0 (Jan 2, + * 2010: moved from pngset.c) */ +/* + * Multiply two 32-bit numbers, V1 and V2, using 32-bit + * arithmetic, to produce a 64-bit result in the HI/LO words. + * + * A B + * x C D + * ------ + * AD || BD + * AC || CB || 0 + * + * where A and B are the high and low 16-bit words of V1, + * C and D are the 16-bit words of V2, AD is the product of + * A and D, and X || Y is (X << 16) + Y. +*/ +static void +png_64bit_product (long v1, long v2, unsigned long *hi_product, + unsigned long *lo_product) +{ + int a, b, c, d; + long lo, hi, x, y; + + a = (v1 >> 16) & 0xffff; + b = v1 & 0xffff; + c = (v2 >> 16) & 0xffff; + d = v2 & 0xffff; + + lo = b * d; /* BD */ + x = a * d + c * b; /* AD + CB */ + y = ((lo >> 16) & 0xffff) + x; + + lo = (lo & 0xffff) | ((y & 0xffff) << 16); + hi = (y >> 16) & 0xffff; + + hi += a * c; /* AC */ + + *hi_product = (unsigned long)hi; + *lo_product = (unsigned long)lo; +} + +static int png_check_cHRM_fixed(png_const_structrp png_ptr, png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, @@ -1006,40 +1155,40 @@ png_check_cHRM_fixed(png_const_structrp png_ptr, return ret; } -# endif /* PNG_CHECK_cHRM_SUPPORTED */ +#endif /*0*/ -#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) +static int +png_xy_from_XYZ(png_xy *xy, const 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; + 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; + 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; + 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; + 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; + 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; + whiteX += XYZ->blueX; + whiteY += XYZ->blueY; - /* The reference white is simply the same of the end-point (X,Y,Z) vectors, + /* The reference white is simply the sum of the end-point (X,Y,Z) vectors, * thus: */ if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1; @@ -1048,7 +1197,8 @@ int png_xy_from_XYZ(png_xy *xy, png_XYZ XYZ) return 0; } -int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy) +static int +png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) { png_fixed_point red_inverse, green_inverse, blue_scale; png_fixed_point left, right, denominator; @@ -1057,14 +1207,14 @@ int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy) * 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; + 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 @@ -1245,89 +1395,688 @@ int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy) /* 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; + 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; + 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 */) + 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) + 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) - + 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, + 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, + 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, + 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_const_structrp png_ptr, png_XYZ *XYZ, png_xy xy) +static int +png_XYZ_normalize(png_XYZ *XYZ) { - switch (png_XYZ_from_xy(XYZ, xy)) + png_int_32 Y; + + if (XYZ->redY < 0 || XYZ->greenY < 0 || XYZ->blueY < 0 || + XYZ->redX < 0 || XYZ->greenX < 0 || XYZ->blueX < 0 || + XYZ->redZ < 0 || XYZ->greenZ < 0 || XYZ->blueZ < 0) + return 1; + + /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. + * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore + * relying on addition of two positive values producing a negative one is not * safe. + */ + Y = XYZ->redY; + if (0x7fffffff - Y < XYZ->greenX) return 1; + Y += XYZ->greenY; + if (0x7fffffff - Y < XYZ->blueX) return 1; + Y += XYZ->blueY; + + if (Y != PNG_FP_1) + { + if (!png_muldiv(&XYZ->redX, XYZ->redX, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->redY, XYZ->redY, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->redZ, XYZ->redZ, PNG_FP_1, Y)) return 1; + + if (!png_muldiv(&XYZ->greenX, XYZ->greenX, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->greenY, XYZ->greenY, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->greenZ, XYZ->greenZ, PNG_FP_1, Y)) return 1; + + if (!png_muldiv(&XYZ->blueX, XYZ->blueX, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->blueY, XYZ->blueY, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->blueZ, XYZ->blueZ, PNG_FP_1, Y)) return 1; + } + + return 0; +} + +static int +png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) +{ + /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ + return !(PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || + PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || + PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || + PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || + PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || + PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || + PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || + PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)); +} + +/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM + * chunk chromaticities. Earlier checks used to simply look for the overflow + * condition (where the determinant of the matrix to solve for XYZ ends up zero + * because the chromaticity values are not all distinct.) Despite this it is + * theoretically possible to produce chromaticities that are apparently valid + * but that rapidly degrade to invalid, potentially crashing, sets because of + * arithmetic inaccuracies when calculations are performed on them. The new + * check is to round-trip xy -> XYZ -> xy and then check that the result is + * within a small percentage of the original. + */ +static int +png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) +{ + int result; + png_xy xyTest; + + /* As a side-effect this routine also returns the XYZ endpoints. */ + result = png_XYZ_from_xy(XYZ, xy); + if (result) return result; + + result = png_xy_from_XYZ(&xyTest, XYZ); + if (result) return result; + + if (png_colorspace_endpoints_match(xy, &xyTest, + 5/*actually, the math is pretty accurate*/)) + return 0; + + /* Too much slip */ + return 1; +} + +/* This is the check going the other way. The XYZ is modified to normalize it + * (another side-effect) and the xy chromaticities are returned. + */ +static int +png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) +{ + int result; + png_XYZ XYZtemp; + + result = png_XYZ_normalize(XYZ); + if (result) return result; + + result = png_xy_from_XYZ(xy, XYZ); + if (result) return result; + + XYZtemp = *XYZ; + return png_colorspace_check_xy(&XYZtemp, xy); +} + +/* Used to check for an endpoint match against sRGB */ +static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ +{ + /* color x y */ + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000, + /* white */ 31270, 32900 +}; + +static int +png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, + int preferred) +{ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + /* The consistency check is performed on the chromaticities; this factors out + * variations because of the normalization (or not) of the end point Y + * values. + */ + if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + /* The end points must be reasonably close to any we already have. The + * following allows an error of up to +/-1% + */ + if (!png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, 1000)) + { + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "inconsistent chromaticities"); + return 0; /* failed */ + } + + /* Only overwrite with preferred values */ + if (!preferred) + return 1; /* ok, but no change */ + } + + colorspace->end_points_xy = *xy; + colorspace->end_points_XYZ = *XYZ; + colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS; + + /* TODO: 4000 is 0.04, and this is sufficient to accomodate the difference + * between the adapted D50 white point and the original D65 one. Is this + * necessary? + */ + if (png_colorspace_endpoints_match(xy, &sRGB_xy, 4000)) + colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; + + else + colorspace->flags &= 0xff ^ PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; + + return 2; /* ok and changed */ +} + +int /* PRIVATE */ +png_colorspace_set_chromaticities(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, int preferred) +{ + /* We must check the end points to ensure they are reasonable - in the past + * color management systems have crashed as a result of getting bogus + * colorant values, while this isn't the fault of libpng it is the + * responsibility of libpng because PNG carries the bomb and libpng is in a + * position to protect against it. + */ + png_XYZ XYZ; + + switch (png_colorspace_check_xy(&XYZ, xy)) { case 0: /* success */ - return 1; + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, + preferred); 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. + /* We can't invert the chromaticities so we can't produce value XYZ + * values. Likely as not a color management system will fail too. */ - png_warning(png_ptr, - "extreme cHRM chunk cannot be converted to tristimulus values"); + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid chromaticities"); 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"); + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); break; } - /* ERROR RETURN */ + return 0; /* failed */ +} + +int /* PRIVATE */ +png_colorspace_set_endpoints(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_XYZ *XYZIn, int preferred) +{ + png_XYZ XYZ = *XYZIn; + png_xy xy; + + switch (png_colorspace_check_XYZ(&xy, &XYZ)) + { + case 0: + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, + preferred); + + case 1: + /* End points are invalid. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid end points"); + break; + + default: + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + break; + } + + return 0; /* failed */ +} + +#if defined PNG_sRGB_SUPPORTED || defined PNG_iCCP_SUPPORTED +static int +profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_alloc_size_t value, png_const_charp reason) +{ + size_t pos; + char message[256]; + + if (colorspace != NULL) + colorspace->flags |= PNG_COLORSPACE_INVALID; + + pos = png_safecat(message, sizeof message, 0, "profile '"); + pos = png_safecat(message, pos+79, pos, name); + pos = png_safecat(message, sizeof message, pos, "': "); +# ifdef PNG_WARNINGS_SUPPORTED + { + char number[PNG_NUMBER_BUFFER_SIZE]; + + pos = png_safecat(message, sizeof message, pos, + png_format_number(number, number+(sizeof number), + PNG_NUMBER_FORMAT_x, value)); + } + pos = png_safecat(message, sizeof message, pos, ": "); +# endif + pos = png_safecat(message, sizeof message, pos, reason); + + if (colorspace != NULL) + { + if (png_ptr->mode & PNG_IS_READ_STRUCT) + png_chunk_benign_error(png_ptr, message); + + else + png_app_error(png_ptr, message); + } + +# ifdef PNG_WARNINGS_SUPPORTED + else + { + if (png_ptr->mode & PNG_IS_READ_STRUCT) + png_chunk_warning(png_ptr, message); + + else + png_warning(png_ptr, message); + } +# endif + return 0; } + +static int /* PRIVATE */ +png_colorspace_set_profile(png_const_structrp png_ptr, png_const_charp name, + png_colorspacerp colorspace, png_fixed_point gAMA, const png_xy *xy, + const png_XYZ *XYZ, int intent, int preferred) +{ + int write_intent, write_gamma, result; + + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + /* Similar to the above routines, but ensure that both the gamma and the + * end-points are checked before doing any assignment. + */ + if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_INTENT)) + { + if (colorspace->rendering_intent != intent) + return profile_error(png_ptr, colorspace, name, (unsigned)intent, + "inconsistent rendering intents"); + + write_intent = 0; /* Ok, don't change */ + } + + else + write_intent = 1; /* Needs to be written */ + + switch (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, preferred)) + { + case 2: + write_gamma = 1; + break; + + case 1: + write_gamma = 0; /* current value ok and preferred */ + break; + + default: /* error */ + return 0; + } + + /* Everything seems ok up to this point, update the endpoints and, if this + * works, do the gamma and intent too. + */ + result = png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, XYZ, + preferred); + + switch (result) + { + case 2: /* ok, changed */ + case 1: /* ok, no end-point change */ + if (write_intent) + { + /* The value of intent must be checked in the caller; bugs in GCC + * force 'int' to be used as the parameter type. + */ + colorspace->rendering_intent = (png_uint_16)intent; + colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; + result = 2; + } + + if (write_gamma) + { + colorspace->gamma = gAMA; + colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; + result = 2; + } + + return result; + + default: /* failure */ + return 0; + } +} +#endif /* sRGB || iCCP */ + +#ifdef PNG_sRGB_SUPPORTED +int /* PRIVATE */ +png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, + int intent, int preferred) +{ + /* sRGB sets known gamma, end points and (from the chunk) intent. */ + /* IMPORTANT: these are not necessarily the values found in an ICC profile + * because ICC profiles assume a D50 environment and therefore use XYZ values + * appropriate to a D50 environment. Perhaps we should too; it's just + * slightly weird because the chromaticities of the adapted colorants don't + * match the above values. + */ + static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */ + { + /* color X Y Z */ + /* red */ 41239, 21264, 1933, + /* green */ 35758, 71517, 11919, + /* blue */ 18048, 7219, 95053 + }; + + int result; + + /* The above XYZ values, which are accurate to 5dp, produce rgb to gray + * coefficients of (6968,23435,2366), which are reduced (because they add up + * to 32769 not 32768) to (6968,23434,2366). These are the values that + * libpng has traditionally used (and are the best values given the 15bit + * algorithm used by the rgb to gray code.) + */ + if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) + return profile_error(png_ptr, colorspace, "sRGB", (unsigned)intent, + "invalid sRGB rendering intent"); + + result = png_colorspace_set_profile(png_ptr, "sRGB", colorspace, + PNG_GAMMA_sRGB_INVERSE, &sRGB_xy, &sRGB_XYZ, intent, preferred); + + /* The implicit profile is the sRGB one, so: */ + if (result) + colorspace->flags |= PNG_COLORSPACE_MATCHES_sRGB; + + return result; +} +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +int /* PRIVATE */ +png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length) +{ + if (profile_length < 132) + return profile_error(png_ptr, colorspace, name, profile_length, + "too short"); + + if (profile_length & 3) + return profile_error(png_ptr, colorspace, name, profile_length, + "invalid length"); + + return 1; +} + +int /* PRIVATE */ +png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */) +{ + png_uint_32 temp; + + /* Length checks (can't be ignored) */ + temp = png_get_uint_32(profile); + if (temp != profile_length) + return profile_error(png_ptr, colorspace, name, temp, + "length does not match profile"); + + temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ + if (temp > 357913930 || /* (2^32-4-132)/12: maxium possible tag count */ + profile_length < 132+12*temp) /* truncated tag table */ + return profile_error(png_ptr, colorspace, name, temp, + "tag count too large"); + + /* The 'intent' must be valid or we can't store it, ICC limits the intent to + * 16 bits. + */ + temp = png_get_uint_32(profile+64); + if (temp >= 0xffff) /* The ICC limit */ + return profile_error(png_ptr, colorspace, name, temp, + "invalid rendering intent"); + + /* This is just a warning because the profile may be valid in future + * versions. + */ + if (temp >= PNG_sRGB_INTENT_LAST) + (void)profile_error(png_ptr, NULL, name, temp, + "intent outside defined range"); + + /* At this point the tag table can't be checked because it hasn't necessarily + * been loaded, however various header fields can be checked. These checks + * are for values permitted by the PNG spec in an ICC profile; the PNG spec + * restricts the profiles than can be passed in an iCCP chunk (they must be + * appropriate to processing PNG data!) + */ + + /* Data checks (could be skipped). These checks must be independent of the + * version number, however the version number doesn't accomodate changes in + * the header fields (just the known tags and the interpretation of the + * data.) + */ + temp = png_get_uint_32(profile+36); /* signature 'ascp' */ + if (temp != 0x61637370) + return profile_error(png_ptr, colorspace, name, temp, + "invalid signature"); + + /* The PNG spec requires this: + * "If the iCCP chunk is present, the image samples conform to the colour + * space represented by the embedded ICC profile as defined by the + * International Color Consortium [ICC]. The colour space of the ICC profile + * shall be an RGB colour space for colour images (PNG colour types 2, 3, and + * 6), or a greyscale colour space for greyscale images (PNG colour types 0 + * and 4)." + * + * This code does not check the color type because png_set_iCCP may be called + * before png_set_IHDR on write and because, anyway, the spec is + * fundamentally flawed: RGB profiles can be used quite meaningfully for + * grayscale images and both RGB and palette images may only have gray colors + * in them, so gray profiles may be appropriate. + */ + temp = png_get_uint_32(profile+16); /* data colour space field */ + if (temp != 0x52474220 /* 'RGB ' */ && temp != 0x47524159 /* 'GRAY' */) + return profile_error(png_ptr, colorspace, name, temp, + "invalid color space"); + + /* It is up to the application to check that the profile class matches the + * application requirements; the spec provides no guidance, but it's pretty + * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer + * ('prtr') or 'spac' (for generic color spaces). Issue a warning in these + * cases. + */ + temp = png_get_uint_32(profile+12); /* profile/device class */ + if (temp != 0x73636E72 /* 'scnr' */ && temp != 0x6D6E7472 /* 'mntr' */ && + temp != 0x70727472 /* 'prtr' */ && temp != 0x73706163 /* 'spac' */) + (void)profile_error(png_ptr, NULL, name, temp, "unexpected class"); + + return 1; +} + +int /* PRIVATE */ +png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) +{ + png_uint_32 tag_count = png_get_uint_32(profile+128); + png_uint_32 itag; + png_const_bytep tag = profile+132; /* The first tag */ + + for (itag=0; itag < tag_count; ++itag, tag += 12) + { + png_uint_32 tag_id = png_get_uint_32(tag+0); + png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */ + png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */ + + /* The ICC specification does not exclude zero length tags, therefore the + * start might actually be anywhere if there is no data, but this would be + * a clear abuse of the intent of the standard so the start is checked for + * being in range. + */ + if ((tag_start & 3) != 0 || tag_start > profile_length || + tag_length > profile_length - tag_start) + return profile_error(png_ptr, colorspace, name, tag_id, + "tag data outside profile"); + } + + return 1; +} + +void /* PRIVATE */ +png_icc_set_gAMA_and_cHRM(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, png_const_bytep profile, + int preferred) +{ + /* TODO: implement this! */ + PNG_UNUSED(png_ptr) + PNG_UNUSED(colorspace) + PNG_UNUSED(name) + PNG_UNUSED(profile) + PNG_UNUSED(preferred) +} + +int /* PRIVATE */ +png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile, int preferred) +{ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + if (png_icc_check_length(png_ptr, colorspace, name, profile_length) && + png_icc_check_header(png_ptr, colorspace, name, profile_length, profile) + && png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, + profile)) + { + png_icc_set_gAMA_and_cHRM(png_ptr, colorspace, name, profile, preferred); + return 1; + } + + /* Failure case */ + return 0; +} +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void /* PRIVATE */ +png_colorspace_set_rgb_coefficients(png_structrp png_ptr) +{ + /* Set the rgb_to_gray coefficients from the colorspace. */ + if (!png_ptr->rgb_to_gray_coefficients_set && + (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + /* png_set_background has not been called, get the coefficients from the Y + * values of the colorspace colorants. + */ + png_fixed_point r = png_ptr->colorspace.end_points_XYZ.redY; + png_fixed_point g = png_ptr->colorspace.end_points_XYZ.greenY; + png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blueY; + png_fixed_point total = r+g+b; + + if (total > 0 && + r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && + g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && + b >= 0 && png_muldiv(&b, b, 32768, total) && 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"); + + else + { + 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 +#endif /* COLORSPACE */ + void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, @@ -2273,50 +3022,6 @@ png_reciprocal2(png_fixed_point a, png_fixed_point b) } #endif /* READ_GAMMA */ -#ifdef PNG_CHECK_cHRM_SUPPORTED -/* Added at libpng version 1.2.34 (Dec 8, 2008) and 1.4.0 (Jan 2, - * 2010: moved from pngset.c) */ -/* - * Multiply two 32-bit numbers, V1 and V2, using 32-bit - * arithmetic, to produce a 64-bit result in the HI/LO words. - * - * A B - * x C D - * ------ - * AD || BD - * AC || CB || 0 - * - * where A and B are the high and low 16-bit words of V1, - * C and D are the 16-bit words of V2, AD is the product of - * A and D, and X || Y is (X << 16) + Y. -*/ - -void /* PRIVATE */ -png_64bit_product (long v1, long v2, unsigned long *hi_product, - unsigned long *lo_product) -{ - int a, b, c, d; - long lo, hi, x, y; - - a = (v1 >> 16) & 0xffff; - b = v1 & 0xffff; - c = (v2 >> 16) & 0xffff; - d = v2 & 0xffff; - - lo = b * d; /* BD */ - x = a * d + c * b; /* AD + CB */ - y = ((lo >> 16) & 0xffff) + x; - - lo = (lo & 0xffff) | ((y & 0xffff) << 16); - hi = (y >> 16) & 0xffff; - - hi += a * c; /* AC */ - - *hi_product = (unsigned long)hi; - *lo_product = (unsigned long)lo; -} -#endif /* CHECK_cHRM */ - #ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */ #ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED /* Fixed point gamma. @@ -2913,7 +3618,7 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) if (bit_depth <= 8) { png_build_8bit_table(png_ptr, &png_ptr->gamma_table, - png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->gamma, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1); #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ @@ -2922,11 +3627,11 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) { png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, - png_reciprocal(png_ptr->gamma)); + png_reciprocal(png_ptr->colorspace.gamma)); png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : - png_ptr->gamma/* Probably doing rgb_to_gray */); + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); } #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ } @@ -2995,13 +3700,13 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) #endif png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_product2(png_ptr->gamma, + png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1); #ifdef PNG_16BIT_SUPPORTED else png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, - png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->gamma, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma) : PNG_FP_1); #endif @@ -3011,7 +3716,7 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) { png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, - png_reciprocal(png_ptr->gamma)); + png_reciprocal(png_ptr->colorspace.gamma)); /* Notice that the '16 from 1' table should be full precision, however * the lookup on this table still uses gamma_shift, so it can't be. @@ -3019,7 +3724,7 @@ png_build_gamma_table(png_structrp png_ptr, int bit_depth) */ png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : - png_ptr->gamma/* Probably doing rgb_to_gray */); + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); } #endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ } diff --git a/png.h b/png.h index d2f5ce137..ebf9827c7 100644 --- a/png.h +++ b/png.h @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.0beta20 - March 19, 2012 + * libpng version 1.6.0beta20 - March 21, 2012 * Copyright (c) 1998-2012 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.6.0beta20 - March 19, 2012: Glenn + * libpng versions 0.97, January 1998, through 1.6.0beta20 - March 21, 2012: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: @@ -198,7 +198,7 @@ * * This code is released under the libpng license. * - * libpng versions 1.2.6, August 15, 2004, through 1.6.0beta20, March 19, 2012, are + * libpng versions 1.2.6, August 15, 2004, through 1.6.0beta20, March 21, 2012, are * Copyright (c) 2004, 2006-2012 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: @@ -310,7 +310,7 @@ * Y2K compliance in libpng: * ========================= * - * March 19, 2012 + * March 21, 2012 * * Since the PNG Development group is an ad-hoc body, we can't make * an official declaration. @@ -376,7 +376,7 @@ /* Version information for png.h - this should match the version in png.c */ #define PNG_LIBPNG_VER_STRING "1.6.0beta20" #define PNG_HEADER_VERSION_STRING \ - " libpng version 1.6.0beta20 - March 19, 2012\n" + " libpng version 1.6.0beta20 - March 21, 2012\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -1095,9 +1095,9 @@ PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, /* Writes all the PNG information before the image. */ PNG_EXPORT(20, void, png_write_info_before_PLTE, - (png_structrp png_ptr, png_inforp info_ptr)); + (png_structrp png_ptr, png_const_inforp info_ptr)); PNG_EXPORT(21, void, png_write_info, - (png_structrp png_ptr, png_inforp info_ptr)); + (png_structrp png_ptr, png_const_inforp info_ptr)); #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the information before the actual image data. */ @@ -1892,9 +1892,12 @@ PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, /* Reassign responsibility for freeing existing data, whether allocated * by libpng or by the application; this works on the png_info structure passed * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. */ -PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, - png_inforp info_ptr, int freer, png_uint_32 mask)); +PNG_EXPORTA(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED); /* Assignments for png_data_freer */ #define PNG_DESTROY_WILL_FREE_DATA 1 @@ -2084,14 +2087,12 @@ PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, png_const_inforp 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_structrp png_ptr, png_const_inforp 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)) -#endif PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_fixed_point *int_red_X, png_fixed_point *int_red_Y, diff --git a/pngconf.h b/pngconf.h index 841251e93..c97be31f4 100644 --- a/pngconf.h +++ b/pngconf.h @@ -1,7 +1,7 @@ /* pngconf.h - machine configurable file for libpng * - * libpng version 1.6.0beta20 - March 19, 2012 + * libpng version 1.6.0beta20 - March 21, 2012 * * Copyright (c) 1998-2012 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) diff --git a/pngget.c b/pngget.c index 0fedb441a..1d27e4d6d 100644 --- a/pngget.c +++ b/pngget.c @@ -463,87 +463,47 @@ png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, * 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_const_structrp png_ptr, png_const_inforp 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_structrp png_ptr, png_const_inforp 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) { - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + /* Quiet API change: this code used to only return the end points if a cHRM + * chunk was present, but the end points can also come from iCCP or sRGB + * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and + * the png_set_ APIs merely check that set end points are mutually + * consistent. + */ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) { png_debug1(1, "in %s retrieval function", "cHRM"); if (white_x != NULL) - *white_x = png_float(png_ptr, info_ptr->x_white, "cHRM white X"); + *white_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); if (white_y != NULL) - *white_y = png_float(png_ptr, info_ptr->y_white, "cHRM white Y"); + *white_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); if (red_x != NULL) - *red_x = png_float(png_ptr, info_ptr->x_red, "cHRM red X"); + *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, + "cHRM red X"); if (red_y != NULL) - *red_y = png_float(png_ptr, info_ptr->y_red, "cHRM red Y"); + *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, + "cHRM red Y"); if (green_x != NULL) - *green_x = png_float(png_ptr, info_ptr->x_green, "cHRM green X"); + *green_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); if (green_y != NULL) - *green_y = png_float(png_ptr, info_ptr->y_green, "cHRM green Y"); + *green_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); if (blue_x != NULL) - *blue_x = png_float(png_ptr, info_ptr->x_blue, "cHRM blue X"); + *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, + "cHRM blue X"); if (blue_y != NULL) - *blue_y = png_float(png_ptr, info_ptr->y_blue, "cHRM blue Y"); + *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, + "cHRM blue Y"); return (PNG_INFO_cHRM); } @@ -556,30 +516,38 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, 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 (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); + if (red_X != NULL) - *red_X = png_float(png_ptr, XYZ.redX, "cHRM red X"); + *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.redX, + "cHRM red X"); if (red_Y != NULL) - *red_Y = png_float(png_ptr, XYZ.redY, "cHRM red Y"); + *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.redY, + "cHRM red Y"); if (red_Z != NULL) - *red_Z = png_float(png_ptr, XYZ.redZ, "cHRM red Z"); + *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.redZ, + "cHRM red Z"); if (green_X != NULL) - *green_X = png_float(png_ptr, XYZ.greenX, "cHRM green X"); + *green_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.greenX, "cHRM green X"); if (green_Y != NULL) - *green_Y = png_float(png_ptr, XYZ.greenY, "cHRM green Y"); + *green_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.greenY, "cHRM green Y"); if (green_Z != NULL) - *green_Z = png_float(png_ptr, XYZ.greenZ, "cHRM green Z"); + *green_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.greenZ, "cHRM green Z"); if (blue_X != NULL) - *blue_X = png_float(png_ptr, XYZ.blueX, "cHRM blue X"); + *blue_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.blueX, + "cHRM blue X"); if (blue_Y != NULL) - *blue_Y = png_float(png_ptr, XYZ.blueY, "cHRM blue Y"); + *blue_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.blueY, + "cHRM blue Y"); if (blue_Z != NULL) - *blue_Z = png_float(png_ptr, XYZ.blueZ, "cHRM blue Z"); + *blue_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.blueZ, + "cHRM blue Z"); return (PNG_INFO_cHRM); } @@ -588,6 +556,43 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, # endif # ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp 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->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); + + if (int_red_X != NULL) + *int_red_X = info_ptr->colorspace.end_points_XYZ.redX; + if (int_red_Y != NULL) + *int_red_Y = info_ptr->colorspace.end_points_XYZ.redY; + if (int_red_Z != NULL) + *int_red_Z = info_ptr->colorspace.end_points_XYZ.redZ; + if (int_green_X != NULL) + *int_green_X = info_ptr->colorspace.end_points_XYZ.greenX; + if (int_green_Y != NULL) + *int_green_Y = info_ptr->colorspace.end_points_XYZ.greenY; + if (int_green_Z != NULL) + *int_green_Z = info_ptr->colorspace.end_points_XYZ.greenZ; + if (int_blue_X != NULL) + *int_blue_X = info_ptr->colorspace.end_points_XYZ.blueX; + if (int_blue_Y != NULL) + *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blueY; + if (int_blue_Z != NULL) + *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blueZ; + return (PNG_INFO_cHRM); + } + + return (0); +} + png_uint_32 PNGAPI png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, @@ -596,24 +601,25 @@ png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, { png_debug1(1, "in %s retrieval function", "cHRM"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) { if (white_x != NULL) - *white_x = info_ptr->x_white; + *white_x = info_ptr->colorspace.end_points_xy.whitex; if (white_y != NULL) - *white_y = info_ptr->y_white; + *white_y = info_ptr->colorspace.end_points_xy.whitey; if (red_x != NULL) - *red_x = info_ptr->x_red; + *red_x = info_ptr->colorspace.end_points_xy.redx; if (red_y != NULL) - *red_y = info_ptr->y_red; + *red_y = info_ptr->colorspace.end_points_xy.redy; if (green_x != NULL) - *green_x = info_ptr->x_green; + *green_x = info_ptr->colorspace.end_points_xy.greenx; if (green_y != NULL) - *green_y = info_ptr->y_green; + *green_y = info_ptr->colorspace.end_points_xy.greeny; if (blue_x != NULL) - *blue_x = info_ptr->x_blue; + *blue_x = info_ptr->colorspace.end_points_xy.bluex; if (blue_y != NULL) - *blue_y = info_ptr->y_blue; + *blue_y = info_ptr->colorspace.end_points_xy.bluey; return (PNG_INFO_cHRM); } @@ -623,35 +629,43 @@ png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, #endif #ifdef PNG_gAMA_SUPPORTED -png_uint_32 PNGFAPI +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, png_fixed_point *file_gamma) { png_debug1(1, "in %s retrieval function", "gAMA"); - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) - && file_gamma != NULL) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + file_gamma != NULL) { - *file_gamma = info_ptr->gamma; + *file_gamma = info_ptr->colorspace.gamma; return (PNG_INFO_gAMA); } return (0); } +# endif + # ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, double *file_gamma) { - png_fixed_point igamma; - png_uint_32 ok = png_get_gAMA_fixed(png_ptr, info_ptr, &igamma); + png_debug1(1, "in %s retrieval function", "gAMA(float)"); - if (ok) - *file_gamma = png_float(png_ptr, igamma, "png_get_gAMA"); + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + file_gamma != NULL) + { + *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, + "png_get_gAMA"); + return (PNG_INFO_gAMA); + } - return ok; + return (0); } - # endif #endif @@ -665,7 +679,7 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) && file_srgb_intent != NULL) { - *file_srgb_intent = (int)info_ptr->srgb_intent; + *file_srgb_intent = info_ptr->colorspace.rendering_intent; return (PNG_INFO_sRGB); } @@ -687,11 +701,11 @@ png_get_iCCP(png_const_structrp png_ptr, png_const_inforp info_ptr, { *name = info_ptr->iccp_name; *profile = info_ptr->iccp_profile; - /* Compression_type is a dummy so the API won't have to change - * if we introduce multiple compression types later. + *proflen = png_get_uint_32(info_ptr->iccp_profile); + /* This is somewhat irrelevant since the profile data returned has + * actually been uncompressed. */ - *proflen = info_ptr->iccp_proflen; - *compression_type = info_ptr->iccp_compression; + *compression_type = PNG_COMPRESSION_TYPE_BASE; return (PNG_INFO_iCCP); } @@ -1133,15 +1147,6 @@ png_get_io_chunk_type (png_const_structrp png_ptr) { return png_ptr->chunk_name; } - -#if PNG_LIBPNG_VER < 10600 -png_const_bytep PNGAPI -png_get_io_chunk_name (png_structrp png_ptr) -{ - PNG_CSTRING_FROM_CHUNK(png_ptr->io_chunk_string, png_ptr->chunk_name); - return png_ptr->io_chunk_string; -} -#endif #endif /* ?PNG_IO_STATE_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/pnginfo.h b/pnginfo.h index a33bfab06..ee1947491 100644 --- a/pnginfo.h +++ b/pnginfo.h @@ -70,11 +70,13 @@ struct png_info_def png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ +#ifdef PNG_READ_SUPPORTED /* The following is informational only on read, and not used on writes. */ png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ png_byte pixel_depth; /* number of bits per pixel */ png_byte spare_byte; /* to align the data, and for future use */ png_byte signature[8]; /* magic bytes read by libpng from start of file */ +#endif /* The rest of the data is optional. If you are reading, check the * valid field to see if the information in these are valid. If you @@ -82,18 +84,25 @@ struct png_info_def * and initialize the appropriate fields below. */ -#if defined(PNG_gAMA_SUPPORTED) - /* The gAMA chunk describes the gamma characteristics of the system - * on which the image was created, normally in the range [1.0, 2.5]. - * Data is valid if (valid & PNG_INFO_gAMA) is non-zero. +#if defined PNG_COLORSPACE_SUPPORTED || defined PNG_GAMMA_SUPPORTED + /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are + * defined. When COLORSPACE is switched on all the colorspace-defining + * chunks should be enabled, when GAMMA is switched on all the gamma-defining + * chunks should be enabled. If this is not done it becomes possible to read + * inconsistent PNG files and assign a probably incorrect interpretation to + * the information. (In other words, by carefully choosing which chunks to + * recognize the system configuration can select an interpretation for PNG + * files containing ambiguous data and this will result in inconsistent + * behavior between different libpng builds!) */ - png_fixed_point gamma; + png_colorspace colorspace; #endif -#ifdef PNG_sRGB_SUPPORTED - /* GR-P, 0.96a */ - /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */ - png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */ +#ifdef PNG_iCCP_SUPPORTED + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_bytep iccp_profile; /* International Color Consortium profile data */ + png_uint_32 iccp_proflen; /* ICC profile data length */ #endif #ifdef PNG_TEXT_SUPPORTED @@ -183,23 +192,6 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) png_uint_16p hist; #endif -#ifdef PNG_cHRM_SUPPORTED - /* The cHRM chunk describes the CIE color characteristics of the monitor - * on which the PNG was created. This data allows the viewer to do gamut - * mapping of the input image to ensure that the viewer sees the same - * colors in the image as the creator. Values are in the range - * [0.0, 0.8]. Data valid if (valid & PNG_INFO_cHRM) non-zero. - */ - png_fixed_point x_white; - png_fixed_point y_white; - png_fixed_point x_red; - png_fixed_point y_red; - png_fixed_point x_green; - png_fixed_point y_green; - png_fixed_point x_blue; - png_fixed_point y_blue; -#endif - #ifdef PNG_pCAL_SUPPORTED /* The pCAL chunk describes a transformation between the stored pixel * values and original physical data values used to create the image. @@ -231,14 +223,6 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) int unknown_chunks_num; #endif -#ifdef PNG_iCCP_SUPPORTED - /* iCCP chunk data. */ - png_charp iccp_name; /* profile name */ - png_bytep iccp_profile; /* International Color Consortium profile data */ - png_uint_32 iccp_proflen; /* ICC profile data length */ - png_byte iccp_compression; /* Always zero */ -#endif - #ifdef PNG_sPLT_SUPPORTED /* Data on sPLT chunks (there may be more than one). */ png_sPLT_tp splt_palettes; diff --git a/pngpriv.h b/pngpriv.h index 82e19aab3..7a61a2d10 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -186,8 +186,8 @@ #endif #include "png.h" -#include "pnginfo.h" #include "pngstruct.h" +#include "pnginfo.h" /* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */ #ifndef PNG_DLL_EXPORT @@ -457,16 +457,16 @@ typedef const png_uint_16p * png_const_uint_16pp; #define PNG_HAVE_IDAT 0x04 /* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */ #define PNG_HAVE_IEND 0x10 -#define PNG_HAVE_gAMA 0x20 -#define PNG_HAVE_cHRM 0x40 -#define PNG_HAVE_sRGB 0x80 + /* 0x20 (unused) */ + /* 0x40 (unused) */ + /* 0x80 (unused) */ #define PNG_HAVE_CHUNK_HEADER 0x100 #define PNG_WROTE_tIME 0x200 #define PNG_WROTE_INFO_BEFORE_PLTE 0x400 #define PNG_BACKGROUND_IS_GRAY 0x800 #define PNG_HAVE_PNG_SIGNATURE 0x1000 #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ -#define PNG_HAVE_iCCP 0x4000 + /* 0x4000 (unused) */ #define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */ /* Flags for the transformations the PNG library does on the image data */ @@ -875,17 +875,9 @@ PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr, #endif #ifdef PNG_WRITE_cHRM_SUPPORTED -# ifdef PNG_FLOATING_POINT_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_write_cHRM,(png_structrp png_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_EMPTY); -# endif PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_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_EMPTY); + const png_xy *xy), PNG_EMPTY); + /* The xy value must have been previously validated */ #endif #ifdef PNG_WRITE_sRGB_SUPPORTED @@ -895,9 +887,11 @@ PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, #ifdef PNG_WRITE_iCCP_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, - png_const_charp name, int compression_type, - png_const_bytep profile, png_uint_32 proflen),PNG_EMPTY); - /* Note to maintainer: profile should be png_bytep */ + png_const_charp name, png_const_bytep profile), PNG_EMPTY); + /* The profile must have been previously validated for correctness, the + * length comes from the first four bytes. Only the base, deflate, + * compression is supported. + */ #endif #ifdef PNG_WRITE_sPLT_SUPPORTED @@ -1395,50 +1389,71 @@ PNG_INTERNAL_FUNCTION(void,png_do_write_intrapixel,(png_row_infop row_info, png_bytep row),PNG_EMPTY); #endif +/* Added at libpng version 1.6.0 */ +#ifdef PNG_GAMMA_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_gamma,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA, int preferred), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Synchronize the info 'valid' flags with the colorspace */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Copy the png_struct colorspace to the info_struct and call the above to + * synchronize the flags. Checks for NULL info_ptr and does nothing. + */ +#endif + /* Added at libpng version 1.4.0 */ -#ifdef PNG_CHECK_cHRM_SUPPORTED -PNG_INTERNAL_FUNCTION(int,png_check_cHRM_fixed,(png_const_structrp png_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_EMPTY); -#endif - -#ifdef PNG_CHECK_cHRM_SUPPORTED -/* Added at libpng version 1.2.34 and 1.4.0 */ -/* Currently only used by png_check_cHRM_fixed */ -PNG_INTERNAL_FUNCTION(void,png_64bit_product,(long v1, long v2, - unsigned long *hi_product, unsigned long *lo_product),PNG_EMPTY); -#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 +#ifdef PNG_COLORSPACE_SUPPORTED +/* These internal functions are for maintaining the colorspace structure within + * a png_info or png_struct (or, indeed, both). */ -PNG_INTERNAL_FUNCTION(int,png_xy_from_XYZ,(png_xy *xy, png_XYZ XYZ),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy,(png_XYZ *XYZ, png_xy xy),PNG_EMPTY); -PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy_checked,(png_const_structrp png_ptr, - png_XYZ *XYZ, png_xy xy),PNG_EMPTY); -#endif +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy, + int preferred), PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ, + int preferred), PNG_EMPTY); + +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, + png_colorspacerp colorspace, int intent, int preferred), PNG_EMPTY); +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, png_const_bytep profile, int preferred), + PNG_EMPTY); + /* The 'name' is used for information only */ + +/* Routines for checking parts of an ICC profile. */ +PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length), PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* first 132 bytes only */), PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_icc_set_gAMA_and_cHRM,( + png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_const_bytep profile, int preferred), PNG_EMPTY); +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, + (png_structrp png_ptr), PNG_EMPTY); + /* Set the rgb_to_gray coefficients from the colorspace Y values */ +#endif /* READ_RGB_TO_GRAY */ +#endif /* COLORSPACE */ /* Added at libpng version 1.4.0 */ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, diff --git a/pngread.c b/pngread.c index 78285b5d2..ef348fa07 100644 --- a/pngread.c +++ b/pngread.c @@ -1278,41 +1278,17 @@ png_image_read_header(png_voidp argument) image->format = format; - /* Now try to work out whether the color data does not match sRGB. */ - if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && - (info_ptr->valid & PNG_INFO_sRGB) == 0) - { - /* gamma is irrelevant because libpng does gamma correction, what - * matters is if the cHRM chunk doesn't match or, in the absence of - * cRHM, if the iCCP profile appears to have different end points. - */ - if (info_ptr->valid & PNG_INFO_cHRM) - { - /* TODO: this is a copy'n'paste from pngrutil.c, make a common - * checking function. This checks for a 1% error. - */ - /* The cHRM chunk is used in preference to iCCP */ - 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, 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, 60000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->x_blue, 15000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->y_blue, 6000, 1000)) - image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; - } - - else if (info_ptr->valid & PNG_INFO_iCCP) - { -# if 0 - /* TODO: IMPLEMENT THIS! */ - /* Here if we just have an iCCP chunk. */ - if (!png_iCCP_is_sRGB(png_ptr, info_ptr)) -# endif - image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; - } - } +#ifdef PNG_COLORSPACE_SUPPORTED + /* Does the colorspace match sRGB? If there is no color endpoint + * (colorant) information assume yes, otherwise require the + * 'ENDPOINTS_MATCHE_sRGB' colorspace flag to have been set. If the + * colorspace has been determined to be invalid ignore it. + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags + & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB| + PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS)) + image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; +#endif } /* We need the maximum number of entries regardless of the format the @@ -1529,7 +1505,7 @@ png_image_skip_unused_chunks(png_structrp png_ptr) static void set_file_encoding(png_image_read_control *display) { - png_fixed_point g = display->image->opaque->png_ptr->gamma; + png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; if (png_gamma_significant(g)) { if (png_gamma_not_sRGB(g)) @@ -2388,7 +2364,8 @@ png_image_read_colormap(png_voidp argument) * alternative of double gamma correction. */ if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || - png_ptr->num_trans > 0) && png_gamma_not_sRGB(png_ptr->gamma)) + png_ptr->num_trans > 0) && + png_gamma_not_sRGB(png_ptr->colorspace.gamma)) { cmap_entries = make_gray_file_colormap(display); data_encoding = E_FILE; @@ -2421,7 +2398,7 @@ png_image_read_colormap(png_voidp argument) gray = png_sRGB_table[gray]; /* now E_LINEAR */ gray = PNG_DIV257(png_gamma_16bit_correct(gray, - png_ptr->gamma)); /* now E_FILE */ + png_ptr->colorspace.gamma)); /* now E_FILE */ /* And make sure the corresponding palette entry contains * exactly the required sRGB value. @@ -3582,8 +3559,8 @@ png_image_read_direct(png_voidp argument) * yet; it's set below. png_struct::gamma, however, is set to the * final value. */ - if (png_muldiv(>est, output_gamma, png_ptr->gamma, PNG_FP_1) && - !png_gamma_significant(gtest)) + if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, + PNG_FP_1) && !png_gamma_significant(gtest)) do_local_background = 0; else if (mode == PNG_ALPHA_STANDARD) diff --git a/pngrtran.c b/pngrtran.c index 55ea99134..9419c332a 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -349,8 +349,11 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, * the side effect that the gamma in a second call to png_set_alpha_mode will * be ignored.) */ - if (png_ptr->gamma == 0) - png_ptr->gamma = file_gamma; + if (png_ptr->colorspace.gamma == 0) + { + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } /* But always set the output gamma: */ png_ptr->screen_gamma = output_gamma; @@ -362,7 +365,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, { /* And obtain alpha pre-multiplication by composing on black: */ png_memset(&png_ptr->background, 0, sizeof png_ptr->background); - png_ptr->background_gamma = png_ptr->gamma; /* just in case */ + png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; @@ -825,7 +828,8 @@ png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, * file if a gAMA chunk was present. png_set_alpha_mode provides a * different, easier, way to default the file gamma. */ - png_ptr->gamma = file_gamma; + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; png_ptr->screen_gamma = scrn_gamma; } @@ -971,6 +975,7 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, png_error(png_ptr, "invalid error action to rgb_to_gray"); break; } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) #ifdef PNG_READ_EXPAND_SUPPORTED png_ptr->transformations |= PNG_EXPAND; @@ -1006,7 +1011,7 @@ png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, else { if (red >= 0 && green >= 0) - png_warning(png_ptr, + png_app_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); /* Use the defaults, from the cHRM chunk if set, else the historical @@ -1286,17 +1291,17 @@ png_init_read_transformations(png_structrp png_ptr) */ int gamma_correction = 0; - if (png_ptr->gamma != 0) /* has been set */ + if (png_ptr->colorspace.gamma != 0) /* has been set */ { if (png_ptr->screen_gamma != 0) /* screen set too */ - gamma_correction = png_gamma_threshold(png_ptr->gamma, + gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, png_ptr->screen_gamma); else /* Assume the output matches the input; a long time default behavior * of libpng, although the standard has nothing to say about this. */ - png_ptr->screen_gamma = png_reciprocal(png_ptr->gamma); + png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); } else if (png_ptr->screen_gamma != 0) @@ -1305,7 +1310,7 @@ png_init_read_transformations(png_structrp png_ptr) * png_set_alpha_mode (even if the alpha handling mode isn't required * or isn't changed from the default.) */ - png_ptr->gamma = png_reciprocal(png_ptr->screen_gamma); + png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); else /* neither are set */ /* Just in case the following prevents any processing - file and screen @@ -1313,7 +1318,10 @@ png_init_read_transformations(png_structrp png_ptr) * third gamma value other than png_set_background with 'UNIQUE', and, * prior to 1.5.4 */ - png_ptr->screen_gamma = png_ptr->gamma = PNG_FP_1; + png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; + + /* We have a gamma value now. */ + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; /* Now turn the gamma transformation on or off as appropriate. Notice * that PNG_GAMMA just refers to the file->screen correction. Alpha @@ -1395,9 +1403,16 @@ png_init_read_transformations(png_structrp png_ptr) } #endif -#if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_READ_BACKGROUND_SUPPORTED) && \ - defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Make sure the coefficients for the rgb to gray convertion are set + * appropriately. + */ + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + png_colorspace_set_rgb_coefficients(png_ptr); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +#if defined PNG_READ_EXPAND_SUPPORTED && defined PNG_READ_BACKGROUND_SUPPORTED /* Detect gray background and attempt to enable optimization for * gray --> RGB case. * @@ -1439,7 +1454,8 @@ png_init_read_transformations(png_structrp png_ptr) } } } -#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED (etc) */ +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */ /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations * can be performed directly on the palette, and some (such as rgb to gray) @@ -1535,10 +1551,10 @@ png_init_read_transformations(png_structrp png_ptr) */ if ((png_ptr->transformations & PNG_GAMMA) || ((png_ptr->transformations & PNG_RGB_TO_GRAY) - && (png_gamma_significant(png_ptr->gamma) || + && (png_gamma_significant(png_ptr->colorspace.gamma) || png_gamma_significant(png_ptr->screen_gamma))) || ((png_ptr->transformations & PNG_COMPOSE) - && (png_gamma_significant(png_ptr->gamma) + && (png_gamma_significant(png_ptr->colorspace.gamma) || png_gamma_significant(png_ptr->screen_gamma) # ifdef PNG_READ_BACKGROUND_SUPPORTED || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE @@ -1595,8 +1611,8 @@ png_init_read_transformations(png_structrp png_ptr) break; case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->gamma); - gs = png_reciprocal2(png_ptr->gamma, + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma); break; @@ -1704,8 +1720,9 @@ png_init_read_transformations(png_structrp png_ptr) break; case PNG_BACKGROUND_GAMMA_FILE: - g = png_reciprocal(png_ptr->gamma); - gs = png_reciprocal2(png_ptr->gamma, png_ptr->screen_gamma); + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); break; case PNG_BACKGROUND_GAMMA_UNIQUE: @@ -1944,8 +1961,12 @@ png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) * however it seems that the code in png_init_read_transformations, which has * been called before this from png_read_update_info->png_read_start_row * sometimes does the gamma transform and cancels the flag. + * + * TODO: this looks wrong; the info_ptr should end up with a gamma equal to + * the screen_gamma value. The following probably results in weirdness if + * the info_ptr is used by the app after the rows have been read. */ - info_ptr->gamma = png_ptr->gamma; + info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; #endif if (info_ptr->bit_depth == 16) diff --git a/pngrutil.c b/pngrutil.c index 6d7eec45d..38a553bc6 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -402,6 +402,7 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) } } +#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED /* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to * allow the caller to do multiple calls if required. If the 'finish' flag is * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must @@ -531,7 +532,6 @@ png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, } } -#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED /* * Decompress trailing data in a chunk. The assumption is that read_buffer * points at an allocated area holding the contents of a chunk with a @@ -704,6 +704,73 @@ png_decompress_chunk(png_structrp png_ptr, } #endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */ +#ifdef PNG_READ_iCCP_SUPPORTED +/* Perform a partial read and decompress, producing 'avail_out' bytes and + * reading from the current chunk as required. + */ +static int +png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, + png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, + int finish) +{ + if (png_ptr->zowner == png_ptr->chunk_name) + { + int ret; + + /* next_in and avail_in must have been initialized by the caller. */ + png_ptr->zstream.next_out = next_out; + png_ptr->zstream.avail_out = 0; /* set in the loop */ + + do + { + if (png_ptr->zstream.avail_in == 0) + { + if (read_size > *chunk_bytes) + read_size = (uInt)*chunk_bytes; + *chunk_bytes -= read_size; + + if (read_size > 0) + png_crc_read(png_ptr, read_buffer, read_size); + + png_ptr->zstream.next_in = read_buffer; + png_ptr->zstream.avail_in = read_size; + } + + if (png_ptr->zstream.avail_out == 0) + { + uInt avail = ZLIB_IO_MAX; + if (avail > *out_size) + avail = (uInt)*out_size; + *out_size -= avail; + + png_ptr->zstream.avail_out = avail; + } + + /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all + * the available output is produced; this allows reading of truncated + * streams. + */ + ret = inflate(&png_ptr->zstream, + *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } + while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0)); + + *out_size += png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */ + + /* Ensure the error message pointer is always set: */ + png_zstream_error(png_ptr, ret); + return ret; + } + + else + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } +} +#endif + /* Read and check the IDHR chunk */ void /* PRIVATE */ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) @@ -716,11 +783,11 @@ png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_IHDR"); if (png_ptr->mode & PNG_HAVE_IHDR) - png_error(png_ptr, "Out of place IHDR"); + png_chunk_error(png_ptr, "out of place"); /* Check the length */ if (length != 13) - png_error(png_ptr, "Invalid IHDR chunk"); + png_chunk_error(png_ptr, "invalid"); png_ptr->mode |= PNG_HAVE_IHDR; @@ -792,25 +859,32 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_PLTE"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before PLTE"); + png_chunk_error(png_ptr, "missing IHDR"); + + /* Moved to before the 'after IDAT' check below because otherwise duplicate + * PLTE chunks are potentially ignored (the spec says there shall not be more + * than one PLTE, the error is not treated as benign, so this check trumps + * the requirement that PLTE appears before IDAT.) + */ + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_chunk_error(png_ptr, "duplicate"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid PLTE after IDAT"); + /* This is benign because the non-benign error happened before, when an + * IDAT was encountered in a color-mapped image with no PLTE. + */ png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } - else if (png_ptr->mode & PNG_HAVE_PLTE) - png_error(png_ptr, "Duplicate PLTE chunk"); - png_ptr->mode |= PNG_HAVE_PLTE; - if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) { - png_warning(png_ptr, - "Ignoring PLTE chunk in grayscale PNG"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); return; } @@ -824,19 +898,18 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) { + png_crc_finish(png_ptr, length); + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - { - png_warning(png_ptr, "Invalid palette chunk"); - png_crc_finish(png_ptr, length); - return; - } + png_chunk_benign_error(png_ptr, "invalid"); else - { - png_error(png_ptr, "Invalid palette chunk"); - } + png_chunk_error(png_ptr, "invalid"); + + return; } + /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ num = (int)length / 3; #ifdef PNG_POINTER_INDEXING_SUPPORTED @@ -881,6 +954,10 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * we have two options: an error abort, or a warning and we * ignore the data in this chunk (which should be OK, since * it's considered ancillary for a RGB or RGBA image). + * + * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the + * chunk type to determine whether to check the ancillary or the critical + * flags. */ if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) { @@ -904,28 +981,51 @@ png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #endif + /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its + * own copy of the palette. This has the side effect that when png_start_row + * is called (this happens after any call to png_read_update_info) the + * info_ptr palette gets changed. This is extremely unexpected and + * confusing. + * + * Fix this by not sharing the palette in this way. + */ png_set_PLTE(png_ptr, info_ptr, palette, num); + /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before + * IDAT. Prior to 1.6.0 this was not checked; instead the code merely + * checked the apparent validity of a tRNS chunk inserted before PLTE on a + * palette PNG. 1.6.0 attempts to rigorously follow the standard and + * therefore does a benign error if the erroneous condition is detected *and* + * cancels the tRNS if the benign error returns. The alternative is to + * amend the standard since it would be rather hypocritical of the standards + * maintainers to ignore it. + */ #ifdef PNG_READ_tRNS_SUPPORTED - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if (png_ptr->num_trans > 0 || + (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) { - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) - { - if (png_ptr->num_trans > (png_uint_16)num) - { - png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); - png_ptr->num_trans = (png_uint_16)num; - } + /* Cancel this because otherwise it would be used if the transforms + * require it. Don't cancel the 'valid' flag because this would prevent + * detection of duplicate chunks. + */ + png_ptr->num_trans = 0; - if (info_ptr->num_trans > (png_uint_16)num) - { - png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); - info_ptr->num_trans = (png_uint_16)num; - } - } + if (info_ptr != NULL) + info_ptr->num_trans = 0; + + png_chunk_benign_error(png_ptr, "tRNS must be after"); } #endif +#ifdef PNG_READ_hIST_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) + png_chunk_benign_error(png_ptr, "hIST must be after"); +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + png_chunk_benign_error(png_ptr, "bKGD must be after"); +#endif } void /* PRIVATE */ @@ -934,20 +1034,16 @@ png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_IEND"); if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) - { - png_error(png_ptr, "No image in file"); - } + png_chunk_error(png_ptr, "out of place"); png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); - if (length != 0) - { - png_warning(png_ptr, "Incorrect IEND chunk length"); - } - png_crc_finish(png_ptr, length); - PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */ + if (length != 0) + png_chunk_benign_error(png_ptr, "invalid"); + + PNG_UNUSED(info_ptr) } #ifdef PNG_READ_gAMA_SUPPORTED @@ -960,34 +1056,19 @@ png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_gAMA"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before gAMA"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid gAMA after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - - else if (png_ptr->mode & PNG_HAVE_PLTE) - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Out of place gAMA chunk"); - - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) -#ifdef PNG_READ_sRGB_SUPPORTED - && !(info_ptr->valid & PNG_INFO_sRGB) -#endif - ) - { - png_warning(png_ptr, "Duplicate gAMA chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } if (length != 4) { - png_warning(png_ptr, "Incorrect gAMA chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -998,35 +1079,32 @@ png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) igamma = png_get_fixed_point(NULL, buf); - /* Check for zero gamma or an error. */ + /* The gAMA value is unsigned (and is a power law correction, so 0 is + * meaningless.) + */ if (igamma <= 0) { - png_warning(png_ptr, - "Ignoring gAMA chunk with out of range gamma"); - + png_chunk_benign_error(png_ptr, "out of range"); return; } -# ifdef PNG_READ_sRGB_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) - { - if (PNG_OUT_OF_RANGE(igamma, 45500, 500)) - { - PNG_WARNING_PARAMETERS(p) - png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, igamma); - png_formatted_warning(png_ptr, p, - "Ignoring incorrect gAMA value @1 when sRGB is also present"); - return; - } - } -# endif /* PNG_READ_sRGB_SUPPORTED */ + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; -# ifdef PNG_READ_GAMMA_SUPPORTED - /* Gamma correction on read is supported. */ - png_ptr->gamma = igamma; -# endif - /* And set the 'info' structure members. */ - png_set_gAMA_fixed(png_ptr, info_ptr, igamma); + if (png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_gAMA; + (void)png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma, + 1/*prefer gAMA values*/); + png_colorspace_sync(png_ptr, info_ptr); } #endif @@ -1042,25 +1120,19 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) buf[0] = buf[1] = buf[2] = buf[3] = 0; if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sBIT"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid sBIT after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } - else if (png_ptr->mode & PNG_HAVE_PLTE) - { - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Out of place sBIT chunk"); - } - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) { - png_warning(png_ptr, "Duplicate sBIT chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } @@ -1072,7 +1144,7 @@ png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length != truelen || length > 4) { - png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_chunk_benign_error(png_ptr, "invalid"); png_crc_finish(png_ptr, length); return; } @@ -1108,40 +1180,24 @@ void /* PRIVATE */ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[32]; - png_fixed_point x_white, y_white, x_red, y_red, x_green, y_green, x_blue, - y_blue; + png_xy xy; png_debug(1, "in png_handle_cHRM"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before cHRM"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid cHRM after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - - else if (png_ptr->mode & PNG_HAVE_PLTE) - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Out of place cHRM chunk"); - - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) -# ifdef PNG_READ_sRGB_SUPPORTED - && !(info_ptr->valid & PNG_INFO_sRGB) -# endif - ) - { - png_warning(png_ptr, "Duplicate cHRM chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } if (length != 32) { - png_warning(png_ptr, "Incorrect cHRM chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1150,141 +1206,44 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (png_crc_finish(png_ptr, 0)) return; - x_white = png_get_fixed_point(NULL, buf); - y_white = png_get_fixed_point(NULL, buf + 4); - x_red = png_get_fixed_point(NULL, buf + 8); - y_red = png_get_fixed_point(NULL, buf + 12); - x_green = png_get_fixed_point(NULL, buf + 16); - y_green = png_get_fixed_point(NULL, buf + 20); - x_blue = png_get_fixed_point(NULL, buf + 24); - y_blue = png_get_fixed_point(NULL, buf + 28); + xy.whitex = png_get_fixed_point(NULL, buf); + xy.whitey = png_get_fixed_point(NULL, buf + 4); + xy.redx = png_get_fixed_point(NULL, buf + 8); + xy.redy = png_get_fixed_point(NULL, buf + 12); + xy.greenx = png_get_fixed_point(NULL, buf + 16); + xy.greeny = png_get_fixed_point(NULL, buf + 20); + xy.bluex = png_get_fixed_point(NULL, buf + 24); + xy.bluey = png_get_fixed_point(NULL, buf + 28); - if (x_white == PNG_FIXED_ERROR || - y_white == PNG_FIXED_ERROR || - x_red == PNG_FIXED_ERROR || - y_red == PNG_FIXED_ERROR || - x_green == PNG_FIXED_ERROR || - y_green == PNG_FIXED_ERROR || - x_blue == PNG_FIXED_ERROR || - y_blue == PNG_FIXED_ERROR) + if (xy.whitex == PNG_FIXED_ERROR || + xy.whitey == PNG_FIXED_ERROR || + xy.redx == PNG_FIXED_ERROR || + xy.redy == PNG_FIXED_ERROR || + xy.greenx == PNG_FIXED_ERROR || + xy.greeny == PNG_FIXED_ERROR || + xy.bluex == PNG_FIXED_ERROR || + xy.bluey == PNG_FIXED_ERROR) { - png_warning(png_ptr, "Ignoring cHRM chunk with negative chromaticities"); + png_chunk_benign_error(png_ptr, "invalid values"); return; } -#ifdef PNG_READ_sRGB_SUPPORTED - if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; + + if (png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) { - if (PNG_OUT_OF_RANGE(x_white, 31270, 1000) || - PNG_OUT_OF_RANGE(y_white, 32900, 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, 60000, 1000) || - PNG_OUT_OF_RANGE(x_blue, 15000, 1000) || - PNG_OUT_OF_RANGE(y_blue, 6000, 1000)) - { - PNG_WARNING_PARAMETERS(p) - - png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, x_white); - png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_fixed, y_white); - png_warning_parameter_signed(p, 3, PNG_NUMBER_FORMAT_fixed, x_red); - png_warning_parameter_signed(p, 4, PNG_NUMBER_FORMAT_fixed, y_red); - png_warning_parameter_signed(p, 5, PNG_NUMBER_FORMAT_fixed, x_green); - png_warning_parameter_signed(p, 6, PNG_NUMBER_FORMAT_fixed, y_green); - png_warning_parameter_signed(p, 7, PNG_NUMBER_FORMAT_fixed, x_blue); - png_warning_parameter_signed(p, 8, PNG_NUMBER_FORMAT_fixed, y_blue); - - png_formatted_warning(png_ptr, p, - "Ignoring incorrect cHRM white(@1,@2) r(@3,@4)g(@5,@6)b(@7,@8) " - "when sRGB is also present"); - } + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "duplicate"); return; } -#endif /* PNG_READ_sRGB_SUPPORTED */ -#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - /* Store the _white values as default coefficients for the rgb to gray - * operation if it is supported. Check if the transform is already set to - * avoid destroying the transform values. - */ - if (!png_ptr->rgb_to_gray_coefficients_set) - { - /* png_set_background has not been called and we haven't seen an sRGB - * chunk yet. Find the XYZ of the three end points. - */ - 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 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_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 - - png_set_cHRM_fixed(png_ptr, info_ptr, x_white, y_white, x_red, y_red, - x_green, y_green, x_blue, y_blue); + png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, + 1/*prefer cHRM values*/); + png_colorspace_sync(png_ptr, info_ptr); } #endif @@ -1292,129 +1251,65 @@ png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) void /* PRIVATE */ png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - int intent; - png_byte buf[1]; + png_byte intent; png_debug(1, "in png_handle_sRGB"); - if (png_ptr->mode & PNG_HAVE_iCCP) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate color profile"); - return; - } - - png_ptr->mode |= PNG_HAVE_iCCP; /* well, a colorspace */ - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "missing IHDR"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid after IDAT"); + png_chunk_benign_error(png_ptr, "out of place"); return; } - else if (png_ptr->mode & PNG_HAVE_PLTE) - /* Should be an error, but we can cope with it */ - png_chunk_benign_error(png_ptr, "out of place"); - if (length != 1) { png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "incorrect length"); + png_chunk_benign_error(png_ptr, "invalid"); return; } - png_crc_read(png_ptr, buf, 1); + png_crc_read(png_ptr, &intent, 1); if (png_crc_finish(png_ptr, 0)) return; - intent = buf[0]; - /* Check for bad intent */ if (intent >= PNG_sRGB_INTENT_LAST) { - png_warning(png_ptr, "Unknown sRGB intent"); + png_chunk_benign_error(png_ptr, "Unknown sRGB intent"); return; } -#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, 45500, 500)) - { - PNG_WARNING_PARAMETERS(p) + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; - png_warning_parameter_signed(p, 1, PNG_NUMBER_FORMAT_fixed, - info_ptr->gamma); - - png_formatted_warning(png_ptr, p, - "Ignoring incorrect gAMA value @1 when sRGB is also present"); - } - } -#endif /* PNG_READ_gAMA_SUPPORTED */ - -#ifdef PNG_READ_cHRM_SUPPORTED - 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, 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, 60000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->x_blue, 15000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->y_blue, 6000, 1000)) - { - png_warning(png_ptr, - "Ignoring incorrect cHRM value when sRGB is also present"); - } -#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. + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. */ - png_ptr->is_sRGB = 1; + if (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "too many profiles"); + return; + } -# 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); + /* Do not override gAMA or cHRM from the PNG file; just check they match. + * This is because we write a cHRM which corresponds to D65, however there is + * an issue with CMMs that assume a D50 environment that requires adaptation + * of the white point. This way at least it is possible to supply an + * adapated value (so long as it is within the tolerance limits for a match + * against the D65 chromaticities.) + * + * TODO: get expert opinions on this issue + */ + (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent, 0); + png_colorspace_sync(png_ptr, info_ptr); } #endif /* PNG_READ_sRGB_SUPPORTED */ @@ -1423,141 +1318,251 @@ void /* PRIVATE */ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Note: this does not properly handle profiles that are > 64K under DOS */ { - png_uint_32 keyword_length; - png_const_charp error_message = NULL; - png_bytep buffer; + png_const_charp errmsg; png_debug(1, "in png_handle_iCCP"); - /* Do this first to set the 'HAVE_iCCP' flag in all cases, even errors */ - if (png_ptr->mode & PNG_HAVE_iCCP) - { - png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "duplicate color profile"); - return; - } - - png_ptr->mode |= PNG_HAVE_iCCP; - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "missing IHDR"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { png_crc_finish(png_ptr, length); - png_chunk_benign_error(png_ptr, "invalid after IDAT"); - return; - } - - else if (png_ptr->mode & PNG_HAVE_PLTE) - { - /* Ignore out-of-place iCCP chunks because other implementations may - * *have* to do so, so it is misleading if libpng handles them. - */ - png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "out of place"); return; } - else if (length < 132) /* minimum ICC profile size */ + /* Consistent with all the above colorspace handling an obviously *invalid* + * chunk is just ignored, so does not invalidate the color space. An + * alternative is to set the 'invalid' flags at the start of this routine + * and only clear them in they were not set before and all the tests pass. + * The minimum 'deflate' stream is assumed to be just the 2 byte header and 4 + * byte checksum. The keyword must be one character and there is a + * terminator (0) byte and the compression method. + */ + if (length < 9) { png_crc_finish(png_ptr, length); png_chunk_benign_error(png_ptr, "too short"); return; } - /* Now read the first 132 bytes. The largest LZ data requirement for 132 - * bytes of input is infinite; by doing really foolish things with the SYNC - * options to deflate it is possible to extend 132 bytes without limit. - */ - /**** WIP ****/ - - /* TODO: read the chunk in pieces, validating it as we go. */ - buffer = png_read_buffer(png_ptr, length, 0/*!warn*/); - - png_crc_read(png_ptr, buffer, length); - - if (png_crc_finish(png_ptr, 0)) - return; - - /* TODO: also check that the keyword contents match the spec! */ - for (keyword_length = 0; - keyword_length < length && buffer[keyword_length] != 0; - ++keyword_length) - /* Empty loop to find end of name */ ; - - if (keyword_length > 79 || keyword_length < 1) + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) { - png_chunk_benign_error(png_ptr, "Bad iCCP keyword"); + png_crc_finish(png_ptr, length); return; } - /* There should be at least one zero (the compression type byte) following - * the separator followed by some LZ data; check for at least one byte. - * Since a chunk length is no more than 2^31 the addition below is safe. + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. */ - if (keyword_length + 3 >= length) + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0) { - png_chunk_benign_error(png_ptr, "iCCP chunk too short"); - return; - } + uInt read_length, keyword_length; + char keyword[81]; - /* We only understand '0' compression - deflate - so if we get a different - * value we can't safely decode the chunk. - */ - if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE) - { - png_chunk_benign_error(png_ptr, "Bad compression type in iCCP chunk"); - return; - } - - { - png_alloc_size_t uncompressed_length; - - /* TODO: this is slightly broken, an ICC profile can be up to 2^32-1 bytes - * in length, but it is stored here with the keyword and the compression - * type prefixed to it. This means that on 32-bit systems the code can't - * accept, or express, the maximum length. + /* Find the keyword; the keyword plus separator and compression method + * bytes can be at most 81 characters long. */ - if (PNG_SIZE_MAX > 0xffffffffU/*ICC profile maximum*/) - uncompressed_length = 0xffffffffU; + read_length = 81; /* maximum */ + if (read_length > length) + read_length = (uInt)length; - else - uncompressed_length = PNG_SIZE_MAX; + png_crc_read(png_ptr, (png_bytep)keyword, read_length); + length -= read_length; - /* TODO: at present png_decompress_chunk imposes a single application - * level memory limit, this should be split to different values for iCCP - * and text chunks. - */ - if (png_decompress_chunk(png_ptr, length, keyword_length+2, - &uncompressed_length, 0/*do not terminate*/) == Z_STREAM_END) + keyword_length = 0; + while (keyword_length < 80 && keyword_length < read_length && + keyword[keyword_length] != 0) + ++keyword_length; + + /* TODO: make the keyword checking common */ + if (keyword_length >= 1 && keyword_length <= 79) { - /* It worked; png_ptr->read_buffer now contains the full uncompressed - * ICC profile. The cast below is safe because of the checks above, - * the final uncompressed length can be at most 2^32-1 + /* We only understand '0' compression - deflate - so if we get a + * different value we can't safely decode the chunk. */ - png_uint_32 profile_length = (png_uint_32)uncompressed_length; - png_bytep profile = png_ptr->read_buffer + (keyword_length+2); + if (keyword_length+1 < read_length && + keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE) + { + read_length -= keyword_length+2; - /* Now perform some basic checks. - * - * TODO: do a lot more checks, the format is pretty simple, at least - * check that the whole tag table is there. - */ - if (profile_length > 132 && png_get_uint_32(profile) == profile_length) - png_set_iCCP(png_ptr, info_ptr, (png_charp)png_ptr->read_buffer, - PNG_COMPRESSION_TYPE_BASE, profile, profile_length); + if (png_inflate_claim(png_ptr, png_iCCP, + png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0) == Z_OK) + { + int ret; + Byte profile_header[132]; + Byte local_buffer[PNG_INFLATE_BUF_SIZE]; + png_alloc_size_t size = sizeof profile_header; + + png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2); + png_ptr->zstream.avail_in = read_length; + ret = png_inflate_read(png_ptr, local_buffer, + sizeof local_buffer, &length, profile_header, &size, + 0/*finish: don't, because the output is too small*/); + + if (size == 0) + { + /* We have the ICC profile header; do the basic header checks. + */ + const png_uint_32 profile_length = + png_get_uint_32(profile_header); + + errmsg = NULL; /* flag to say error message output */ + + if (png_icc_check_length(png_ptr, &png_ptr->colorspace, + keyword, profile_length)) + { + /* The length is apparently ok, so we can check the 132 + * byte header. + */ + if (png_icc_check_header(png_ptr, &png_ptr->colorspace, + keyword, profile_length, profile_header)) + { + /* Now read the tag table; a variable size buffer is + * needed at this point, allocate one for the whole + * profile. The header check has already validated + * that none of these stuff will overflow. + */ + const png_uint_32 tag_count = png_get_uint_32( + profile_header+128); + png_bytep profile = png_read_buffer(png_ptr, + profile_length, 2/*silent*/); + + if (profile != NULL) + { + memcpy(profile, profile_header, + sizeof profile_header); + + size = (sizeof profile_header) + 12 * tag_count; + ret = png_inflate_read(png_ptr, local_buffer, + sizeof local_buffer, &length, + profile + (sizeof profile_header), &size, 0); + + if (size == 0) + { + if (png_icc_check_tag_table(png_ptr, + &png_ptr->colorspace, keyword, profile_length, + profile)) + { + /* The profile has been validated for basic + * security issues, so read the whole thing in. + */ + size = profile_length - (sizeof profile_header) + - 12 * tag_count; + ret = png_inflate_read(png_ptr, local_buffer, + sizeof local_buffer, &length, + profile + (sizeof profile_header) + + 12 * tag_count, &size, 1/*finish*/); + + if (size == 0) + { + if (ret != Z_STREAM_END) + png_chunk_warning(png_ptr, + png_ptr->zstream.msg); + + else if (length > 0) + png_chunk_warning(png_ptr, + "extra compressed data"); + + /* But, because we got the whole profile, + * assume it is ok. + */ + png_ptr->zowner = 0; + + /* Yet the CRC should still be correct */ + if (png_crc_finish(png_ptr, length)) + return; + + /* Set the gAMA and cHRM information */ + png_icc_set_gAMA_and_cHRM(png_ptr, + &png_ptr->colorspace, keyword, profile, + 0/*prefer explicit gAMA/cHRM*/); + + /* And steal the profile for info_ptr. */ + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, + PNG_FREE_ICCP, 0); + + info_ptr->iccp_name = png_voidcast(char*, + png_malloc_base(png_ptr, + keyword_length+1)); + if (info_ptr->iccp_name == NULL) + { + png_ptr->colorspace.flags |= + PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, + info_ptr); + png_chunk_benign_error(png_ptr, + "out of memory"); + return; + } + + memcpy(info_ptr->iccp_name, keyword, + keyword_length+1); + info_ptr->iccp_proflen = profile_length; + info_ptr->iccp_profile = profile; + png_ptr->read_buffer = NULL; /*steal*/ + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; + + png_colorspace_sync(png_ptr, info_ptr); + } + + return; + } + + else + errmsg = "truncated"; + } + + /* else png_icc_check_tag_table output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "out of memory"; + } + + /* else png_icc_check_header output an error */ + } + + /* else png_icc_check_length output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + + /* Release the stream */ + png_ptr->zowner = 0; + } + + else /* png_inflate_claim failed */ + errmsg = png_ptr->zstream.msg; + } else - error_message = "Malformed ICC profile"; + errmsg = "bad compression method"; /* or missing */ } else - error_message = png_ptr->zstream.msg; + errmsg = "bad keyword"; } + + else + errmsg = "too many profiles"; - if (error_message != NULL) - png_chunk_benign_error(png_ptr, error_message); + /* Failure: the reason is in 'errmsg' */ + png_crc_finish(png_ptr, length); + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + if (errmsg != NULL) /* else already output */ + png_chunk_benign_error(png_ptr, errmsg); } #endif /* PNG_READ_iCCP_SUPPORTED */ @@ -1578,7 +1583,6 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sPLT"); #ifdef PNG_USER_LIMITS_SUPPORTED - if (png_ptr->user_chunk_cache_max != 0) { if (png_ptr->user_chunk_cache_max == 1) @@ -1597,25 +1601,32 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sPLT"); + png_chunk_error(png_ptr, "missing IHDR"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid sPLT after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } #ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) + if (length > 65535U) { - png_warning(png_ptr, "sPLT chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; } #endif - buffer = png_read_buffer(png_ptr, length+1, 0/*!warn*/); + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + /* WARNING: this may break if size_t is less than 32 bits; it is assumed * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a @@ -1741,19 +1752,19 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_tRNS"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before tRNS"); + png_chunk_error(png_ptr, "missing IHDR"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid tRNS after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { - png_warning(png_ptr, "Duplicate tRNS chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } @@ -1763,8 +1774,8 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length != 2) { - png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1779,8 +1790,8 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length != 6) { - png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1795,22 +1806,17 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { if (!(png_ptr->mode & PNG_HAVE_PLTE)) { - /* Should be an error, but we can cope with it. */ - png_warning(png_ptr, "Missing PLTE before tRNS"); - } - - if (length > (png_uint_32)png_ptr->num_palette || - length > PNG_MAX_PALETTE_LENGTH) - { - png_warning(png_ptr, "Incorrect tRNS chunk length"); + /* TODO: is this actually an error in the ISO spec? */ png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } - if (length == 0) + if (length > png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH || + length == 0) { - png_warning(png_ptr, "Zero length tRNS chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1820,8 +1826,8 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else { - png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid with alpha channel"); return; } @@ -1831,6 +1837,10 @@ png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) return; } + /* TODO: this is a horrible side effect in the palette case because the + * png_struct ends up with a pointer to the tRNS buffer owned by the + * png_info. Fix this. + */ png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, &(png_ptr->trans_color)); } @@ -1847,27 +1857,21 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_bKGD"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before bKGD"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if ((png_ptr->mode & PNG_HAVE_IDAT) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE))) { - png_warning(png_ptr, "Invalid bKGD after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - { - png_warning(png_ptr, "Missing PLTE before bKGD"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) { - png_warning(png_ptr, "Duplicate bKGD chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } @@ -1882,8 +1886,8 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length != truelen) { - png_warning(png_ptr, "Incorrect bKGD chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1905,7 +1909,7 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { if (buf[0] >= info_ptr->num_palette) { - png_warning(png_ptr, "Incorrect bKGD chunk index value"); + png_chunk_benign_error(png_ptr, "invalid index"); return; } @@ -1952,36 +1956,28 @@ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_hIST"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before hIST"); + png_chunk_error(png_ptr, "missing IHDR"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid hIST after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - - else if (!(png_ptr->mode & PNG_HAVE_PLTE)) - { - png_warning(png_ptr, "Missing PLTE before hIST"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) { - png_warning(png_ptr, "Duplicate hIST chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } num = length / 2 ; - if (num != (unsigned int)png_ptr->num_palette || num > - (unsigned int)PNG_MAX_PALETTE_LENGTH) + if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH) { - png_warning(png_ptr, "Incorrect hIST chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -2011,26 +2007,26 @@ png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pHYs"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before pHYs"); + png_chunk_error(png_ptr, "missing IHDR"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid pHYs after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_warning(png_ptr, "Duplicate pHYs chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } if (length != 9) { - png_warning(png_ptr, "Incorrect pHYs chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -2057,26 +2053,26 @@ png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_oFFs"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before oFFs"); + png_chunk_error(png_ptr, "missing IHDR"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid oFFs after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_warning(png_ptr, "Duplicate oFFs chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } if (length != 9) { - png_warning(png_ptr, "Incorrect oFFs chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -2106,30 +2102,31 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_pCAL"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before pCAL"); + png_chunk_error(png_ptr, "missing IHDR"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid pCAL after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) { - png_warning(png_ptr, "Duplicate pCAL chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", length + 1); - buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); if (buffer == NULL) { - png_warning(png_ptr, "No memory for pCAL purpose"); + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); return; } @@ -2151,7 +2148,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) */ if (endptr <= buf + 12) { - png_warning(png_ptr, "Invalid pCAL data"); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -2171,13 +2168,13 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) (type == PNG_EQUATION_ARBITRARY && nparams != 3) || (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) { - png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_chunk_benign_error(png_ptr, "invalid parameter count"); return; } else if (type >= PNG_EQUATION_LAST) { - png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + png_chunk_benign_error(png_ptr, "unrecognized equation type"); } for (buf = units; *buf; buf++) @@ -2190,7 +2187,7 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (params == NULL) { - png_warning(png_ptr, "No memory for pCAL params"); + png_chunk_benign_error(png_ptr, "out of memory"); return; } @@ -2207,8 +2204,8 @@ png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Make sure we haven't run out of data yet */ if (buf > endptr) { - png_warning(png_ptr, "Invalid pCAL data"); png_free(png_ptr, params); + png_chunk_benign_error(png_ptr, "invalid data"); return; } } @@ -2232,38 +2229,38 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_sCAL"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sCAL"); + png_chunk_error(png_ptr, "missing IHDR"); else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid sCAL after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { - png_warning(png_ptr, "Duplicate sCAL chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } /* Need unit type, width, \0, height: minimum 4 bytes */ else if (length < 4) { - png_warning(png_ptr, "sCAL chunk too short"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", length + 1); - buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); if (buffer == NULL) { - png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + png_chunk_benign_error(png_ptr, "out of memory"); png_crc_finish(png_ptr, length); return; } @@ -2277,7 +2274,7 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Validate the unit. */ if (buffer[0] != 1 && buffer[0] != 2) { - png_warning(png_ptr, "Invalid sCAL ignored: invalid unit"); + png_chunk_benign_error(png_ptr, "invalid unit"); return; } @@ -2289,10 +2286,10 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) || i >= length || buffer[i++] != 0) - png_warning(png_ptr, "Invalid sCAL chunk ignored: bad width format"); + png_chunk_benign_error(png_ptr, "bad width format"); else if (!PNG_FP_IS_POSITIVE(state)) - png_warning(png_ptr, "Invalid sCAL chunk ignored: non-positive width"); + png_chunk_benign_error(png_ptr, "non-positive width"); else { @@ -2301,11 +2298,10 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) state = 0; if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) || i != length) - png_warning(png_ptr, "Invalid sCAL chunk ignored: bad height format"); + png_chunk_benign_error(png_ptr, "bad height format"); else if (!PNG_FP_IS_POSITIVE(state)) - png_warning(png_ptr, - "Invalid sCAL chunk ignored: non-positive height"); + png_chunk_benign_error(png_ptr, "non-positive height"); else /* This is the (only) success case. */ @@ -2325,12 +2321,12 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) png_debug(1, "in png_handle_tIME"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Out of place tIME chunk"); + png_chunk_error(png_ptr, "missing IHDR"); else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) { - png_warning(png_ptr, "Duplicate tIME chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } @@ -2339,8 +2335,8 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (length != 7) { - png_warning(png_ptr, "Incorrect tIME chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -2384,25 +2380,25 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_warning(png_ptr, "No space in chunk cache for tEXt"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before tEXt"); + png_chunk_error(png_ptr, "missing IHDR"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; #ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) + if (length > 65535U) { - png_warning(png_ptr, "tEXt chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; } #endif @@ -2410,7 +2406,7 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buffer == NULL) { - png_warning(png_ptr, "No memory to process text chunk"); + png_chunk_benign_error(png_ptr, "out of memory"); return; } @@ -2463,24 +2459,25 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_warning(png_ptr, "No space in chunk cache for zTXt"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before zTXt"); + png_chunk_error(png_ptr, "missing IHDR"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; - buffer = png_read_buffer(png_ptr, length, 1/*warn*/); + buffer = png_read_buffer(png_ptr, length, 2/*silent*/); if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "insufficient memory"); + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); return; } @@ -2537,7 +2534,7 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) text.lang_key = NULL; if (png_set_text_2(png_ptr, info_ptr, &text, 1)) - errmsg = "insufficient memory to store zTXt chunk"; + errmsg = "insufficient memory"; } else @@ -2571,15 +2568,15 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (--png_ptr->user_chunk_cache_max == 1) { - png_warning(png_ptr, "No space in chunk cache for iTXt"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before iTXt"); + png_chunk_error(png_ptr, "missing IHDR"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; @@ -2588,7 +2585,8 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) if (buffer == NULL) { - png_chunk_benign_error(png_ptr, "insufficient memory"); + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); return; } @@ -2705,7 +2703,8 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) * case it will be saved away to be written out later. */ void /* PRIVATE */ -png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, + png_uint_32 length) { png_uint_32 skip = 0; @@ -2722,8 +2721,8 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length if (--png_ptr->user_chunk_cache_max == 1) { - png_warning(png_ptr, "No space in chunk cache for unknown chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); return; } } @@ -2758,9 +2757,10 @@ png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length #ifdef PNG_MAX_MALLOC_64K if (length > 65535) { - png_warning(png_ptr, "unknown chunk too large to fit in memory"); - skip = length - 65535; - length = 65535; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, + "unknown chunk too large to fit in memory"); + return; } #endif diff --git a/pngset.c b/pngset.c index c8df0dc09..ead9817ad 100644 --- a/pngset.c +++ b/pngset.c @@ -42,26 +42,27 @@ png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, png_fixed_point blue_y) { + png_xy xy; + png_debug1(1, "in %s storage function", "cHRM fixed"); if (png_ptr == NULL || info_ptr == NULL) return; -# ifdef PNG_CHECK_cHRM_SUPPORTED - if (png_check_cHRM_fixed(png_ptr, - white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) -# endif - { - info_ptr->x_white = white_x; - info_ptr->y_white = white_y; - info_ptr->x_red = red_x; - info_ptr->y_red = red_y; - info_ptr->x_green = green_x; - info_ptr->y_green = green_y; - info_ptr->x_blue = blue_x; - info_ptr->y_blue = blue_y; - info_ptr->valid |= PNG_INFO_cHRM; - } + xy.redx = red_x; + xy.redy = red_y; + xy.greenx = green_x; + xy.greeny = green_y; + xy.bluex = blue_x; + xy.bluey = blue_y; + xy.whitex = white_x; + xy.whitey = white_y; + + if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, + 2/* override with app values*/)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); } void PNGFAPI @@ -73,7 +74,6 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, png_fixed_point int_blue_Z) { png_XYZ XYZ; - png_xy xy; png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); @@ -90,11 +90,10 @@ png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, 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"); + if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; - png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy, - xy.greenx, xy.greeny, xy.bluex, xy.bluey); + png_colorspace_sync_info(png_ptr, info_ptr); } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -153,12 +152,15 @@ png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, * displays that are all black or all white.) */ if (file_gamma < 16 || file_gamma > 625000000) - png_warning(png_ptr, "Out of range gamma value ignored"); + png_app_error(png_ptr, "Out of range gamma value ignored"); else { - info_ptr->gamma = file_gamma; - info_ptr->valid |= PNG_INFO_gAMA; + if (png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma, + 2/* overrided with app value */)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_gAMA; + + png_colorspace_sync_info(png_ptr, info_ptr); } } @@ -593,8 +595,10 @@ png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) if (png_ptr == NULL || info_ptr == NULL) return; - info_ptr->srgb_intent = (png_byte)srgb_intent; - info_ptr->valid |= PNG_INFO_sRGB; + (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent, + 2/* app value overrides*/); + + png_colorspace_sync_info(png_ptr, info_ptr); } void PNGAPI @@ -606,21 +610,15 @@ png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - png_set_sRGB(png_ptr, info_ptr, srgb_intent); + if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent, + 2/* app value overrides*/)) + { + /* And cause the gAMA and cHRM to be written too */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } -# ifdef PNG_gAMA_SUPPORTED - png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); -# endif - -# ifdef PNG_cHRM_SUPPORTED - png_set_cHRM_fixed(png_ptr, info_ptr, - /* color x y */ - /* white */ 31270, 32900, - /* red */ 64000, 33000, - /* green */ 30000, 60000, - /* blue */ 15000, 6000 - ); -# endif /* cHRM */ + png_colorspace_sync_info(png_ptr, info_ptr); } #endif /* sRGB */ @@ -640,12 +638,34 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) return; + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_app_error(png_ptr, "Invalid iCCP compression method"); + + /* Set the colorspace first because this validates the profile; do not + * override previously set app cHRM or gAMA here (because likely as not the + * application knows better than libpng what the correct values are.) + */ + { + int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, + proflen, profile, 0/* do *not* override the app cHRM or gAMA */); + + png_colorspace_sync_info(png_ptr, info_ptr); + + /* Don't do any of the copying if the profile was bad, or inconsistent. */ + if (!result) + return; + + /* But do write the gAMA and cHRM chunks from the profile. */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } + length = png_strlen(name)+1; new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); if (new_iccp_name == NULL) { - png_warning(png_ptr, "Insufficient memory to process iCCP chunk"); + png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); return; } @@ -655,8 +675,8 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, if (new_iccp_profile == NULL) { - png_free (png_ptr, new_iccp_name); - png_warning(png_ptr, + png_free(png_ptr, new_iccp_name); + png_benign_error(png_ptr, "Insufficient memory to process iCCP profile"); return; } @@ -668,10 +688,6 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, info_ptr->iccp_proflen = proflen; info_ptr->iccp_name = new_iccp_name; info_ptr->iccp_profile = new_iccp_profile; - /* Compression is always zero but is here so the API and info structure - * does not have to change if we introduce multiple compression types - */ - info_ptr->iccp_compression = (png_byte)compression_type; info_ptr->free_me |= PNG_FREE_ICCP; info_ptr->valid |= PNG_INFO_iCCP; } @@ -1081,7 +1097,7 @@ png_set_unknown_chunks(png_const_structrp png_ptr, to->size = from->size; /* Note our location in the read or write sequence */ - to->location = (png_byte)(png_ptr->mode & 0xff); + to->location = (png_byte)png_ptr->mode; if (from->size == 0) to->data=NULL; diff --git a/pngstruct.h b/pngstruct.h index 88a5ddad6..5debf3870 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -69,6 +69,76 @@ typedef struct png_compression_buffer (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size) #endif +/* Colorspace support; structures used in png_struct, png_info and in internal + * functions to hold and communicate information about the color space. + * + * PNG_COLORSPACE_SUPPORTED is only required if the application will perform + * colorspace corrections, otherwise all the colorspace information can be + * skipped and the size of libpng can be reduced (significantly) by compiling + * out the colorspace support. + */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* The chromaticities of the red, green and blue colorants and the chromaticity + * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)). + */ +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; + +/* The same data as above but encoded as CIE XYZ values. When this data comes + * from chromaticities the sum of the Y values is assumed to be 1.0 + */ +typedef struct png_XYZ +{ + png_fixed_point redX, redY, redZ; + png_fixed_point greenX, greenY, greenZ; + png_fixed_point blueX, blueY, blueZ; +} png_XYZ; +#endif /* COLORSPACE */ + +#if defined PNG_COLORSPACE_SUPPORTED || defined PNG_GAMMA_SUPPORTED +/* A colorspace is all the above plus, potentially, profile information, + * however at present libpng does not use the profile internally so it is only + * stored in the png_info struct (if iCCP is supported.) The rendering intent + * is retained here and is checked. + * + * The file gamma encoding information is also stored here and gamma correction + * is done by libpng, whereas color correction must currently be done by the + * application. + */ +typedef struct png_colorspace +{ +#ifdef PNG_GAMMA_SUPPORTED + png_fixed_point gamma; /* File gamma */ +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED + png_xy end_points_xy; /* End points as chromaticities */ + png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ + png_uint_16 rendering_intent; /* Rendering intent of a profile */ +#endif + + /* Flags are always defined to simplify the code. */ + png_uint_16 flags; /* As defined below */ +} png_colorspace, * PNG_RESTRICT png_colorspacerp; + +typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; + +#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 +#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 +#define PNG_COLORSPACE_HAVE_INTENT 0x0004 +#define PNG_COLORSPACE_FROM_gAMA 0x0008 +#define PNG_COLORSPACE_FROM_cHRM 0x0010 +#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0020 +#define PNG_COLORSPACE_MATCHES_sRGB 0x0040 /* exact match on profile */ +#define PNG_COLORSPACE_INVALID 0x8000 +#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) +#endif /* COLORSPACE || GAMMA */ + struct png_struct_def { #ifdef PNG_SETJMP_SUPPORTED @@ -153,10 +223,12 @@ struct png_struct_def png_bytep row_buf; /* buffer to save current (unfiltered) row. * This is a pointer into big_row_buf */ +#ifdef PNG_WRITE_SUPPORTED png_bytep sub_row; /* buffer to save "sub" row when filtering */ png_bytep up_row; /* buffer to save "up" row when filtering */ png_bytep avg_row; /* buffer to save "avg" row when filtering */ png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ png_uint_32 idat_size; /* current IDAT size for read */ @@ -186,11 +258,6 @@ struct png_struct_def /* pixel depth used for the row buffers */ png_byte transformed_pixel_depth; /* pixel depth after read/write transforms */ -#if PNG_LIBPNG_VER < 10600 - png_byte io_chunk_string[5]; - /* string name of chunk */ -#endif - #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) png_uint_16 filler; /* filler bytes for pixel expansion */ #endif @@ -213,7 +280,6 @@ struct png_struct_def #ifdef PNG_READ_GAMMA_SUPPORTED int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ - png_fixed_point gamma; /* file gamma value */ png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ png_bytep gamma_table; /* gamma table for 8-bit depth files */ @@ -307,11 +373,6 @@ 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; @@ -405,5 +466,11 @@ struct png_struct_def void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, png_bytep row, png_const_bytep prev_row); + +#ifdef PNG_READ_SUPPORTED +#if defined PNG_COLORSPACE_SUPPORTED || defined PNG_GAMMA_SUPPORTED + png_colorspace colorspace; +#endif +#endif }; #endif /* PNGSTRUCT_H */ diff --git a/pngwrite.c b/pngwrite.c index 8b3ed0604..606ee5e3f 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -28,7 +28,7 @@ * them in png_write_end(), and compressing them. */ void PNGAPI -png_write_info_before_PLTE(png_structrp png_ptr, png_inforp info_ptr) +png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) { png_debug(1, "in png_write_info_before_PLTE"); @@ -60,32 +60,61 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_inforp info_ptr) #endif /* The rest of these check to see if the valid field has the appropriate * flag set, and if it does, writes the chunk. + * + * 1.6.0: COLORSPACE support controls the writing of these chunks too, and + * the chunks will be written if the WRITE routine is there and information + * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c + * for where the valid flags get set.) + * + * Under certain circumstances the colorspace can be invalidated without + * syncing the info_struct 'valid' flags; this happens if libpng detects and + * error and calls png_error while the color space is being set, yet the + * application continues writing the PNG. So check the 'invalid' flag here + * too. */ -#ifdef PNG_WRITE_gAMA_SUPPORTED - if (info_ptr->valid & PNG_INFO_gAMA) - png_write_gAMA_fixed(png_ptr, info_ptr->gamma); -#endif -#ifdef PNG_WRITE_sRGB_SUPPORTED - if (info_ptr->valid & PNG_INFO_sRGB) - png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#ifdef PNG_GAMMA_SUPPORTED +# ifdef PNG_WRITE_gAMA_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) && + (info_ptr->valid & PNG_INFO_gAMA)) + png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); +# endif #endif -#ifdef PNG_WRITE_iCCP_SUPPORTED - if (info_ptr->valid & PNG_INFO_iCCP) - png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, - info_ptr->iccp_profile, info_ptr->iccp_proflen); -#endif +#ifdef PNG_COLORSPACE_SUPPORTED + /* Write only one of sRGB or an ICC profile, favour sRGB if the profile + * matches sRGB. + */ +# ifdef PNG_WRITE_sRGB_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->valid & PNG_INFO_sRGB)) + png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); + +# ifdef PNG_WRITE_iCCP_SUPPORTED + else +# endif +# endif /* WRITE_sRGB */ + +# ifdef PNG_WRITE_iCCP_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->valid & PNG_INFO_iCCP)) + png_write_iCCP(png_ptr, info_ptr->iccp_name, + info_ptr->iccp_profile); +# endif +#endif /* COLORSPACE */ + #ifdef PNG_WRITE_sBIT_SUPPORTED if (info_ptr->valid & PNG_INFO_sBIT) png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); #endif -#ifdef PNG_WRITE_cHRM_SUPPORTED - if (info_ptr->valid & PNG_INFO_cHRM) - png_write_cHRM_fixed(png_ptr, - info_ptr->x_white, info_ptr->y_white, - info_ptr->x_red, info_ptr->y_red, - info_ptr->x_green, info_ptr->y_green, - info_ptr->x_blue, info_ptr->y_blue); + +#ifdef PNG_COLORSPACE_SUPPORTED +# ifdef PNG_WRITE_cHRM_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) && + (info_ptr->valid & PNG_INFO_cHRM)) + png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); +# endif #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED @@ -122,7 +151,7 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_inforp info_ptr) } void PNGAPI -png_write_info(png_structrp png_ptr, png_inforp info_ptr) +png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) { #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) int i; diff --git a/pngwutil.c b/pngwutil.c index 44a841359..a6a659bc0 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1200,20 +1200,23 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent) #ifdef PNG_WRITE_iCCP_SUPPORTED /* Write an iCCP chunk */ void /* PRIVATE */ -png_write_iCCP(png_structrp png_ptr, png_const_charp name, int compression_type, - png_const_bytep profile, png_uint_32 profile_len) +png_write_iCCP(png_structrp png_ptr, png_const_charp name, + png_const_bytep profile) { png_uint_32 name_len; + png_uint_32 profile_len; png_byte new_name[81]; /* 1 byte for the compression byte */ compression_state comp; png_debug(1, "in png_write_iCCP"); - if (compression_type != PNG_COMPRESSION_TYPE_BASE) - png_error(png_ptr, "Unknown compression type for iCCP chunk"); - + /* These are all internal problems: the profile should have been checked + * before when it was stored. + */ if (profile == NULL) - png_error(png_ptr, "No profile for iCCP chunk"); + png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */ + + profile_len = png_get_uint_32(profile); if (profile_len < 132) png_error(png_ptr, "ICC profile too short"); @@ -1401,35 +1404,26 @@ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) #ifdef PNG_WRITE_cHRM_SUPPORTED /* Write the cHRM chunk */ void /* PRIVATE */ -png_write_cHRM_fixed(png_structrp png_ptr, png_fixed_point white_x, - png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, - png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, - png_fixed_point blue_y) +png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy) { png_byte buf[32]; png_debug(1, "in png_write_cHRM"); /* Each value is saved in 1/100,000ths */ -#ifdef PNG_CHECK_cHRM_SUPPORTED - if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, - green_x, green_y, blue_x, blue_y)) -#endif - { - png_save_uint_32(buf, (png_uint_32)white_x); - png_save_uint_32(buf + 4, (png_uint_32)white_y); + png_save_int_32(buf, xy->whitex); + png_save_int_32(buf + 4, xy->whitey); - png_save_uint_32(buf + 8, (png_uint_32)red_x); - png_save_uint_32(buf + 12, (png_uint_32)red_y); + png_save_int_32(buf + 8, xy->redx); + png_save_int_32(buf + 12, xy->redy); - png_save_uint_32(buf + 16, (png_uint_32)green_x); - png_save_uint_32(buf + 20, (png_uint_32)green_y); + png_save_int_32(buf + 16, xy->greenx); + png_save_int_32(buf + 20, xy->greeny); - png_save_uint_32(buf + 24, (png_uint_32)blue_x); - png_save_uint_32(buf + 28, (png_uint_32)blue_y); + png_save_int_32(buf + 24, xy->bluex); + png_save_int_32(buf + 28, xy->bluey); - png_write_complete_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); - } + png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); } #endif diff --git a/scripts/options.awk b/scripts/options.awk index a8dff044a..f5d7729f7 100755 --- a/scripts/options.awk +++ b/scripts/options.awk @@ -286,15 +286,15 @@ $1 == "option" && NF >= 2{ # Else fall through to the error handler } -# chunk NAME [requires OPT] [on|off|disabled] +# chunk NAME [requires OPT] [enables LIST] [on|off|disabled] # Expands to the 'option' settings appropriate to the reading and # writing of an ancilliary PNG chunk 'NAME': # # option READ_NAME requires READ_ANCILLARY_CHUNKS [READ_OPT] -# option READ_NAME enables NAME +# option READ_NAME enables NAME LIST # [option READ_NAME off] # option WRITE_NAME requires WRITE_ANCILLARY_CHUNKS [WRITE_OPT] -# option WRITE_NAME enables NAME +# option WRITE_NAME enables NAME LIST # [option WRITE_NAME off] pre != 0 && $1 == "chunk" && NF >= 2{ @@ -303,6 +303,7 @@ pre != 0 && $1 == "chunk" && NF >= 2{ onoff = "" reqread = "" reqwrite = "" + enables = "" i = 3 # indicates format error if (NF > 2) { # read the keywords/additional OPTS @@ -315,21 +316,25 @@ pre != 0 && $1 == "chunk" && NF >= 2{ else break # on/off conflict } + req = 0 } else if ($(i) == "requires") req = 1 - else if (req != 1) - break # bad line: handled below - else { + else if ($(i) == "enables") + req = 2 + else if (req == 1){ reqread = reqread " READ_" $(i) reqwrite = reqwrite " WRITE_" $(i) - } + } else if (req == 2) + enables = enables " " $(i) + else + break # bad line: handled below } } if (i > NF) { # Output new 'option' lines to the intermediate file (out) - print "option READ_" $2, "requires READ_ANCILLARY_CHUNKS" reqread, "enables", $2, onoff >out - print "option WRITE_" $2, "requires WRITE_ANCILLARY_CHUNKS" reqwrite, "enables", $2, onoff >out + print "option READ_" $2, "requires READ_ANCILLARY_CHUNKS" reqread, "enables", $2 enables , onoff >out + print "option WRITE_" $2, "requires WRITE_ANCILLARY_CHUNKS" reqwrite, "enables", $2 enables, onoff >out next } # Else hit the error handler below - bad line format! diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 97149771e..2687b8acd 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -32,8 +32,12 @@ file pnglibconf.h scripts/pnglibconf.dfa PNGLCONF_H # option [requires ...] [if ...] [enables ...] [disabled] # #define PNG__SUPPORTED if the requirements are met and # enable the other options listed -# chunk [requires ...] [disabled] -# Enable chunk processing for the given ancillary chunk +# chunk [requires ...] [enables ...] [disabled] +# Enable chunk processing for the given ancillary chunk; any +# 'requires something' expands to READ_something for read and +# WRITE_something for write, but the enables list members are +# used as given (e.g. enables GAMMA just expands to that on the +# correspond READ_name and WRITE_name lines.) # # Note that the 'on' and 'off' keywords, while valid on both option # and chunk, should not be used in this file because they force the @@ -461,9 +465,18 @@ option WRITE_TEXT requires WRITE_ANCILLARY_CHUNKS enables TEXT # API is implemented, they get recorded in pnglibconf.h, but # can't be changed by the application. -# Check the correctness of cHRM chunks +# Colorspace support (enabled as required); just the support for colorant +# information. Gamma support, likewise, is just support for the gamma +# information, READ_GAMMA is required for gamma transformations (so it +# is possible to read PNG gamma without enabling all the libpng transform +# code - do this for applications that do their own gamma processing) +# +# As of 1.6.0 COLORSPACE is only useful if the application processes the +# information; this is because the library does not do any colorspace +# processing, it just validates the data in the PNG file. -option CHECK_cHRM requires cHRM +option GAMMA disabled +option COLORSPACE enables GAMMA disabled # # Artificially align memory - the code typically aligns to 8 byte @@ -560,18 +573,18 @@ setting IDAT_READ_SIZE default PNG_ZBUF_SIZE # Ancillary chunks chunk bKGD -chunk cHRM -chunk gAMA +chunk cHRM enables COLORSPACE +chunk gAMA enables GAMMA chunk hIST -chunk iCCP +chunk iCCP enables COLORSPACE GAMMA chunk iTXt chunk oFFs chunk pCAL -chunk sCAL chunk pHYs chunk sBIT +chunk sCAL chunk sPLT -chunk sRGB +chunk sRGB enables COLORSPACE GAMMA chunk tEXt requires TEXT chunk tIME chunk tRNS diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index 019455ab9..902ef93b4 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -3,7 +3,7 @@ /* pnglibconf.h - library build configuration */ -/* Libpng 1.6.0beta20 - March 19, 2012 */ +/* Libpng 1.6.0beta20 - March 21, 2012 */ /* Copyright (c) 1998-2012 Glenn Randers-Pehrson */ @@ -44,9 +44,9 @@ /*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ #define PNG_bKGD_SUPPORTED #define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED -#define PNG_CHECK_cHRM_SUPPORTED #define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED #define PNG_cHRM_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED #define PNG_CONSOLE_IO_SUPPORTED #define PNG_CONVERT_tIME_SUPPORTED #define PNG_EASY_ACCESS_SUPPORTED @@ -58,6 +58,7 @@ #define PNG_FORMAT_AFIRST_SUPPORTED #define PNG_FORMAT_BGR_SUPPORTED #define PNG_gAMA_SUPPORTED +#define PNG_GAMMA_SUPPORTED #define PNG_HANDLE_AS_UNKNOWN_SUPPORTED #define PNG_hIST_SUPPORTED #define PNG_iCCP_SUPPORTED