Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/LibPNG/png.c view on Meta::CPAN
if (png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1) == 0)
return 1;
if (png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale,
PNG_FP_1) == 0)
return 1;
return 0; /*success*/
}
static int
png_XYZ_normalize(png_XYZ *XYZ)
{
png_int_32 Y;
if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 ||
XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 ||
XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 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->red_Y;
if (0x7fffffff - Y < XYZ->green_X)
return 1;
Y += XYZ->green_Y;
if (0x7fffffff - Y < XYZ->blue_X)
return 1;
Y += XYZ->blue_Y;
if (Y != PNG_FP_1)
{
if (png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y) == 0)
return 1;
if (png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y) == 0)
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 */
if (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))
return 0;
return 1;
}
/* 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 xy_test;
/* As a side-effect this routine also returns the XYZ endpoints. */
result = png_XYZ_from_xy(XYZ, xy);
if (result != 0)
return result;
result = png_xy_from_XYZ(&xy_test, XYZ);
if (result != 0)
return result;
if (png_colorspace_endpoints_match(xy, &xy_test,
5/*actually, the math is pretty accurate*/) != 0)
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 != 0)
return result;
result = png_xy_from_XYZ(xy, XYZ);
if (result != 0)
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) != 0)
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) != 0)
{
/* The end points must be reasonably close to any we already have. The
* following allows an error of up to +/-.001
*/
if (png_colorspace_endpoints_match(xy, &colorspace->end_points_xy,
100) == 0)
{
colorspace->flags |= PNG_COLORSPACE_INVALID;
png_benign_error(png_ptr, "inconsistent chromaticities");
return 0; /* failed */
}
/* Only overwrite with preferred values */
if (preferred == 0)
return 1; /* ok, but no change */
}
colorspace->end_points_xy = *xy;
colorspace->end_points_XYZ = *XYZ;
colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS;
/* The end points are normally quoted to two decimal digits, so allow +/-0.01
* on this test.
*/
if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000) != 0)
colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB;
else
colorspace->flags &= PNG_COLORSPACE_CANCEL(
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 png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ,
preferred);
case 1:
/* We can't invert the chromaticities so we can't produce value XYZ
* values. Likely as not a color management system will fail too.
*/
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.
*/
colorspace->flags |= PNG_COLORSPACE_INVALID;
png_error(png_ptr, "internal error checking chromaticities");
break;
}
return 0; /* failed */
}
int /* PRIVATE */
png_colorspace_set_endpoints(png_const_structrp png_ptr,
png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred)
{
png_XYZ XYZ = *XYZ_in;
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)
/* Error message generation */
static char
png_icc_tag_char(png_uint_32 byte)
{
byte &= 0xff;
if (byte >= 32 && byte <= 126)
return (char)byte;
else
return '?';
}
static void
png_icc_tag_name(char *name, png_uint_32 tag)
{
name[0] = '\'';
name[1] = png_icc_tag_char(tag >> 24);
name[2] = png_icc_tag_char(tag >> 16);
name[3] = png_icc_tag_char(tag >> 8);
name[4] = png_icc_tag_char(tag );
name[5] = '\'';
}
static int
is_ICC_signature_char(png_alloc_size_t it)
{
return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) ||
(it >= 97 && it <= 122);
}
static int
is_ICC_signature(png_alloc_size_t it)
{
return is_ICC_signature_char(it >> 24) /* checks all the top bits */ &&
src/Source/LibPNG/png.c view on Meta::CPAN
}
#endif /* sRGB || iCCP */
#ifdef PNG_sRGB_SUPPORTED
int /* PRIVATE */
png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace,
int intent)
{
/* 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 store values adapted to a D50 environment; it is
* expected that the ICC profile mediaWhitePointTag will be D50; see the
* checks and code elsewhere to understand this better.
*
* These 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.)
*/
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
};
/* Do nothing if the colorspace is already invalidated. */
if ((colorspace->flags & PNG_COLORSPACE_INVALID) != 0)
return 0;
/* Check the intent, then check for existing settings. It is valid for the
* PNG file to have cHRM or gAMA chunks along with sRGB, but the values must
* be consistent with the correct values. If, however, this function is
* called below because an iCCP chunk matches sRGB then it is quite
* conceivable that an older app recorded incorrect gAMA and cHRM because of
* an incorrect calculation based on the values in the profile - this does
* *not* invalidate the profile (though it still produces an error, which can
* be ignored.)
*/
if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST)
return png_icc_profile_error(png_ptr, colorspace, "sRGB",
(unsigned)intent, "invalid sRGB rendering intent");
if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 &&
colorspace->rendering_intent != intent)
return png_icc_profile_error(png_ptr, colorspace, "sRGB",
(unsigned)intent, "inconsistent rendering intents");
if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0)
{
png_benign_error(png_ptr, "duplicate sRGB information ignored");
return 0;
}
/* If the standard sRGB cHRM chunk does not match the one from the PNG file
* warn but overwrite the value with the correct one.
*/
if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 &&
!png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy,
100))
png_chunk_report(png_ptr, "cHRM chunk does not match sRGB",
PNG_CHUNK_ERROR);
/* This check is just done for the error reporting - the routine always
* returns true when the 'from' argument corresponds to sRGB (2).
*/
(void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE,
2/*from sRGB*/);
/* intent: 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;
/* endpoints */
colorspace->end_points_xy = sRGB_xy;
colorspace->end_points_XYZ = sRGB_XYZ;
colorspace->flags |=
(PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB);
/* gamma */
colorspace->gamma = PNG_GAMMA_sRGB_INVERSE;
colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA;
/* Finally record that we have an sRGB profile */
colorspace->flags |=
(PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB);
return 1; /* set */
}
#endif /* sRGB */
#ifdef PNG_iCCP_SUPPORTED
/* Encoded value of D50 as an ICC XYZNumber. From the ICC 2010 spec the value
* is XYZ(0.9642,1.0,0.8249), which scales to:
*
* (63189.8112, 65536, 54060.6464)
*/
static const png_byte D50_nCIEXYZ[12] =
{ 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d };
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 png_icc_profile_error(png_ptr, colorspace, name, profile_length,
"too short");
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 */, int color_type)
{
png_uint_32 temp;
/* Length check; this cannot be ignored in this code because profile_length
* is used later to check the tag table, so even if the profile seems over
* long profile_length from the caller must be correct. The caller can fix
* this up on read or write by just passing in the profile header length.
*/
temp = png_get_uint_32(profile);
if (temp != profile_length)
return png_icc_profile_error(png_ptr, colorspace, name, temp,
"length does not match profile");
temp = (png_uint_32) (*(profile+8));
if (temp > 3 && (profile_length & 3))
return png_icc_profile_error(png_ptr, colorspace, name, profile_length,
"invalid length");
temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */
( run in 2.209 seconds using v1.01-cache-2.11-cpan-63c85eba8c4 )