[devel] Fixes to rgb_to_gray and cHRM XYZ APIs

This commit is contained in:
John Bowler 2011-08-25 16:19:44 -05:00 committed by Glenn Randers-Pehrson
parent 0c03fc6f75
commit 736f40f459
13 changed files with 2055 additions and 514 deletions

View File

@ -1,5 +1,5 @@
Libpng 1.5.5beta06 - August 17, 2011
Libpng 1.5.5beta06 - August 25, 2011
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.
@ -62,9 +62,22 @@ Version 1.5.5beta05 [August 17, 2011]
Added new types and internal functions for CIE RGB end point handling to
pngpriv.h (functions yet to be implemented).
Version 1.5.5beta06 [August 17, 2011]
Version 1.5.5beta06 [August 25, 2011]
Ensure the CMAKE_LIBRARY_OUTPUT_DIRECTORY is set in CMakeLists.txt
(Clifford Yap)
Fixes to rgb_to_gray and cHRM XYZ APIs (John Bowler):
The rgb_to_gray code had errors when combined with gamma correction.
Some pixels were treated as true grey when they weren't and such pixels
and true grey ones were not gamma corrected (the original value of the
red component was used instead). APIs to get and set cHRM using color
space end points have been added and the rgb_to_gray code that defaults
based on cHRM (introduced in 1.5.4) has been corrected. A considerable
number of tests has been added to pngvalid for the rgb_to_gray transform.
Arithmetic errors in rgb_to_gray whereby the calculated gray value was
truncated to the bit depth rather than rounded have been fixed except in
the 8-bit non-gamma-corrected case (where consistency seems more important
than correctness.) The code still has considerable inaccuracies in the
8-bit case because 8-bit linear arithmetic is used.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net:
(subscription required; visit

15
CHANGES
View File

@ -3546,9 +3546,22 @@ Version 1.5.5beta05 [August 17, 2011]
Added new types and internal functions for CIE RGB end point handling to
pngpriv.h (functions yet to be implemented).
Version 1.5.5beta06 [August 17, 2011]
Version 1.5.5beta06 [August 25, 2011]
Ensure the CMAKE_LIBRARY_OUTPUT_DIRECTORY is set in CMakeLists.txt
(Clifford Yap)
Fixes to rgb_to_gray and cHRM XYZ APIs (John Bowler):
The rgb_to_gray code had errors when combined with gamma correction.
Some pixels were treated as true grey when they weren't and such pixels
and true grey ones were not gamma corrected (the original value of the
red component was used instead). APIs to get and set cHRM using color
space end points have been added and the rgb_to_gray code that defaults
based on cHRM (introduced in 1.5.4) has been corrected. A considerable
number of tests has been added to pngvalid for the rgb_to_gray transform.
Arithmetic errors in rgb_to_gray whereby the calculated gray value was
truncated to the bit depth rather than rounded have been fixed except in
the 8-bit non-gamma-corrected case (where consistency seems more important
than correctness.) The code still has considerable inaccuracies in the
8-bit case because 8-bit linear arithmetic is used.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit

View File

@ -1,6 +1,6 @@
libpng-manual.txt - A description on how to use and modify libpng
libpng version 1.5.5beta06 - August 17, 2011
libpng version 1.5.5beta06 - August 25, 2011
Updated and distributed by Glenn Randers-Pehrson
<glennrp at users.sourceforge.net>
Copyright (c) 1998-2011 Glenn Randers-Pehrson
@ -11,7 +11,7 @@ libpng-manual.txt - A description on how to use and modify libpng
Based on:
libpng versions 0.97, January 1998, through 1.5.5beta06 - August 17, 2011
libpng versions 0.97, January 1998, through 1.5.5beta06 - August 25, 2011
Updated and distributed by Glenn Randers-Pehrson
Copyright (c) 1998-2011 Glenn Randers-Pehrson
@ -4509,7 +4509,7 @@ Other rules can be inferred by inspecting the libpng source.
XIV. Y2K Compliance in libpng
August 17, 2011
August 25, 2011
Since the PNG Development group is an ad-hoc body, we can't make
an official declaration.

View File

@ -1,4 +1,4 @@
.TH LIBPNG 3 "August 17, 2011"
.TH LIBPNG 3 "August 25, 2011"
.SH NAME
libpng \- Portable Network Graphics (PNG) Reference Library 1.5.5beta06
.SH SYNOPSIS
@ -128,10 +128,22 @@ libpng \- Portable Network Graphics (PNG) Reference Library 1.5.5beta06
\fI\fB
\fBpng_uint_32 png_get_chunk_cache_max (png_const_structp \fIpng_ptr\fP\fB);\fP
\fBpng_uint_32 png_get_cHRM_XYZ (png_structp \fIpng_ptr,
\fBpng_const_infop \fP\fIinfo_ptr\fP\fB, double \fP\fI*red_X\fP\fB, double \fP\fI*red_Y\fP\fB, double \fI*red_Z,
\fBdouble \fP\fI*green_X\fP\fB, double \fP\fI*green_Y\fP\fB, double \fP\fI*green_Z\fP\fB, double \fI*blue_X,
\fBdouble \fP\fI*blue_Y\fP\fB, double \fI*blue_Z\fP\fB);\fP
\fI\fB
\fBpng_uint_32 png_get_cHRM_XYZ_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_fixed_point \fP\fI*int_red_X\fP\fB, png_fixed_point \fP\fI*int_red_Y\fP\fB, png_fixed_point \fP\fI*int_red_Z\fP\fB, png_fixed_point \fP\fI*int_green_X\fP\fB, png_fixed_point \fP\fI*int_green_Y\fP\fB, png_fixed_point \fP\fI*int_green_Z\fP\fB, png_fixed_point \fP\fI*int_blue_X\fP\fB, png_fixed_point \fP\fI*int_blue_Y\fP\fB, png_fixed_point \fI*int_blue_Z\fP\fB);\fP
\fI\fB
\fBpng_uint_32 png_get_chunk_cache_max (png_const_structp \fIpng_ptr\fP\fB);\fP
\fI\fB
\fBpng_alloc_size_t png_get_chunk_malloc_max (png_const_structp \fIpng_ptr\fP\fB);\fP
@ -548,6 +560,16 @@ libpng \- Portable Network Graphics (PNG) Reference Library 1.5.5beta06
\fI\fB
\fBvoid png_set_cHRM_XYZ (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fIred_X\fP\fB, double \fP\fIred_Y\fP\fB, double \fP\fIred_Z\fP\fB, double \fP\fIgreen_X\fP\fB, double \fIgreen_Y,
\fBdouble \fP\fIgreen_Z\fP\fB, double \fP\fIblue_X\fP\fB, double \fP\fIblue_Y\fP\fB, double \fIblue_Z\fP\fB);\fP
\fI\fB
\fBvoid png_set_cHRM_XYZ_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_fixed_point \fP\fIint_red_X\fP\fB, png_fixed_point \fP\fIint_red_Y\fP\fB, png_fixed_point \fP\fIint_red_Z\fP\fB, png_fixed_point \fP\fIint_green_X\fP\fB, png_fixed_point \fP\fIint_green_Y\fP\fB, png_fixed_point \fP\fIint_green_Z\fP\fB, png_fixed_point \fP\fIint_blue_X\fP\fB, png_fixed_point \fP\fIint_blue_Y\fP\fB, png_fixed_point \fIint_blue_Z\fP\fB);\fP
\fI\fB
\fBvoid png_set_chunk_cache_max (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIuser_chunk_cache_max\fP\fB);\fP
\fI\fB
@ -955,7 +977,7 @@ Following is a copy of the libpng-manual.txt file that accompanies libpng.
.SH LIBPNG.TXT
libpng-manual.txt - A description on how to use and modify libpng
libpng version 1.5.5beta06 - August 17, 2011
libpng version 1.5.5beta06 - August 25, 2011
Updated and distributed by Glenn Randers-Pehrson
<glennrp at users.sourceforge.net>
Copyright (c) 1998-2011 Glenn Randers-Pehrson
@ -966,7 +988,7 @@ libpng-manual.txt - A description on how to use and modify libpng
Based on:
libpng versions 0.97, January 1998, through 1.5.5beta06 - August 17, 2011
libpng versions 0.97, January 1998, through 1.5.5beta06 - August 25, 2011
Updated and distributed by Glenn Randers-Pehrson
Copyright (c) 1998-2011 Glenn Randers-Pehrson
@ -5465,7 +5487,7 @@ Other rules can be inferred by inspecting the libpng source.
.SH XIV. Y2K Compliance in libpng
August 17, 2011
August 25, 2011
Since the PNG Development group is an ad-hoc body, we can't make
an official declaration.
@ -5724,7 +5746,7 @@ possible without all of you.
Thanks to Frank J. T. Wojcik for helping with the documentation.
Libpng version 1.5.5beta06 - August 17, 2011:
Libpng version 1.5.5beta06 - August 25, 2011:
Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc.
Currently maintained by Glenn Randers-Pehrson (glennrp at users.sourceforge.net).
@ -5747,7 +5769,7 @@ this sentence.
This code is released under the libpng license.
libpng versions 1.2.6, August 15, 2004, through 1.5.5beta06, August 17, 2011, are
libpng versions 1.2.6, August 15, 2004, through 1.5.5beta06, August 25, 2011, are
Copyright (c) 2004,2006-2007 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
@ -5846,7 +5868,7 @@ certification mark of the Open Source Initiative.
Glenn Randers-Pehrson
glennrp at users.sourceforge.net
August 17, 2011
August 25, 2011
.\" end of man page

339
png.c
View File

@ -617,13 +617,13 @@ png_get_copyright(png_const_structp png_ptr)
#else
# ifdef __STDC__
return PNG_STRING_NEWLINE \
"libpng version 1.5.5beta06 - August 17, 2011" PNG_STRING_NEWLINE \
"libpng version 1.5.5beta06 - August 25, 2011" PNG_STRING_NEWLINE \
"Copyright (c) 1998-2011 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
"Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
"Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
PNG_STRING_NEWLINE;
# else
return "libpng version 1.5.5beta06 - August 17, 2011\
return "libpng version 1.5.5beta06 - August 25, 2011\
Copyright (c) 1998-2011 Glenn Randers-Pehrson\
Copyright (c) 1996-1997 Andreas Dilger\
Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
@ -713,18 +713,9 @@ png_access_version_number(void)
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
# ifdef PNG_SIZE_T
/* Added at libpng version 1.2.6 */
PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size));
png_size_t PNGAPI
png_convert_size(size_t size)
{
if (size > (png_size_t)-1)
PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */
return ((png_size_t)size);
}
# endif /* PNG_SIZE_T */
/* png_convert_size: a PNGAPI but no longer in png.h, so deleted
* at libpng 1.5.5!
*/
/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */
# ifdef PNG_CHECK_cHRM_SUPPORTED
@ -798,6 +789,326 @@ png_check_cHRM_fixed(png_structp png_ptr,
}
# endif /* PNG_CHECK_cHRM_SUPPORTED */
#ifdef PNG_cHRM_SUPPORTED
/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for
* cHRM, as opposed to using chromaticities. These internal APIs return
* non-zero on a parameter error. The X, Y and Z values are required to be
* positive and less than 1.0.
*/
int png_xy_from_XYZ(png_xy *xy, png_XYZ XYZ)
{
png_int_32 d, dwhite, whiteX, whiteY;
d = XYZ.redX + XYZ.redY + XYZ.redZ;
if (!png_muldiv(&xy->redx, XYZ.redX, PNG_FP_1, d)) return 1;
if (!png_muldiv(&xy->redy, XYZ.redY, PNG_FP_1, d)) return 1;
dwhite = d;
whiteX = XYZ.redX;
whiteY = XYZ.redY;
d = XYZ.greenX + XYZ.greenY + XYZ.greenZ;
if (!png_muldiv(&xy->greenx, XYZ.greenX, PNG_FP_1, d)) return 1;
if (!png_muldiv(&xy->greeny, XYZ.greenY, PNG_FP_1, d)) return 1;
dwhite += d;
whiteX += XYZ.greenX;
whiteY += XYZ.greenY;
d = XYZ.blueX + XYZ.blueY + XYZ.blueZ;
if (!png_muldiv(&xy->bluex, XYZ.blueX, PNG_FP_1, d)) return 1;
if (!png_muldiv(&xy->bluey, XYZ.blueY, PNG_FP_1, d)) return 1;
dwhite += d;
whiteX += XYZ.blueX;
whiteY += XYZ.blueY;
/* The reference white is simply the same of the end-point (X,Y,Z) vectors,
* thus:
*/
if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1;
if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1;
return 0;
}
int png_XYZ_from_xy(png_XYZ *XYZ, png_xy xy)
{
png_fixed_point red_inverse, green_inverse, blue_scale;
png_fixed_point left, right, denominator;
/* Check xy and, implicitly, z. Note that wide gamut color spaces typically
* have end points with 0 tristimulus values (these are impossible end
* points, but they are used to cover the possible colors.)
*/
if (xy.redx < 0 || xy.redx > PNG_FP_1) return 1;
if (xy.redy < 0 || xy.redy > PNG_FP_1-xy.redx) return 1;
if (xy.greenx < 0 || xy.greenx > PNG_FP_1) return 1;
if (xy.greeny < 0 || xy.greeny > PNG_FP_1-xy.greenx) return 1;
if (xy.bluex < 0 || xy.bluex > PNG_FP_1) return 1;
if (xy.bluey < 0 || xy.bluey > PNG_FP_1-xy.bluex) return 1;
if (xy.whitex < 0 || xy.whitex > PNG_FP_1) return 1;
if (xy.whitey < 0 || xy.whitey > PNG_FP_1-xy.whitex) return 1;
/* The reverse calculation is more difficult because the original tristimulus
* value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8
* derived values were recorded in the cHRM chunk;
* (red,green,blue,white)x(x,y). This loses one degree of freedom and
* therefore an arbitrary ninth value has to be introduced to undo the
* original transformations.
*
* Think of the original end-points as points in (X,Y,Z) space. The
* chromaticity values (c) have the property:
*
* C
* c = ---------
* X + Y + Z
*
* For each c (x,y,z) from the corresponding original C (X,Y,Z). Thus the
* three chromaticity values (x,y,z) for each end-point obey the
* relationship:
*
* x + y + z = 1
*
* This describes the plane in (X,Y,Z) space that intersects each axis at the
* value 1.0; call this the chromaticity plane. Thus the chromaticity
* calculation has scaled each end-point so that it is on the x+y+z=1 plane
* and chromaticity is the intersection of the vector from the origin to the
* (X,Y,Z) value with the chromaticity plane.
*
* To fully invert the chromaticity calculation we would need the three
* end-point scale factors, (red-scale, green-scale, blue-scale), but these
* were not recorded. Instead we calculated the reference white (X,Y,Z) and
* recorded the chromaticity of this. The reference white (X,Y,Z) would have
* given all three of the scale factors since:
*
* color-C = color-c * color-scale
* white-C = red-C + green-C + blue-C
* = red-c*red-scale + green-c*green-scale + blue-c*blue-scale
*
* But cHRM records only white-x and white-y, so we have lost the white scale
* factor:
*
* white-C = white-c*white-scale
*
* To handle this the inverse transformation makes an arbitrary assumption
* about white-scale:
*
* Assume: white-Y = 1.0
* Hence: white-scale = 1/white-y
* Or: red-Y + green-Y + blue-Y = 1.0
*
* Notice the last statement of the assumption gives an equation in three of
* the nine values we want to calculate. 8 more equations come from the
* above routine as summarised at the top above (the chromaticity
* calculation):
*
* Given: color-x = color-X / (color-X + color-Y + color-Z)
* Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0
*
* This is 9 simultaneous equations in the 9 variables "color-C" and can be
* solved by Cramer's rule. Cramer's rule requires calculating 10 9x9 matrix
* determinants, however this is not as bad as it seems because only 28 of
* the total of 90 terms in the various matrices are non-zero. Nevertheless
* Cramer's rule is notoriously numerically unstable because the determinant
* calculation involves the difference of large, but similar, numbers. It is
* difficult to be sure that the calculation is stable for real world values
* and it is certain that it becomes unstable where the end points are close
* together.
*
* So this code uses the perhaps slighly less optimal but more understandable
* and totally obvious approach of calculating color-scale.
*
* This algorithm depends on the precision in white-scale and that is
* (1/white-y), so we can immediately see that as white-y approaches 0 the
* accuracy inherent in the cHRM chunk drops off substantially.
*
* libpng arithmetic: a simple invertion of the above equations
* ------------------------------------------------------------
*
* white_scale = 1/white-y
* white-X = white-x * white-scale
* white-Y = 1.0
* white-Z = (1 - white-x - white-y) * white_scale
*
* white-C = red-C + green-C + blue-C
* = red-c*red-scale + green-c*green-scale + blue-c*blue-scale
*
* This gives us three equations in (red-scale,green-scale,blue-scale) where
* all the coefficients are now known:
*
* red-x*red-scale + green-x*green-scale + blue-x*blue-scale
* = white-x/white-y
* red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1
* red-z*red-scale + green-z*green-scale + blue-z*blue-scale
* = (1 - white-x - white-y)/white-y
*
* In the last equation color-z is (1 - color-x - color-y) so we can add all
* three equations together to get an alternative third:
*
* red-scale + green-scale + blue-scale = 1/white-y = white-scale
*
* So now we have a Cramer's rule solution where the determinants are just
* 3x3 - far more tractible. Unfortunately 3x3 determinants still involve
* multiplication of three coefficients so we can't guarantee to avoid
* overflow in the libpng fixed point representation. Using Cramer's rule in
* floating point is probably a good choice here, but it's not an option for
* fixed point. Instead proceed to simplify the first two equations by
* eliminating what is likely to be the largest value, blue-scale:
*
* blue-scale = white-scale - red-scale - green-scale
*
* Hence:
*
* (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale =
* (white-x - blue-x)*white-scale
*
* (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale =
* 1 - blue-y*white-scale
*
* And now we can trivially solve for (red-scale,green-scale):
*
* green-scale =
* (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale
* -----------------------------------------------------------
* green-x - blue-x
*
* red-scale =
* 1 - blue-y*white-scale - (green-y - blue-y) * green-scale
* ---------------------------------------------------------
* red-y - blue-y
*
* Hence:
*
* red-scale =
* ( (green-x - blue-x) * (white-y - blue-y) -
* (green-y - blue-y) * (white-x - blue-x) ) / white-y
* -------------------------------------------------------------------------
* (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)
*
* green-scale =
* ( (red-y - blue-y) * (white-x - blue-x) -
* (red-x - blue-x) * (white-y - blue-y) ) / white-y
* -------------------------------------------------------------------------
* (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x)
*
* Accuracy:
* The input values have 5 decimal digits of accuracy. The values are all in
* the range 0 < value < 1, so simple products are in the same range but may
* need up to 10 decimal digits to preserve the original precision and avoid
* underflow. Because we are using a 32-bit signed representation we cannot
* match this; the best is a little over 9 decimal digits, less than 10.
*
* The approach used here is to preserve the maximum precision within the
* signed representation. Because the red-scale calculation above uses the
* difference between two products of values that must be in the range -1..+1
* it is sufficient to divide the product by 7; ceil(100,000/32767*2). The
* factor is irrelevant in the calculation because it is applied to both
* numerator and denominator.
*
* Note that the values of the differences of the products of the
* chromaticities in the above equations tend to be small, for example for
* the sRGB chromaticities they are:
*
* red numerator: -0.04751
* green numerator: -0.08788
* denominator: -0.2241 (without white-y multiplication)
*
* The resultant Y coefficients from the chromaticities of some widely used
* color space definitions are (to 15 decimal places):
*
* sRGB
* 0.212639005871510 0.715168678767756 0.072192315360734
* Kodak ProPhoto
* 0.288071128229293 0.711843217810102 0.000085653960605
* Adobe RGB
* 0.297344975250536 0.627363566255466 0.075291458493998
* Adobe Wide Gamut RGB
* 0.258728243040113 0.724682314948566 0.016589442011321
*/
/* By the argument, above overflow should be impossible here. The return
* value of 2 indicates an internal error to the caller.
*/
if (!png_muldiv(&left, xy.greenx-xy.bluex, xy.redy - xy.bluey, 7)) return 2;
if (!png_muldiv(&right, xy.greeny-xy.bluey, xy.redx - xy.bluex, 7)) return 2;
denominator = left - right;
/* Now find the red numerator. */
if (!png_muldiv(&left, xy.greenx-xy.bluex, xy.whitey-xy.bluey, 7)) return 2;
if (!png_muldiv(&right, xy.greeny-xy.bluey, xy.whitex-xy.bluex, 7)) return 2;
/* Overflow is possible here and it indicates an extreme set of PNG cHRM
* chunk values. This calculation actually returns the reciprocal of the
* scale value because this allows us to delay the multiplication of white-y
* into the denominator, which tends to produce a small number.
*/
if (!png_muldiv(&red_inverse, xy.whitey, denominator, left-right) ||
red_inverse <= xy.whitey /* r+g+b scales = white scale */)
return 1;
/* Similarly for green_inverse: */
if (!png_muldiv(&left, xy.redy-xy.bluey, xy.whitex-xy.bluex, 7)) return 2;
if (!png_muldiv(&right, xy.redx-xy.bluex, xy.whitey-xy.bluey, 7)) return 2;
if (!png_muldiv(&green_inverse, xy.whitey, denominator, left-right) ||
green_inverse <= xy.whitey)
return 1;
/* And the blue scale, the checks above guarantee this can't overflow but it
* can still produce 0 for extreme cHRM values.
*/
blue_scale = png_reciprocal(xy.whitey) - png_reciprocal(red_inverse) -
png_reciprocal(green_inverse);
if (blue_scale <= 0) return 1;
/* And fill in the png_XYZ: */
if (!png_muldiv(&XYZ->redX, xy.redx, PNG_FP_1, red_inverse)) return 1;
if (!png_muldiv(&XYZ->redY, xy.redy, PNG_FP_1, red_inverse)) return 1;
if (!png_muldiv(&XYZ->redZ, PNG_FP_1 - xy.redx - xy.redy, PNG_FP_1,
red_inverse))
return 1;
if (!png_muldiv(&XYZ->greenX, xy.greenx, PNG_FP_1, green_inverse)) return 1;
if (!png_muldiv(&XYZ->greenY, xy.greeny, PNG_FP_1, green_inverse)) return 1;
if (!png_muldiv(&XYZ->greenZ, PNG_FP_1 - xy.greenx - xy.greeny, PNG_FP_1,
green_inverse))
return 1;
if (!png_muldiv(&XYZ->blueX, xy.bluex, blue_scale, PNG_FP_1)) return 1;
if (!png_muldiv(&XYZ->blueY, xy.bluey, blue_scale, PNG_FP_1)) return 1;
if (!png_muldiv(&XYZ->blueZ, PNG_FP_1 - xy.bluex - xy.bluey, blue_scale,
PNG_FP_1))
return 1;
return 0; /*success*/
}
int png_XYZ_from_xy_checked(png_structp png_ptr, png_XYZ *XYZ, png_xy xy)
{
switch (png_XYZ_from_xy(XYZ, xy))
{
case 0: /* success */
return 1;
case 1:
/* The chunk may be technically valid, but we got png_fixed_point
* overflow while trying to get XYZ values out of it. This is
* entirely benign - the cHRM chunk is pretty extreme.
*/
png_chunk_benign_error(png_ptr,
"extreme cHRM chunk cannot be converted to tristimulus values");
break;
default:
/* libpng is broken; this should be a warning but if it happens we
* want error reports so for the moment it is an error.
*/
png_error(png_ptr, "internal error in png_XYZ_from_xy");
break;
}
/* ERROR RETURN */
return 0;
}
#endif
void /* PRIVATE */
png_check_IHDR(png_structp png_ptr,
png_uint_32 width, png_uint_32 height, int bit_depth,

33
png.h
View File

@ -1,7 +1,7 @@
/* png.h - header file for PNG reference library
*
* libpng version 1.5.5beta06 - August 17, 2011
* libpng version 1.5.5beta06 - August 25, 2011
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@ -11,7 +11,7 @@
* Authors and maintainers:
* libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
* libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
* libpng versions 0.97, January 1998, through 1.5.5beta06 - August 17, 2011: Glenn
* libpng versions 0.97, January 1998, through 1.5.5beta06 - August 25, 2011: Glenn
* See also "Contributing Authors", below.
*
* Note about libpng version numbers:
@ -189,7 +189,7 @@
*
* This code is released under the libpng license.
*
* libpng versions 1.2.6, August 15, 2004, through 1.5.5beta06, August 17, 2011, are
* libpng versions 1.2.6, August 15, 2004, through 1.5.5beta06, August 25, 2011, are
* Copyright (c) 2004, 2006-2011 Glenn Randers-Pehrson, and are
* distributed according to the same disclaimer and license as libpng-1.2.5
* with the following individual added to the list of Contributing Authors:
@ -301,7 +301,7 @@
* Y2K compliance in libpng:
* =========================
*
* August 17, 2011
* August 25, 2011
*
* Since the PNG Development group is an ad-hoc body, we can't make
* an official declaration.
@ -364,7 +364,7 @@
/* Version information for png.h - this should match the version in png.c */
#define PNG_LIBPNG_VER_STRING "1.5.5beta06"
#define PNG_HEADER_VERSION_STRING \
" libpng version 1.5.5beta06 - August 17, 2011\n"
" libpng version 1.5.5beta06 - August 25, 2011\n"
#define PNG_LIBPNG_VER_SONUM 15
#define PNG_LIBPNG_VER_DLLNUM 15
@ -2048,6 +2048,10 @@ PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structp png_ptr,
png_const_infop info_ptr, double *white_x, double *white_y, double *red_x,
double *red_y, double *green_x, double *green_y, double *blue_x,
double *blue_y));
PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_structp png_ptr,
png_const_infop info_ptr, double *red_X, double *red_Y, double *red_Z,
double *green_X, double *green_Y, double *green_Z, double *blue_X,
double *blue_Y, double *blue_Z));
#ifdef PNG_FIXED_POINT_SUPPORTED /* Otherwise not implemented */
PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
(png_const_structp png_ptr,
@ -2057,6 +2061,13 @@ PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
png_fixed_point *int_green_y, png_fixed_point *int_blue_x,
png_fixed_point *int_blue_y));
#endif
PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed,
(png_structp png_ptr, png_const_infop info_ptr,
png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
png_fixed_point *int_blue_Z));
#endif
#ifdef PNG_cHRM_SUPPORTED
@ -2064,12 +2075,22 @@ PNG_FP_EXPORT(135, void, png_set_cHRM,
(png_structp png_ptr, png_infop info_ptr,
double white_x, double white_y, double red_x, double red_y, double green_x,
double green_y, double blue_x, double blue_y));
PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_structp png_ptr,
png_infop info_ptr, double red_X, double red_Y, double red_Z,
double green_X, double green_Y, double green_Z, double blue_X,
double blue_Y, double blue_Z));
PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_structp png_ptr,
png_infop info_ptr, png_fixed_point int_white_x,
png_fixed_point int_white_y, png_fixed_point int_red_x,
png_fixed_point int_red_y, png_fixed_point int_green_x,
png_fixed_point int_green_y, png_fixed_point int_blue_x,
png_fixed_point int_blue_y));
PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_structp png_ptr,
png_infop info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y,
png_fixed_point int_red_Z, png_fixed_point int_green_X,
png_fixed_point int_green_Y, png_fixed_point int_green_Z,
png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
png_fixed_point int_blue_Z));
#endif
#ifdef PNG_gAMA_SUPPORTED
@ -2577,7 +2598,7 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
* scripts/symbols.def as well.
*/
#ifdef PNG_EXPORT_LAST_ORDINAL
PNG_EXPORT_LAST_ORDINAL(229);
PNG_EXPORT_LAST_ORDINAL(233);
#endif
#ifdef __cplusplus

View File

@ -459,6 +459,65 @@ png_get_bKGD(png_const_structp png_ptr, png_infop info_ptr,
#endif
#ifdef PNG_cHRM_SUPPORTED
/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the
* same time to correct the rgb grayscale coefficient defaults obtained from the
* cHRM chunk in 1.5.4
*/
png_uint_32 PNGFAPI
png_get_cHRM_XYZ_fixed(png_structp png_ptr, png_const_infop info_ptr,
png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
png_fixed_point *int_blue_Z)
{
if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
{
png_xy xy;
png_XYZ XYZ;
png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
xy.whitex = info_ptr->x_white;
xy.whitey = info_ptr->y_white;
xy.redx = info_ptr->x_red;
xy.redy = info_ptr->y_red;
xy.greenx = info_ptr->x_green;
xy.greeny = info_ptr->y_green;
xy.bluex = info_ptr->x_blue;
xy.bluey = info_ptr->y_blue;
/* The *_checked function handles error reporting, so just return 0 if
* there is a failure here.
*/
if (png_XYZ_from_xy_checked(png_ptr, &XYZ, xy))
{
if (int_red_X != NULL)
*int_red_X = XYZ.redX;
if (int_red_Y != NULL)
*int_red_Y = XYZ.redY;
if (int_red_Z != NULL)
*int_red_Z = XYZ.redZ;
if (int_green_X != NULL)
*int_green_X = XYZ.greenX;
if (int_green_Y != NULL)
*int_green_Y = XYZ.greenY;
if (int_green_Z != NULL)
*int_green_Z = XYZ.greenZ;
if (int_blue_X != NULL)
*int_blue_X = XYZ.blueX;
if (int_blue_Y != NULL)
*int_blue_Y = XYZ.blueY;
if (int_blue_Z != NULL)
*int_blue_Z = XYZ.blueZ;
return (PNG_INFO_cHRM);
}
}
return (0);
}
# ifdef PNG_FLOATING_POINT_SUPPORTED
png_uint_32 PNGAPI
png_get_cHRM(png_const_structp png_ptr, png_const_infop info_ptr,
@ -490,6 +549,42 @@ png_get_cHRM(png_const_structp png_ptr, png_const_infop info_ptr,
return (0);
}
png_uint_32 PNGAPI
png_get_cHRM_XYZ(png_structp png_ptr, png_const_infop info_ptr,
double *red_X, double *red_Y, double *red_Z, double *green_X,
double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
double *blue_Z)
{
png_XYZ XYZ;
if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr,
&XYZ.redX, &XYZ.redY, &XYZ.redZ, &XYZ.greenX, &XYZ.greenY, &XYZ.greenZ,
&XYZ.blueX, &XYZ.blueY, &XYZ.blueZ) & PNG_INFO_cHRM)
{
if (red_X != NULL)
*red_X = png_float(png_ptr, XYZ.redX, "cHRM red X");
if (red_Y != NULL)
*red_Y = png_float(png_ptr, XYZ.redY, "cHRM red Y");
if (red_Z != NULL)
*red_Z = png_float(png_ptr, XYZ.redZ, "cHRM red Z");
if (green_X != NULL)
*green_X = png_float(png_ptr, XYZ.greenX, "cHRM green X");
if (green_Y != NULL)
*green_Y = png_float(png_ptr, XYZ.greenY, "cHRM green Y");
if (green_Z != NULL)
*green_Z = png_float(png_ptr, XYZ.greenZ, "cHRM green Z");
if (blue_X != NULL)
*blue_X = png_float(png_ptr, XYZ.blueX, "cHRM blue X");
if (blue_Y != NULL)
*blue_Y = png_float(png_ptr, XYZ.blueY, "cHRM blue Y");
if (blue_Z != NULL)
*blue_Z = png_float(png_ptr, XYZ.blueZ, "cHRM blue Z");
return (PNG_INFO_cHRM);
}
return (0);
}
# endif
# ifdef PNG_FIXED_POINT_SUPPORTED

View File

@ -1166,7 +1166,12 @@ typedef struct png_XYZ
png_fixed_point blueX, blueY, blueZ;
} png_XYZ;
/* The conversion APIs return 0 on success, non-zero on a parameter error. */
/* The conversion APIs return 0 on success, non-zero on a parameter error. They
* allow conversion between the above representations of a color encoding. When
* converting from XYZ end points to chromaticities the absolute magnitude of
* the end points is lost, when converting back the sum of the Y values of the
* three end points will be 1.0
*/
PNG_EXTERN int png_xy_from_XYZ PNGARG((png_xy *xy, png_XYZ XYZ));
PNG_EXTERN int png_XYZ_from_xy PNGARG((png_XYZ *XYZ, png_xy xy));
PNG_EXTERN int png_XYZ_from_xy_checked PNGARG((png_structp png_ptr,

View File

@ -968,13 +968,17 @@ png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action,
{
png_uint_16 red_int, green_int;
/* NOTE: this calculation does not round, but this behavior is retained
* for consistency, the inaccuracy is very small. The code here always
* overwrites the coefficients, regardless of whether they have been
* defaulted or set already.
*/
red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L);
green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L);
png_ptr->rgb_to_gray_red_coeff = red_int;
png_ptr->rgb_to_gray_green_coeff = green_int;
png_ptr->rgb_to_gray_blue_coeff =
(png_uint_16)(32768 - red_int - green_int);
png_ptr->rgb_to_gray_coefficients_set = 1;
}
else
@ -983,17 +987,18 @@ png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action,
png_warning(png_ptr,
"ignoring out of range rgb_to_gray coefficients");
/* Use the defaults, from the cHRM chunk if set, else the built in Rec
* 709 values (which correspond to sRGB, so we don't have to worry
* about the sRGB chunk!)
/* Use the defaults, from the cHRM chunk if set, else the historical
* values which are close to the sRGB/HDTV/ITU-Rec 709 values. See
* png_do_rgb_to_gray for more discussion of the values. In this case
* the coefficients are not marked as 'set' and are not overwritten if
* something has already provided a default.
*/
if (png_ptr->rgb_to_gray_red_coeff == 0 &&
png_ptr->rgb_to_gray_green_coeff == 0 &&
png_ptr->rgb_to_gray_blue_coeff == 0)
png_ptr->rgb_to_gray_green_coeff == 0)
{
png_ptr->rgb_to_gray_red_coeff = 6968; /* .212671 * 32768 + .5 */
png_ptr->rgb_to_gray_green_coeff = 23434; /* .715160 * 32768 + .5 */
png_ptr->rgb_to_gray_blue_coeff = 2366;
png_ptr->rgb_to_gray_red_coeff = 6968;
png_ptr->rgb_to_gray_green_coeff = 23434;
/* png_ptr->rgb_to_gray_blue_coeff = 2366; */
}
}
}
@ -1401,7 +1406,7 @@ png_init_read_transformations(png_structp png_ptr)
if (png_ptr->transformations & PNG_BACKGROUND_EXPAND)
{
/* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if
* the file was greyscale the background value is gray.
* the file was grayscale the background value is gray.
*/
if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR))
png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
@ -3089,26 +3094,38 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
*
* Y = (6966 * R + 23436 * G + 2366 * B)/32768
*
* We use the approximation
* Historically, however, libpng uses numbers derived from the ITU-R Rec 709
* end point chromaticities and the D65 white point. Depending on the
* precision used for the D65 white point this produces a variety of different
* numbers, however if the four decimal place value used in ITU-R Rec 709 is
* used (0.3127,0.3290) the Y calculation would be:
*
* Y = (6968 * R + 23435 * G + 2366 * B)/32768
*
* While this is correct the rounding results in an overflow for white, because
* the sum of the rounded coefficients is 32769, not 32768. Consequently
* libpng uses, instead, the closest non-overflowing approximation:
*
* Y = (6968 * R + 23434 * G + 2366 * B)/32768
*
* Starting with libpng-1.5.4, if the image being converted has the
* sRGB chunk, then the sRGB numbers are used by default:
* Starting with libpng-1.5.5, if the image being converted has a cHRM chunk
* (including an sRGB chunk) then the chromaticities are used to calculate the
* coefficients. See the chunk handling in pngrutil.c for more information.
*
* Y = 0.33000*R + 0.60000*G + 0.06000*B
* In all cases the calculation is to be done in a linear colorspace. If no
* gamma information is available to correct the encoding of the original RGB
* values this results in an implicit assumption that the original PNG RGB
* values were linear.
*
* The calculation is to be done in a linear colorspace.
*
* Other integer coefficents can be used via png_set_rgb_to_gray().
* Other integer coefficents can be used via png_set_rgb_to_gray(). Because
* the API takes just red and green coefficients the blue coefficient is
* calculated to make the sum 32768. This will result in different rounding
* to that used above.
*/
int /* PRIVATE */
png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
{
png_uint_32 i;
png_uint_32 row_width = row_info->width;
int rgb_error = 0;
png_debug(1, "in png_do_rgb_to_gray");
@ -3116,234 +3133,179 @@ png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) &&
(row_info->color_type & PNG_COLOR_MASK_COLOR))
{
png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff;
PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
PNG_CONST png_uint_32 bc = 32768 - rc - gc;
PNG_CONST png_uint_32 row_width = row_info->width;
PNG_CONST int have_alpha =
(row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0;
if (row_info->color_type == PNG_COLOR_TYPE_RGB)
if (row_info->bit_depth == 8)
{
if (row_info->bit_depth == 8)
{
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
/* Notice that gamma to/from 1 are not necessarily inverses (if
* there is an overall gamma correction). Prior to 1.5.5 this code
* checked the linearized values for equality; this doesn't match
* the documentation, the original values must be checked.
*/
if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
{
png_bytep sp = row;
png_bytep dp = row;
png_uint_32 i;
for (i = 0; i < row_width; i++)
{
png_bytep sp = row;
png_bytep dp = row;
png_byte red = *(sp++);
png_byte green = *(sp++);
png_byte blue = *(sp++);
for (i = 0; i < row_width; i++)
if (red != green || red != blue)
{
png_byte red = png_ptr->gamma_to_1[*(sp++)];
png_byte green = png_ptr->gamma_to_1[*(sp++)];
png_byte blue = png_ptr->gamma_to_1[*(sp++)];
red = png_ptr->gamma_to_1[red];
green = png_ptr->gamma_to_1[green];
blue = png_ptr->gamma_to_1[blue];
if (red != green || red != blue)
{
rgb_error |= 1;
*(dp++) = png_ptr->gamma_from_1[
(rc*red + gc*green + bc*blue)>>15];
}
else
*(dp++) = *(sp - 1);
rgb_error |= 1;
*(dp++) = png_ptr->gamma_from_1[
(rc*red + gc*green + bc*blue + 16384)>>15];
}
}
else
#endif
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
else
{
png_byte red = *(sp++);
png_byte green = *(sp++);
png_byte blue = *(sp++);
/* If there is no overall correction the table will not be
* set.
*/
if (png_ptr->gamma_table != NULL)
red = png_ptr->gamma_table[red];
if (red != green || red != blue)
{
rgb_error |= 1;
*(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
}
else
*(dp++) = *(sp - 1);
*(dp++) = red;
}
if (have_alpha)
*(dp++) = *(sp++);
}
}
else /* RGB bit_depth == 16 */
else
#endif
{
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
if (png_ptr->gamma_16_to_1 != NULL &&
png_ptr->gamma_16_from_1 != NULL)
png_bytep sp = row;
png_bytep dp = row;
png_uint_32 i;
for (i = 0; i < row_width; i++)
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
png_byte red = *(sp++);
png_byte green = *(sp++);
png_byte blue = *(sp++);
if (red != green || red != blue)
{
png_uint_16 red, green, blue, w;
rgb_error |= 1;
/*NOTE: this is the historical approach which simply
* truncates the results.
*/
*(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
}
red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
else
*(dp++) = red;
if (red == green && red == blue)
w = red;
if (have_alpha)
*(dp++) = *(sp++);
}
}
}
else
{
png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff)
else /* RGB bit_depth == 16 */
{
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL)
{
png_bytep sp = row;
png_bytep dp = row;
png_uint_32 i;
for (i = 0; i < row_width; i++)
{
png_uint_16 red, green, blue, w;
red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
if (red == green && red == blue)
{
if (png_ptr->gamma_16_table != NULL)
w = png_ptr->gamma_16_table[(red&0xff)
>> png_ptr->gamma_shift][red>>8];
png_uint_16 green_1 =
png_ptr->gamma_16_to_1[(green&0xff) >>
png_ptr->gamma_shift][green>>8];
png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff)
>> png_ptr->gamma_shift][blue>>8];
png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1
+ bc*blue_1)>>15);
w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
png_ptr->gamma_shift][gray16 >> 8];
rgb_error |= 1;
}
*(dp++) = (png_byte)((w>>8) & 0xff);
*(dp++) = (png_byte)(w & 0xff);
}
}
else
#endif
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
{
png_uint_16 red, green, blue, gray16;
red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
if (red != green || red != blue)
rgb_error |= 1;
gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
*(dp++) = (png_byte)((gray16>>8) & 0xff);
*(dp++) = (png_byte)(gray16 & 0xff);
}
}
}
}
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
if (row_info->bit_depth == 8)
{
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
{
png_byte red = png_ptr->gamma_to_1[*(sp++)];
png_byte green = png_ptr->gamma_to_1[*(sp++)];
png_byte blue = png_ptr->gamma_to_1[*(sp++)];
if (red != green || red != blue)
rgb_error |= 1;
*(dp++) = png_ptr->gamma_from_1
[(rc*red + gc*green + bc*blue)>>15];
*(dp++) = *(sp++); /* alpha */
}
}
else
#endif
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
{
png_byte red = *(sp++);
png_byte green = *(sp++);
png_byte blue = *(sp++);
if (red != green || red != blue)
rgb_error |= 1;
*(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
*(dp++) = *(sp++); /* alpha */
}
}
}
else /* RGBA bit_depth == 16 */
{
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
if (png_ptr->gamma_16_to_1 != NULL &&
png_ptr->gamma_16_from_1 != NULL)
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
{
png_uint_16 red, green, blue, w;
red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
if (red == green && red == blue)
w = red;
else
{
png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >>
png_ptr->gamma_shift][red>>8];
w = red;
}
png_uint_16 green_1 =
png_ptr->gamma_16_to_1[(green&0xff) >>
png_ptr->gamma_shift][green>>8];
else
{
png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff)
>> png_ptr->gamma_shift][red>>8];
png_uint_16 green_1 =
png_ptr->gamma_16_to_1[(green&0xff) >>
png_ptr->gamma_shift][green>>8];
png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff)
>> png_ptr->gamma_shift][blue>>8];
png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1
+ bc*blue_1 + 16384)>>15);
w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
png_ptr->gamma_shift][gray16 >> 8];
rgb_error |= 1;
}
png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >>
png_ptr->gamma_shift][blue>>8];
*(dp++) = (png_byte)((w>>8) & 0xff);
*(dp++) = (png_byte)(w & 0xff);
png_uint_16 gray16 = (png_uint_16)((rc * red_1
+ gc * green_1 + bc * blue_1)>>15);
w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
png_ptr->gamma_shift][gray16 >> 8];
rgb_error |= 1;
}
*(dp++) = (png_byte)((w>>8) & 0xff);
*(dp++) = (png_byte)(w & 0xff);
*(dp++) = *(sp++); /* alpha */
if (have_alpha)
{
*(dp++) = *(sp++);
*(dp++) = *(sp++);
}
}
else
}
else
#endif
{
png_bytep sp = row;
png_bytep dp = row;
png_uint_32 i;
for (i = 0; i < row_width; i++)
{
png_bytep sp = row;
png_bytep dp = row;
for (i = 0; i < row_width; i++)
png_uint_16 red, green, blue, gray16;
red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
if (red != green || red != blue)
rgb_error |= 1;
/* From 1.5.5 in the 16 bit case do the accurate convertion even
* in the 'fast' case - this is because this is where the code
* ends up when handling linear 16 bit data.
*/
gray16 = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >>
15);
*(dp++) = (png_byte)((gray16>>8) & 0xff);
*(dp++) = (png_byte)(gray16 & 0xff);
if (have_alpha)
{
png_uint_16 red, green, blue, gray16;
red = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2;
green = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2;
blue = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2;
if (red != green || red != blue)
rgb_error |= 1;
gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
*(dp++) = (png_byte)((gray16>>8) & 0xff);
*(dp++) = (png_byte)(gray16 & 0xff);
*(dp++) = *(sp++); /* alpha */
*(dp++) = *(sp++);
*(dp++) = *(sp++);
}
}
}
}
row_info->channels -= 2;
row_info->color_type = (png_byte)(row_info->color_type &
~PNG_COLOR_MASK_COLOR);

View File

@ -1022,27 +1022,80 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
/* Store the _white values as default coefficients for the rgb to gray
* operation if it is supported.
* operation if it is supported. Check if the transform is already set to
* avoid destroying the transform values.
*/
if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0)
if (!png_ptr->rgb_to_gray_coefficients_set)
{
/* png_set_background has not been called, the coefficients must be in
* range for the following to work without overflow.
/* png_set_background has not been called and we haven't seen an sRGB
* chunk yet. Find the XYZ of the three end points.
*/
if (y_red <= (1<<17) && y_green <= (1<<17) && y_blue <= (1<<17))
{
/* The y values are chromaticities: Y/X+Y+Z, the weights for the gray
* transformation are simply the normalized Y values for red, green and
* blue scaled by 32768.
*/
png_uint_32 w = y_red + y_green + y_blue;
png_XYZ XYZ;
png_xy xy;
png_ptr->rgb_to_gray_red_coeff = (png_uint_16)(((png_uint_32)y_red *
32768)/w);
png_ptr->rgb_to_gray_green_coeff = (png_uint_16)(((png_uint_32)y_green
* 32768)/w);
png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(((png_uint_32)y_blue *
32768)/w);
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
@ -1135,6 +1188,47 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
}
#endif /* PNG_READ_cHRM_SUPPORTED */
/* This is recorded for use when handling the cHRM chunk above. An sRGB
* chunk unconditionally overwrites the coefficients for grayscale conversion
* too.
*/
png_ptr->is_sRGB = 1;
# ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
/* Don't overwrite user supplied values: */
if (!png_ptr->rgb_to_gray_coefficients_set)
{
/* These numbers come from the sRGB specification (or, since one has to
* pay much money to get a copy, the wikipedia sRGB page) the
* chromaticity values quoted have been inverted to get the reverse
* transformation from RGB to XYZ and the 'Y' coefficients scaled by
* 32768 (then rounded).
*
* sRGB and ITU Rec-709 both truncate the values for the D65 white
* point to four digits and, even though it actually stores five
* digits, the PNG spec gives the truncated value.
*
* This means that when the chromaticities are converted back to XYZ
* end points we end up with (6968,23435,2366), which, as described in
* pngrtran.c, would overflow. If the five digit precision and up is
* used we get, instead:
*
* 6968*R + 23435*G + 2365*B
*
* (Notice that this rounds the blue coefficient down, rather than the
* choice used in pngrtran.c which is to round the green one down.)
*/
png_ptr->rgb_to_gray_red_coeff = 6968; /* 0.212639005871510 */
png_ptr->rgb_to_gray_green_coeff = 23434; /* 0.715168678767756 */
/* png_ptr->rgb_to_gray_blue_coeff = 2366; 0.072192315360734 */
/* The following keeps the cHRM chunk from destroying the
* coefficients again in the event that it follows the sRGB chunk.
*/
png_ptr->rgb_to_gray_coefficients_set = 1;
}
# endif
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent);
}
#endif /* PNG_READ_sRGB_SUPPORTED */

View File

@ -64,6 +64,39 @@ png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr,
}
}
void PNGFAPI
png_set_cHRM_XYZ_fixed(png_structp png_ptr, png_infop info_ptr,
png_fixed_point int_red_X, png_fixed_point int_red_Y,
png_fixed_point int_red_Z, png_fixed_point int_green_X,
png_fixed_point int_green_Y, png_fixed_point int_green_Z,
png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
png_fixed_point int_blue_Z)
{
png_XYZ XYZ;
png_xy xy;
png_debug1(1, "in %s storage function", "cHRM XYZ fixed");
if (png_ptr == NULL || info_ptr == NULL)
return;
XYZ.redX = int_red_X;
XYZ.redY = int_red_Y;
XYZ.redZ = int_red_Z;
XYZ.greenX = int_green_X;
XYZ.greenY = int_green_Y;
XYZ.greenZ = int_green_Z;
XYZ.blueX = int_blue_X;
XYZ.blueY = int_blue_Y;
XYZ.blueZ = int_blue_Z;
if (png_xy_from_XYZ(&xy, XYZ))
png_error(png_ptr, "XYZ values out of representable range");
png_set_cHRM_fixed(png_ptr, info_ptr, xy.whitex, xy.whitey, xy.redx, xy.redy,
xy.greenx, xy.greeny, xy.bluex, xy.bluey);
}
# ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
png_set_cHRM(png_structp png_ptr, png_infop info_ptr,
@ -80,6 +113,23 @@ png_set_cHRM(png_structp png_ptr, png_infop info_ptr,
png_fixed(png_ptr, blue_x, "cHRM Blue X"),
png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
}
void PNGAPI
png_set_cHRM_XYZ(png_structp png_ptr, png_infop info_ptr, double red_X,
double red_Y, double red_Z, double green_X, double green_Y, double green_Z,
double blue_X, double blue_Y, double blue_Z)
{
png_set_cHRM_XYZ_fixed(png_ptr, info_ptr,
png_fixed(png_ptr, red_X, "cHRM Red X"),
png_fixed(png_ptr, red_Y, "cHRM Red Y"),
png_fixed(png_ptr, red_Z, "cHRM Red Z"),
png_fixed(png_ptr, green_X, "cHRM Red X"),
png_fixed(png_ptr, green_Y, "cHRM Red Y"),
png_fixed(png_ptr, green_Z, "cHRM Red Z"),
png_fixed(png_ptr, blue_X, "cHRM Red X"),
png_fixed(png_ptr, blue_Y, "cHRM Red Y"),
png_fixed(png_ptr, blue_Z, "cHRM Red Z"));
}
# endif /* PNG_FLOATING_POINT_SUPPORTED */
#endif /* PNG_cHRM_SUPPORTED */

View File

@ -255,13 +255,20 @@ struct png_struct_def
png_bytep chunk_list;
#endif
#ifdef PNG_READ_sRGB_SUPPORTED
/* Added in 1.5.5 to record an sRGB chunk in the png. */
png_byte is_sRGB;
#endif
/* New members added in libpng-1.0.3 */
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
png_byte rgb_to_gray_status;
/* Added in libpng 1.5.5 to record setting of coefficients: */
png_byte rgb_to_gray_coefficients_set;
/* These were changed from png_byte in libpng-1.0.6 */
png_uint_16 rgb_to_gray_red_coeff;
png_uint_16 rgb_to_gray_green_coeff;
png_uint_16 rgb_to_gray_blue_coeff;
/* deleted in 1.5.5: rgb_to_gray_blue_coeff; */
#endif
/* New member added in libpng-1.0.4 (renamed in 1.0.9) */

1444
pngvalid.c

File diff suppressed because it is too large Load Diff